diff --git a/src/common/crypto_serialization.h b/src/common/crypto_serialization.h index 79af98bc..89b05d8f 100644 --- a/src/common/crypto_serialization.h +++ b/src/common/crypto_serialization.h @@ -94,6 +94,27 @@ namespace crypto END_BOOST_SERIALIZATION() }; + struct CLSAG_GGXG_signature_serialized : public CLSAG_GGXG_signature + { + BEGIN_SERIALIZE_OBJECT() + FIELD(c) + FIELD((std::vector&)(r_g)) + FIELD((std::vector&)(r_x)) + FIELD(K1) + FIELD(K2) + FIELD(K3) + END_SERIALIZE() + + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(c) + BOOST_SERIALIZE((std::vector&)(r_g)) + BOOST_SERIALIZE((std::vector&)(r_x)) + BOOST_SERIALIZE(K1) + BOOST_SERIALIZE(K2) + BOOST_SERIALIZE(K3) + END_BOOST_SERIALIZATION() + }; + } // namespace crypto BLOB_SERIALIZER(crypto::chacha8_iv); diff --git a/src/crypto/clsag.cpp b/src/crypto/clsag.cpp index 80eb7982..bcc369e1 100644 --- a/src/crypto/clsag.cpp +++ b/src/crypto/clsag.cpp @@ -9,12 +9,15 @@ //#include "misc_log_ex.h" #include "../currency_core/crypto_config.h" +DISABLE_GCC_AND_CLANG_WARNING(unused-function) + namespace crypto { #define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl #define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl - //static std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return o << pod_to_hex(v); } + static std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return o << pod_to_hex(v); } + static std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return o << pod_to_hex(v); } bool generate_CLSAG_GG(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki, const scalar_t& secret_x, const scalar_t& secret_f, uint64_t secret_index, CLSAG_GG_signature& sig) @@ -183,7 +186,14 @@ namespace crypto // calculate key images point_t ki_base = hash_helper_t::hp(ring[secret_index].stealth_address); point_t key_image = secret_0_xp * ki_base; + +#ifndef NDEBUG CRYPTO_CHECK_AND_THROW_MES(key_image == point_t(ki), "key image 0 mismatch"); + CRYPTO_CHECK_AND_THROW_MES((secret_0_xp * c_point_G).to_public_key() == ring[secret_index].stealth_address, "secret_0_xp mismatch"); + CRYPTO_CHECK_AND_THROW_MES(secret_1_f * c_point_G == 8 * point_t(ring[secret_index].amount_commitment) - pseudo_out_amount_commitment, "secret_1_f mismatch"); + CRYPTO_CHECK_AND_THROW_MES(secret_3_q * c_point_G == 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch"); + CRYPTO_CHECK_AND_THROW_MES(secret_2_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch"); +#endif point_t K1_div8 = (c_scalar_1div8 * secret_1_f) * ki_base; K1_div8.to_public_key(sig.K1); @@ -208,6 +218,7 @@ namespace crypto hsc.add_pub_key(ring[i].stealth_address); hsc.add_pub_key(ring[i].amount_commitment); hsc.add_pub_key(ring[i].concealing_point); + DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", cp:" << ring[i].concealing_point); } hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment); hsc.add_point(c_scalar_1div8 * extended_amount_commitment); @@ -282,21 +293,33 @@ namespace crypto // calculate aggregate key image (layers 0, 1, 3; G component) point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1 + /*agg_coeff_2 * K2 +*/ agg_coeff_3 * K3; + DBG_VAL_PRINT(key_image); + DBG_VAL_PRINT(K1); + DBG_VAL_PRINT(K3); DBG_VAL_PRINT(W_key_image_g); // calculate aggregate key image (layer 2; X component) point_t W_key_image_x = agg_coeff_2 * K2; + DBG_VAL_PRINT(K2); DBG_VAL_PRINT(W_key_image_x); +#ifndef NDEBUG + CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * c_point_G == W_pub_keys_g[secret_index], "aggregated secret G and pub key mismatch"); + CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_g, "aggregated secret G and key image mismatch"); + CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * c_point_X == W_pub_keys_x[secret_index], "aggregated secret X and pub key mismatch"); + CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_x, "aggregated secret X and key image mismatch"); +#endif + // initial commitment scalar_t alpha_g = scalar_t::random(); // randomness for layers 0,1,3 scalar_t alpha_x = scalar_t::random(); // randomness for layer 2 hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXG_CHALLENGE); hsc.add_hash(input_hash); - hsc.add_point(alpha_g * c_point_G); DBG_VAL_PRINT(alpha_g * c_point_G); - hsc.add_point(alpha_g * ki_base); DBG_VAL_PRINT(alpha_g * ki_base); - hsc.add_point(alpha_x * c_point_X); DBG_VAL_PRINT(alpha_x * c_point_X); - hsc.add_point(alpha_x * ki_base); DBG_VAL_PRINT(alpha_x * ki_base); + hsc.add_point(alpha_g * c_point_G); + hsc.add_point(alpha_g * ki_base); + hsc.add_point(alpha_x * c_point_X); + hsc.add_point(alpha_x * ki_base); + //DBG_PRINT("c[" << secret_index << "] = Hs(ih, " << alpha_g * c_point_G << ", " << alpha_g * ki_base << ", " << alpha_x * c_point_X << ", " << alpha_x * ki_base << ")"); scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1} sig.r_g.clear(); @@ -334,8 +357,8 @@ namespace crypto } - bool verify_CLSAG_GGXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const public_key& extended_amount_commitment, const key_image& ki, - const CLSAG_GGXG_signature& sig) + bool verify_CLSAG_GGXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const public_key& extended_amount_commitment, + const key_image& ki, const CLSAG_GGXG_signature& sig) { DBG_PRINT("== verify_CLSAG_GGXG =="); size_t ring_size = ring.size(); @@ -360,6 +383,7 @@ namespace crypto hsc.add_pub_key(ring[i].stealth_address); hsc.add_pub_key(ring[i].amount_commitment); hsc.add_pub_key(ring[i].concealing_point); + DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", cp:" << ring[i].concealing_point); } hsc.add_pub_key(pseudo_out_amount_commitment); hsc.add_pub_key(extended_amount_commitment); @@ -429,11 +453,14 @@ namespace crypto agg_coeff_0 * key_image + agg_coeff_1 * point_t(sig.K1).modify_mul8() + agg_coeff_3 * point_t(sig.K3).modify_mul8(); + DBG_VAL_PRINT(point_t(sig.K1).modify_mul8()); + DBG_VAL_PRINT(point_t(sig.K3).modify_mul8()); DBG_VAL_PRINT(W_key_image_g); // calculate aggregate key image (layer 2; X component) point_t W_key_image_x = agg_coeff_2 * point_t(sig.K2).modify_mul8(); + DBG_VAL_PRINT(point_t(sig.K2).modify_mul8()); DBG_VAL_PRINT(W_key_image_x); @@ -449,6 +476,7 @@ namespace crypto hsc.add_point(sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x); c_prev = hsc.calc_hash(); // c_{i + 1} DBG_PRINT("c[" << i + 1 << "] = " << c_prev); + //DBG_PRINT("c[" << i + 1 << "] = Hs(ih, " << sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i] << ", " << sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g << ", " << sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i] << ", " << sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x << ")"); } return c_prev == sig.c; diff --git a/src/crypto/clsag.h b/src/crypto/clsag.h index 140a25e1..8a4400e5 100644 --- a/src/crypto/clsag.h +++ b/src/crypto/clsag.h @@ -32,15 +32,18 @@ namespace crypto struct CLSAG_GG_input_ref_t { CLSAG_GG_input_ref_t(const public_key& stealth_address, const public_key& amount_commitment) - : stealth_address(stealth_address), amount_commitment(amount_commitment) {} + : stealth_address(stealth_address), amount_commitment(amount_commitment) + {} - const public_key& stealth_address; // not premultiplied by 1/8, TODO @#@#: make sure it's okay - const public_key& amount_commitment; // multiplied by 1/8 + const public_key& stealth_address; // P, not premultiplied by 1/8, TODO @#@#: make sure it's okay + const public_key& amount_commitment; // A, premultiplied by 1/8 }; + // pseudo_out_amount_commitment -- not premultiplied by 1/8 bool generate_CLSAG_GG(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki, const scalar_t& secret_x, const scalar_t& secret_f, uint64_t secret_index, CLSAG_GG_signature& sig); + // pseudo_out_amount_commitment -- premultiplied by 1/8 bool verify_CLSAG_GG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const key_image& ki, const CLSAG_GG_signature& sig); @@ -71,10 +74,15 @@ namespace crypto const public_key& concealing_point; // Q, premultiplied by 1/8 }; + // pseudo_out_amount_commitment -- not premultiplied by 1/8 + // extended_amount_commitment -- not premultiplied by 1/8 bool generate_CLSAG_GGXG(const hash& m, const std::vector& ring, const point_t& pseudo_out_amount_commitment, const point_t& extended_amount_commitment, const key_image& ki, const scalar_t& secret_0_xp, const scalar_t& secret_1_f, const scalar_t& secret_2_x, const scalar_t& secret_3_q, uint64_t secret_index, CLSAG_GGXG_signature& sig); - bool verify_CLSAG_GGXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, const public_key& extended_amount_commitment, const key_image& ki, - const CLSAG_GGXG_signature& sig); + // pseudo_out_amount_commitment -- premultiplied by 1/8 + // extended_amount_commitment -- premultiplied by 1/8 + // may throw an exception TODO @#@# make sure it's okay + bool verify_CLSAG_GGXG(const hash& m, const std::vector& ring, const public_key& pseudo_out_amount_commitment, + const public_key& extended_amount_commitment, const key_image& ki, const CLSAG_GGXG_signature& sig); } // namespace crypto diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index 92980fc9..4e25ed79 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -137,15 +137,15 @@ namespace crypto const point_t& bpp_crypto_trait_zano::bpp_H = c_point_G; template - const point_t& bpp_crypto_trait_zano::bpp_H2 = c_point_H2; + const point_t& bpp_crypto_trait_zano::bpp_H2 = c_point_X; // efficient multiexponentiation (naive stub implementation atm, TODO) template bool multiexp_and_check_being_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand) { - CHECK_AND_ASSERT_MES(g_scalars.size() < CT::c_bpp_mn_max, false, "g_scalars oversized"); - CHECK_AND_ASSERT_MES(h_scalars.size() < CT::c_bpp_mn_max, false, "h_scalars oversized"); + CHECK_AND_ASSERT_MES(g_scalars.size() <= CT::c_bpp_mn_max, false, "g_scalars oversized"); + CHECK_AND_ASSERT_MES(h_scalars.size() <= CT::c_bpp_mn_max, false, "h_scalars oversized"); point_t result = summand; diff --git a/src/crypto/zarcanum.cpp b/src/crypto/zarcanum.cpp index d9f1b39b..c1f58a9a 100644 --- a/src/crypto/zarcanum.cpp +++ b/src/crypto/zarcanum.cpp @@ -45,9 +45,8 @@ namespace crypto #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ if (!(cond)) { LOG_PRINT_RED("zarcanum_generate_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } - - bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, const point_t& pseudo_out_amount_commitment, + bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki, const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask, zarcanum_proof& result, uint8_t* p_err /* = nullptr */) @@ -117,7 +116,7 @@ namespace crypto const scalar_vec_t masks2 = { bx }; // X component const std::vector E_1div8_vec_ptr = { &result.E }; - CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen>(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen>(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10); // = four-layers ring signature data outline = // (j in [0, ring_size-1]) @@ -144,6 +143,10 @@ namespace crypto // Q[j] // layer 3 secret (with respect to G) // secret_q + + point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G; + result.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key(); + TRY_ENTRY() CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXG(m, ring, pseudo_out_amount_commitment, C, stake_ki, secret_x, stake_blinding_mask - pseudo_out_blinding_mask, x0, secret_q, secret_index, @@ -152,11 +155,29 @@ namespace crypto return true; } + #undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE - bool zarcanum_verify_proof(const hash& kernel_hash, const public_key& commitment_1div8, const scalar_t& last_pow_block_id_hashed, const zarcanum_proof& proof, uint8_t* p_err /* = nullptr */) + + #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("zarcanum_verify_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) { *p_err = err_code; } return false; } + + bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, + const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki, + const zarcanum_proof& sig, uint8_t* p_err /* = nullptr */) { - return false; + bool r = false; + + // TODO @#@# + + std::vector E_for_range_proof = { point_t(sig.E) }; + std::vector range_proofs = { bppe_sig_commit_ref_t(sig.E_range_proof, E_for_range_proof) }; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify>(range_proofs), 10); + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXG(m, ring, sig.pseudo_out_amount_commitment, sig.C, stake_ki, sig.clsag_ggxg), 1); + + return true; } diff --git a/src/crypto/zarcanum.h b/src/crypto/zarcanum.h index 9d19ce5b..86a85fce 100644 --- a/src/crypto/zarcanum.h +++ b/src/crypto/zarcanum.h @@ -44,12 +44,14 @@ namespace crypto CLSAG_GGXG_signature clsag_ggxg; }; - bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, const point_t& pseudo_out_amount_commitment, + bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki, const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask, zarcanum_proof& result, uint8_t* p_err = nullptr); - bool zarcanum_verify_proof(const hash& kernel_hash, const public_key& commitment_1div8, const scalar_t& last_pow_block_id_hashed, const zarcanum_proof& proof, uint8_t* p_err = nullptr); + bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, + const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki, + const zarcanum_proof& sig, uint8_t* p_err = nullptr); } // namespace crypto diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 0cbe66a9..3cadffea 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -37,6 +37,7 @@ #include "tx_semantic_validation.h" #include "crypto/RIPEMD160_helper.h" #include "crypto/bitcoin/sha256_helper.h" +#include "crypto_config.h" #undef LOG_DEFAULT_CHANNEL @@ -1280,7 +1281,10 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t } if (pos) { - CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "coinstake transaction in the block has the wrong type"); + if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms + CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_zc_input), false, "coinstake tx has incorrect type of input #1: " << b.miner_tx.vin[1].type().name()); + else + CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "coinstake tx has incorrect type of input #1: " << b.miner_tx.vin[1].type().name()); } if (m_core_runtime_config.is_hardfork_active_for_height(1, height)) @@ -1315,11 +1319,19 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t return false; } - if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) + if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms { - CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 2, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2"); - CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)"); - CHECK_AND_ASSERT_MES(b.miner_tx.attachment[1].type() == typeid(zc_balance_proof), false, "coinbase transaction wrong attachmenttype #1 (expected: zc_balance_proof)"); + if (pos) + { + CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 1, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2"); + CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)"); + } + else + { + CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 2, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2"); + CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)"); + CHECK_AND_ASSERT_MES(b.miner_tx.attachment[1].type() == typeid(zc_balance_proof), false, "coinbase transaction wrong attachmenttype #1 (expected: zc_balance_proof)"); + } } else { @@ -1394,24 +1406,25 @@ uint64_t blockchain_storage::get_current_comulative_blocksize_limit() const return m_db_current_block_cumul_sz_limit; } //------------------------------------------------------------------ -bool blockchain_storage::create_block_template(block& b, - const account_public_address& miner_address, +bool blockchain_storage::create_block_template(const account_public_address& miner_address, + const blobdata& ex_nonce, + block& b, wide_difficulty_type& diffic, - uint64_t& height, - const blobdata& ex_nonce) const + uint64_t& height) const { - return create_block_template(b, miner_address, miner_address, diffic, height, ex_nonce, false, pos_entry()); + return create_block_template(miner_address, miner_address, ex_nonce, false, pos_entry(), nullptr, b, diffic, height); } //------------------------------------------------------------------ -bool blockchain_storage::create_block_template(block& b, - const account_public_address& miner_address, +bool blockchain_storage::create_block_template(const account_public_address& miner_address, const account_public_address& stakeholder_address, - wide_difficulty_type& diffic, - uint64_t& height, - const blobdata& ex_nonce, - bool pos, + const blobdata& ex_nonce, + bool pos, const pos_entry& pe, - fill_block_template_func_t custom_fill_block_template_func /* = nullptr */) const + fill_block_template_func_t custom_fill_block_template_func, + block& b, + wide_difficulty_type& diffic, + uint64_t& height, + crypto::scalar_t* blinding_mask_sum_ptr /* = nullptr */) const { create_block_template_params params = AUTO_VAL_INIT(params); params.miner_address = miner_address; @@ -1422,9 +1435,12 @@ bool blockchain_storage::create_block_template(block& b, params.pcustom_fill_block_template_func = custom_fill_block_template_func; create_block_template_response resp = AUTO_VAL_INIT(resp); bool r = create_block_template(params, resp); + b = resp.b; diffic = resp.diffic; - height = resp.height; + height = resp.height; + if (blinding_mask_sum_ptr) + *blinding_mask_sum_ptr = resp.blinding_mask_sum; return r; } @@ -1508,7 +1524,8 @@ bool blockchain_storage::create_block_template(const create_block_template_param ex_nonce, CURRENCY_MINER_TX_MAX_OUTS, pos, - pe); + pe, + &resp.blinding_mask_sum); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); uint64_t coinbase_size = get_object_blobsize(b.miner_tx); // "- 100" - to reserve room for PoS additions into miner tx @@ -5419,7 +5436,36 @@ bool blockchain_storage::validate_pos_block(const block& b, if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) { - return false; // not implemented yet, TODO @#@# + CHECK_AND_ASSERT_MES(b.miner_tx.version > TRANSACTION_VERSION_PRE_HF4, false, "Zarcanum PoS: miner tx with version " << b.miner_tx.version << " is not allowed"); + CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_zc_input), false, "incorrect input 1 type: " << b.miner_tx.vin[1].type().name() << ", txin_zc_input expected"); + const txin_zc_input& stake_input = boost::get(b.miner_tx.vin[1]); + CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1, false, "incorrect number of stake input signatures: " << b.miner_tx.signatures.size()); + CHECK_AND_ASSERT_MES(b.miner_tx.signatures[0].type() == typeid(zarcanum_sig), false, "incorrect sig 0 type: " << b.miner_tx.signatures[0].type().name()); + const zarcanum_sig& sig = boost::get(b.miner_tx.signatures[0]); + const crypto::hash miner_tx_hash = get_transaction_hash(b.miner_tx); + + // TODO @#@# do general input check for main chain blocks only? + uint64_t max_related_block_height = 0; + std::vector dummy_output_keys; // won't be used + uint64_t dummy_source_max_unlock_time_for_pos_coinbase_dummy = 0; // won't be used + scan_for_keys_context scan_contex = AUTO_VAL_INIT(scan_contex); + r = get_output_keys_for_input_with_checks(b.miner_tx, stake_input, dummy_output_keys, max_related_block_height, dummy_source_max_unlock_time_for_pos_coinbase_dummy, scan_contex); + CHECK_AND_ASSERT_MES(r, false, "get_output_keys_for_input_with_checks failed for stake input"); + CHECK_AND_ASSERT_MES(scan_contex.zc_outs.size() == stake_input.key_offsets.size(), false, "incorrect number of referenced outputs found: " << scan_contex.zc_outs.size() << ", while " << stake_input.key_offsets.size() << " is expected."); + // build a ring of references + vector ring; + ring.reserve(scan_contex.zc_outs.size()); + for(auto& zc_out : scan_contex.zc_outs) + ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.concealing_point); + + crypto::scalar_t last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, sm.last_pow_id); + + uint8_t err = 0; + r = crypto::zarcanum_verify_proof(miner_tx_hash, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, sig, &err); + CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << err); + + final_diff = basic_diff; // just for logs + return true; } else { @@ -5703,17 +5749,24 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt m_is_in_checkpoint_zone = false; crypto::hash proof_hash = null_hash; - uint64_t pos_coinstake_amount = 0; + uint64_t pos_coinstake_amount = UINT64_MAX; wide_difficulty_type this_coin_diff = 0; bool is_pos_bl = is_pos_block(bl); //check if PoS allowed in this height CHECK_AND_ASSERT_MES_CUSTOM(!(is_pos_bl && m_db_blocks.size() < m_core_runtime_config.pos_minimum_heigh), false, bvc.m_verification_failed = true, "PoS block not allowed on height " << m_db_blocks.size()); + if (!prevalidate_miner_transaction(bl, m_db_blocks.size(), is_pos_bl)) + { + LOG_PRINT_L0("Block with id: " << id << " @ " << height << " failed to pass miner tx prevalidation"); + bvc.m_verification_failed = true; + return false; + } + //check proof of work TIME_MEASURE_START_PD(target_calculating_time_2); wide_difficulty_type current_diffic = get_next_diff_conditional(is_pos_bl); CHECK_AND_ASSERT_MES_CUSTOM(current_diffic, false, bvc.m_verification_failed = true, "!!!!!!!!! difficulty overhead !!!!!!!!!"); - TIME_MEASURE_FINISH_PD(target_calculating_time_2); + TIME_MEASURE_FINISH_PD(target_calculating_time_2); TIME_MEASURE_START_PD(longhash_calculating_time_3); if (is_pos_bl) @@ -5737,16 +5790,10 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt return false; } } - TIME_MEASURE_FINISH_PD(longhash_calculating_time_3); + TIME_MEASURE_FINISH_PD(longhash_calculating_time_3); size_t aliases_count_befor_block = m_db_aliases.size(); - if (!prevalidate_miner_transaction(bl, m_db_blocks.size(), is_pos_bl)) - { - LOG_PRINT_L0("Block with id: " << id << " @ " << height << " failed to pass miner tx prevalidation"); - bvc.m_verification_failed = true; - return false; - } size_t cumulative_block_size = 0; size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); @@ -6049,7 +6096,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt { // PoS int64_t actual_ts = get_block_datetime(bei.bl); // signed int is intentionally used here int64_t ts_diff = actual_ts - m_core_runtime_config.get_core_time(); - powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: " << print_money_brief(pos_coinstake_amount) << ", final_difficulty: " << this_coin_diff; + powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: "; + if (pos_coinstake_amount != UINT64_MAX) + powpos_str_entry << print_money_brief(pos_coinstake_amount); + else + powpos_str_entry << "hidden"; + powpos_str_entry << ", final_difficulty: " << this_coin_diff; timestamp_str_entry << ", actual ts: " << actual_ts << " (diff: " << std::showpos << ts_diff << "s) block ts: " << std::noshowpos << bei.bl.timestamp << " (shift: " << std::showpos << static_cast(bei.bl.timestamp) - actual_ts << ")"; } else diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 4cb3d9cd..3ae1bfea 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -260,8 +260,8 @@ namespace currency wide_difficulty_type get_cached_next_difficulty(bool pos) const; - bool create_block_template(block& b, const account_public_address& miner_address, const account_public_address& stakeholder_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func = nullptr) const; - bool create_block_template(block& b, const account_public_address& miner_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const; + bool create_block_template(const account_public_address& miner_address, const blobdata& ex_nonce, block& b, wide_difficulty_type& di, uint64_t& height) const; + bool create_block_template(const account_public_address& miner_address, const account_public_address& stakeholder_address, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func, block& b, wide_difficulty_type& di, uint64_t& height, crypto::scalar_t* blinding_mask_sum_ptr = nullptr) const; bool create_block_template(const create_block_template_params& params, create_block_template_response& resp) const; bool have_block(const crypto::hash& id) const; @@ -332,6 +332,9 @@ namespace currency bool is_tx_expired(const transaction& tx) const; std::shared_ptr find_key_image_and_related_tx(const crypto::key_image& ki, crypto::hash& id_result) const; + // returns true as soon as the hardfork is active for the NEXT upcoming block (not for the top block in the blockchain storage) + bool is_hardfork_active(size_t hardfork_id) const; + wide_difficulty_type block_difficulty(size_t i)const; bool forecast_difficulty(std::vector> &out_height_2_diff_vector, bool pos) const; bool prune_aged_alt_blocks(); @@ -676,10 +679,6 @@ namespace currency bool is_output_allowed_for_input(const txout_htlc& out_v, const txin_v& in_v, uint64_t top_minus_source_height)const; bool is_output_allowed_for_input(const tx_out_zarcanum& out, const txin_v& in_v) const; - // returns true as soon as the hardfork is active for the NEXT upcoming block (not for the top block in the blockchain storage) - bool is_hardfork_active(size_t hardfork_id) const; - - //POS diff --git a/src/currency_core/blockchain_storage_basic.h b/src/currency_core/blockchain_storage_basic.h index 8a487003..c0a1a181 100644 --- a/src/currency_core/blockchain_storage_basic.h +++ b/src/currency_core/blockchain_storage_basic.h @@ -144,6 +144,7 @@ namespace currency block b; wide_difficulty_type diffic; uint64_t height; + crypto::scalar_t blinding_mask_sum; // sum of all the outputs' blinding masks }; typedef std::unordered_map transactions_map; diff --git a/src/currency_core/core_runtime_config.h b/src/currency_core/core_runtime_config.h index d25bb34f..8c058ac3 100644 --- a/src/currency_core/core_runtime_config.h +++ b/src/currency_core/core_runtime_config.h @@ -29,6 +29,13 @@ namespace currency { CHECK_AND_ASSERT_THROW_MES(hardfork_id < m_total_count, "invalid hardfork id: " << hardfork_id); m_height_the_hardfork_n_active_after[hardfork_id] = height_the_hardfork_is_active_after; + + // set all unset previous hardforks to this height + for(size_t hid = hardfork_id - 1; hid + 1 != 0; --hid) + { + if (m_height_the_hardfork_n_active_after[hid] == CURRENCY_MAX_BLOCK_NUMBER) + m_height_the_hardfork_n_active_after[hid] = height_the_hardfork_is_active_after; + } } bool is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const @@ -57,7 +64,7 @@ namespace currency return 0; } - uint8_t get_block_major_version_by_height(uint64_t height) + uint8_t get_block_major_version_by_height(uint64_t height) const { if (!this->is_hardfork_active_for_height(1, height)) return BLOCK_MAJOR_VERSION_INITIAL; diff --git a/src/currency_core/crypto_config.h b/src/currency_core/crypto_config.h index 3e4deffe..087c1cfd 100644 --- a/src/currency_core/crypto_config.h +++ b/src/currency_core/crypto_config.h @@ -22,7 +22,6 @@ #define CRYPTO_HDS_CLSAG_GGXG_CHALLENGE "ZANO_HDS_CLSAG_GGXG_CHALLENGE__" #define CRYPTO_HDS_ZARCANUM_LAST_POW_HASH "ZANO_HDS_ZARCANUM_LAST_POW_HASH" -#define CRYPTO_HDS_ZARCANUM_SECRET_Q "ZANO_HDS_ZARCANUM_SECRET_Q_____" #define CRYPTO_HDS_ZARCANUM_PROOF_HASH "ZANO_HDS_ZARCANUM_PROOF_HASH___" #define CRYPTO_HDS_ASSET_CONTROL_KEY "ZANO_HDS_ASSET_CONTROL_KEY_____" diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 6dad210d..9ab8a12f 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -493,13 +493,33 @@ namespace currency BEGIN_SERIALIZE_OBJECT() FIELD(d) FIELD(C) - // TODO + FIELD(C_prime); + FIELD(E); + FIELD(c); + FIELD(y0); + FIELD(y1); + FIELD(y2); + FIELD(y3); + FIELD(y4); + FIELD((crypto::bppe_signature_serialized&)E_range_proof); + FIELD(pseudo_out_amount_commitment); + FIELD((crypto::CLSAG_GGXG_signature_serialized&)clsag_ggxg); END_SERIALIZE() BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(d) BOOST_SERIALIZE(C) - // TODO + BOOST_SERIALIZE(C_prime); + BOOST_SERIALIZE(E); + BOOST_SERIALIZE(c); + BOOST_SERIALIZE(y0); + BOOST_SERIALIZE(y1); + BOOST_SERIALIZE(y2); + BOOST_SERIALIZE(y3); + BOOST_SERIALIZE(y4); + BOOST_SERIALIZE((crypto::bppe_signature_serialized&)E_range_proof); + BOOST_SERIALIZE(pseudo_out_amount_commitment); + BOOST_SERIALIZE((crypto::CLSAG_GGXG_signature_serialized&)clsag_ggxg); END_BOOST_SERIALIZATION() }; diff --git a/src/currency_core/currency_core.cpp b/src/currency_core/currency_core.cpp index b4c3d25c..7b4ca62d 100644 --- a/src/currency_core/currency_core.cpp +++ b/src/currency_core/currency_core.cpp @@ -355,7 +355,7 @@ namespace currency //----------------------------------------------------------------------------------------------- bool core::get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe) { - return m_blockchain_storage.create_block_template(b, adr, stakeholder_address, diffic, height, ex_nonce, pos, pe); + return m_blockchain_storage.create_block_template(adr, stakeholder_address, ex_nonce, pos, pe, nullptr, b, diffic, height); } //----------------------------------------------------------------------------------------------- bool core::get_block_template(const create_block_template_params& params, create_block_template_response& resp) @@ -511,11 +511,11 @@ namespace currency if (h > 0) { auto& crc = m_blockchain_storage.get_core_runtime_config(); - size_t hardfork_id_for_prev_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h - 1); - size_t hardfork_id_for_curr_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h); + size_t hardfork_id_for_prev_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h); + size_t hardfork_id_for_curr_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h + 1); if (hardfork_id_for_prev_block != hardfork_id_for_curr_block) { - LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " activated at height " << h, LOG_LEVEL_0); + LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " has been activated after the block at height " << h, LOG_LEVEL_0); } } diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 1331d4ab..ff566f7d 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -183,10 +183,13 @@ namespace currency const account_public_address &stakeholder_address, transaction& tx, uint64_t tx_version, - const blobdata& extra_nonce, - size_t max_outs, - bool pos, - const pos_entry& pe) + const blobdata& extra_nonce /* = blobdata() */, + size_t max_outs /* = CURRENCY_MINER_TX_MAX_OUTS */, + bool pos /* = false */, + const pos_entry& pe /* = pos_entry() */, // only pe.stake_unlock_time and pe.stake_amount are used now, TODO: consider refactoring -- sowle + crypto::scalar_t* blinding_masks_sum_ptr /* = nullptr */, + const keypair* tx_one_time_key_to_use /* = nullptr */ + ) { bool r = false; @@ -248,7 +251,10 @@ namespace currency tx = AUTO_VAL_INIT_T(transaction); tx.version = tx_version; - keypair txkey = keypair::generate(); + keypair txkey_local{}; + if (!tx_one_time_key_to_use) + txkey_local = keypair::generate(); + const keypair& txkey = tx_one_time_key_to_use ? *tx_one_time_key_to_use : txkey_local; add_tx_pub_key_to_extra(tx, txkey.pub); if (extra_nonce.size()) if (!add_tx_extra_userdata(tx, extra_nonce)) @@ -269,26 +275,16 @@ namespace currency { if (tx.version > TRANSACTION_VERSION_PRE_HF4 /* && stake is zarcanum */) { - // TODO: add Zarcanum part - //txin_zc_input stake_input = AUTO_VAL_INIT(stake_input); - //stake_input.key_offsets.push_back(pe.g_index); - //stake_input.k_image = pe.keyimage; + // just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block() tx.vin.emplace_back(std::move(txin_zc_input())); - //reserve place for ring signature tx.signatures.emplace_back(std::move(zarcanum_sig())); } else { // old fashioned non-hidden amount direct spend PoS scheme - txin_to_key stake_input; - stake_input.amount = pe.amount; - stake_input.key_offsets.push_back(pe.g_index); - stake_input.k_image = pe.keyimage; - tx.vin.push_back(stake_input); - //reserve place for ring signature - NLSAG_sig nlsag; - nlsag.s.resize(stake_input.key_offsets.size()); - tx.signatures.push_back(nlsag); // consider using emplace_back and avoid copying + // just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block() + tx.vin.emplace_back(std::move(txin_to_key())); + tx.signatures.emplace_back(std::move(NLSAG_sig())); } } @@ -333,6 +329,9 @@ namespace currency set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); } + if (blinding_masks_sum_ptr) + *blinding_masks_sum_ptr = blinding_masks_sum; + return true; } //------------------------------------------------------------------ @@ -387,6 +386,9 @@ namespace currency VARIANT_CASE_CONST(ZC_sig, zc_sig); sum_of_pseudo_out_amount_commitments += crypto::point_t(zc_sig.pseudo_out_amount_commitment); // *1/8 ++zc_sigs_count; + VARIANT_CASE_CONST(zarcanum_sig, sig); + sum_of_pseudo_out_amount_commitments += crypto::point_t(sig.pseudo_out_amount_commitment); // *1/8 + ++zc_sigs_count; VARIANT_SWITCH_END(); } sum_of_pseudo_out_amount_commitments.modify_mul8(); @@ -927,12 +929,12 @@ namespace currency crypto::derivation_to_scalar((const crypto::key_derivation&)derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i) out.stealth_address = (h * crypto::c_point_G + crypto::point_t(apa.spend_public_key)).to_public_key(); - out.concealing_point = (crypto::c_scalar_1div8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = 1/8 * Hs(domain_sep, h) * V + out.concealing_point = (crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = 1/8 * Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h); out.encrypted_amount = de.amount ^ amount_mask.m_u64[0]; - out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, Hs(8 * r * V, i) ) out.amount_commitment = (crypto::c_scalar_1div8 * de.amount * crypto::c_point_H + crypto::c_scalar_1div8 * out_blinding_mask * crypto::c_point_G).to_public_key(); // A = 1/8 * a * H + 1/8 * f * G if (de.addr.front().is_auditable()) @@ -1594,8 +1596,9 @@ namespace currency // prepare inputs struct input_generation_context_data { - keypair in_ephemeral; - //std::vector participants_derived_keys; + keypair in_ephemeral {}; // ephemeral output key (stealth_address and secret_x) + size_t real_out_index = SIZE_MAX; // index of real output in local outputs vector + std::vector outputs{}; // sorted by gindex }; //-------------------------------------------------------------------------------- bool generate_ZC_sig(const crypto::hash& tx_hash_for_signature, size_t input_index, const tx_source_entry& se, const input_generation_context_data& in_context, @@ -1616,7 +1619,7 @@ namespace currency #ifndef NDEBUG { crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * se.amount * crypto::c_point_H + crypto::c_scalar_1div8 * se.real_out_amount_blinding_mask * crypto::c_point_G; - CHECK_AND_ASSERT_MES(se.outputs[se.real_output].amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed"); + CHECK_AND_ASSERT_MES(in_context.outputs[in_context.real_out_index].amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed"); } #endif @@ -1651,10 +1654,10 @@ namespace currency // se.real_out_amount_blinding_mask - blinding_mask; std::vector ring; - for(size_t j = 0; j < se.outputs.size(); ++j) - ring.emplace_back(se.outputs[j].stealth_address, se.outputs[j].amount_commitment); + for(size_t j = 0; j < in_context.outputs.size(); ++j) + ring.emplace_back(in_context.outputs[j].stealth_address, in_context.outputs[j].amount_commitment); - return crypto::generate_CLSAG_GG(tx_hash_for_signature, ring, pseudo_out_amount_commitment, in.k_image, in_context.in_ephemeral.sec, se.real_out_amount_blinding_mask - blinding_mask, se.real_output, sig.clsags_gg); + return crypto::generate_CLSAG_GG(tx_hash_for_signature, ring, pseudo_out_amount_commitment, in.k_image, in_context.in_ephemeral.sec, se.real_out_amount_blinding_mask - blinding_mask, in_context.real_out_index, sig.clsags_gg); } //-------------------------------------------------------------------------------- bool generate_NLSAG_sig(const crypto::hash& tx_hash_for_signature, const crypto::hash& tx_prefix_hash, size_t input_index, const tx_source_entry& src_entr, @@ -1679,22 +1682,22 @@ namespace currency *pss_ring_s << "input #" << input_index << ", pub_keys:" << ENDL; std::vector keys_ptrs; - for(const tx_source_entry::output_entry& o : src_entr.outputs) + for(const tx_source_entry::output_entry& o : in_context.outputs) { keys_ptrs.push_back(&o.stealth_address); if (pss_ring_s) *pss_ring_s << o.stealth_address << ENDL; } - sigs.resize(src_entr.outputs.size()); + sigs.resize(in_context.outputs.size()); if (!watch_only_mode) - crypto::generate_ring_signature(tx_hash_for_signature, get_key_image_from_txin_v(tx.vin[input_index]), keys_ptrs, in_context.in_ephemeral.sec, src_entr.real_output, sigs.data()); + crypto::generate_ring_signature(tx_hash_for_signature, get_key_image_from_txin_v(tx.vin[input_index]), keys_ptrs, in_context.in_ephemeral.sec, in_context.real_out_index, sigs.data()); if (pss_ring_s) { *pss_ring_s << "signatures:" << ENDL; std::for_each(sigs.begin(), sigs.end(), [&pss_ring_s](const crypto::signature& s) { *pss_ring_s << s << ENDL; }); - *pss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_context.in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; + *pss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_context.in_ephemeral.sec << ENDL << "real_output: " << in_context.real_out_index << ENDL; } } @@ -1832,7 +1835,11 @@ namespace currency { inputs_mapping[current_index] = current_index; current_index++; - in_contexts.push_back(input_generation_context_data()); + in_contexts.push_back(input_generation_context_data{}); + input_generation_context_data& in_context = in_contexts.back(); + + in_context.outputs = prepare_outputs_entries_for_key_offsets(src_entr.outputs, src_entr.real_output, in_context.real_out_index); + if(src_entr.is_multisig()) {//multisig input txin_multisig input_multisig = AUTO_VAL_INIT(input_multisig); @@ -1844,7 +1851,7 @@ namespace currency else if (src_entr.htlc_origin.size()) { //htlc redeem - keypair& in_ephemeral = in_contexts.back().in_ephemeral; + keypair& in_ephemeral = in_context.in_ephemeral; //txin_to_key if(src_entr.outputs.size() != 1) { @@ -1859,11 +1866,11 @@ namespace currency return false; //check that derivated key is equal with real output key - if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].stealth_address)) + if (!(in_ephemeral.pub == src_entr.outputs.front().stealth_address)) { LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:" << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].stealth_address)); + << string_tools::pod_to_hex(src_entr.outputs.front().stealth_address)); return false; } @@ -1872,47 +1879,37 @@ namespace currency input_to_key.amount = src_entr.amount; input_to_key.k_image = img; input_to_key.hltc_origin = src_entr.htlc_origin; + input_to_key.key_offsets.push_back(src_entr.outputs.front().out_reference); - //fill outputs array and use relative offsets - for(const tx_source_entry::output_entry& out_entry : src_entr.outputs) - input_to_key.key_offsets.push_back(out_entry.out_reference); - - input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); tx.vin.push_back(input_to_key); } else { - //regular to key out - keypair& in_ephemeral = in_contexts.back().in_ephemeral; - //txin_to_key - if (src_entr.real_output >= src_entr.outputs.size()) - { - LOG_ERROR("real_output index (" << src_entr.real_output << ") greater than or equal to output_keys.size()=" << src_entr.outputs.size()); - return false; - } + // txin_to_key or txin_zc_input + CHECK_AND_ASSERT_MES(in_context.real_out_index < in_context.outputs.size(), false, + "real_output index (" << in_context.real_out_index << ") greater than or equal to in_context.outputs.size()=" << in_context.outputs.size()); + summary_inputs_money += src_entr.amount; //key_derivation recv_derivation; crypto::key_image img; - if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_context.in_ephemeral, img)) return false; //check that derivated key is equal with real output key - if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].stealth_address)) + if (!(in_context.in_ephemeral.pub == in_context.outputs[in_context.real_out_index].stealth_address)) { LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:" - << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].stealth_address)); + << string_tools::pod_to_hex(in_context.in_ephemeral.pub) << ENDL << "real output_public_key:" + << string_tools::pod_to_hex(in_context.outputs[in_context.real_out_index].stealth_address)); return false; } //fill key_offsets array with relative offsets std::vector key_offsets; - for(const tx_source_entry::output_entry& out_entry : src_entr.outputs) + for(const tx_source_entry::output_entry& out_entry : in_context.outputs) key_offsets.push_back(out_entry.out_reference); - key_offsets = absolute_output_offsets_to_relative(key_offsets); - //TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything) //potentially this approach might help to support htlc and multisig without making to complicated code if (src_entr.is_zarcanum()) @@ -1936,15 +1933,10 @@ namespace currency input_to_key.k_image = img; input_to_key.key_offsets = std::move(key_offsets); tx.vin.push_back(input_to_key); - //NLSAG_sources.push_back(&src_entr); } } } - /*if (ins_zc.elements.size()) - { - tx.vin.push_back(ins_zc); - }*/ uint64_t amount_of_assets = 0; std::vector shuffled_dsts(destinations); if (asset_id_for_destinations != currency::null_hash) @@ -2106,45 +2098,21 @@ namespace currency } LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3); - - //input_index++; - //in_context_index++; } - /* - for(const tx_source_entry& source_entry : sources) - { - crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, input_index, tx_prefix_hash); - CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "prepare_prefix_hash_for_sign failed"); - std::stringstream ss_ring_s; - if (source_entry.is_zarcanum()) - { - // ZC - // blinding_masks_sum is supposed to be sum(mask of all tx output) - sum(masks of all pseudo out commitments) - r = generate_ZC_sig(tx_hash_for_signature, input_index, source_entry, in_contexts[in_context_index], sender_account_keys, blinding_masks_sum, flags, local_blinding_masks_sum, tx); - CHECK_AND_ASSERT_MES(r, false, "generate_ZC_sigs failed"); - } - else - { - // NLSAG - r = generate_NLSAG_sig(tx_hash_for_signature, tx_prefix_hash, input_index, source_entry, sender_account_keys, in_contexts[in_context_index], txkey, flags, tx, &ss_ring_s); - CHECK_AND_ASSERT_MES(r, false, "generate_NLSAG_sig failed"); - } + //size_t prefix_size = get_object_blobsize(static_cast(tx)); + //size_t full_blob_size = t_serializable_object_to_blob(tx).size(); + //size_t estimated_blob_size = get_object_blobsize(tx); + //CHECK_AND_ASSERT_MES(full_blob_size == estimated_blob_size, false, "!"); - LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3); - - input_index++; - in_context_index++; - } - */ return true; } //--------------------------------------------------------------- - uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd) + uint64_t get_tx_version(uint64_t tx_expected_block_height, const hard_forks_descriptor& hfd) { - if (!hfd.is_hardfork_active_for_height(4, h)) + if (!hfd.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, tx_expected_block_height)) { return TRANSACTION_VERSION_PRE_HF4; } @@ -2503,18 +2471,18 @@ namespace currency return res; } //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index) + bool is_out_to_acc(const account_public_address& addr, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index) { crypto::public_key pk; - if (!derive_public_key(derivation, output_index, acc.account_address.spend_public_key, pk)) + if (!derive_public_key(derivation, output_index, addr.spend_public_key, pk)) return false; return pk == out_key.key; } //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index) + bool is_out_to_acc(const account_public_address& addr, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index) { crypto::public_key pk; - if (!derive_public_key(derivation, output_index, acc.account_address.spend_public_key, pk)) + if (!derive_public_key(derivation, output_index, addr.spend_public_key, pk)) return false; auto it = std::find(out_multisig.keys.begin(), out_multisig.keys.end(), pk); if (out_multisig.keys.end() == it) @@ -2522,16 +2490,16 @@ namespace currency return true; } - bool is_out_to_acc(const account_keys& acc, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask) + bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask) { crypto::scalar_t h; // = crypto::hash_helper_t::hs(reinterpret_cast(derivation), output_index); // h = Hs(8 * r * V, i) crypto::derivation_to_scalar(derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i) - crypto::point_t P_prime = h * crypto::c_point_G + crypto::point_t(acc.account_address.spend_public_key); // P =? Hs(8rV, i) * G + S + crypto::point_t P_prime = h * crypto::c_point_G + crypto::point_t(addr.spend_public_key); // P =? Hs(8rV, i) * G + S if (P_prime.to_public_key() != zo.stealth_address) return false; - crypto::point_t Q_prime = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(acc.account_address.view_public_key); // Q' * 8 =? Hs(domain_sep, h) * V + crypto::point_t Q_prime = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * 8 * crypto::point_t(addr.view_public_key); // Q' * 8 =? Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V if (Q_prime != crypto::point_t(zo.concealing_point).modify_mul8()) return false; @@ -2595,7 +2563,7 @@ namespace currency const tx_out_bare& o = boost::get(ov); CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "condition failed: o.target.type() == typeid(txout_to_key)"); - if (is_out_to_acc(acc, boost::get(o.target), derivation, offset)) + if (is_out_to_acc(acc.account_address, boost::get(o.target), derivation, offset)) { outs.emplace_back(offset, o.amount); money_transfered += o.amount; @@ -2632,25 +2600,25 @@ namespace currency { VARIANT_SWITCH_BEGIN(o.target); VARIANT_CASE_CONST(txout_to_key, t) - if (is_out_to_acc(acc, t, derivation, output_index)) + if (is_out_to_acc(acc.account_address, t, derivation, output_index)) { outs.emplace_back(output_index, o.amount); money_transfered += o.amount; } VARIANT_CASE_CONST(txout_multisig, t) - if (is_out_to_acc(acc, t, derivation, output_index)) + if (is_out_to_acc(acc.account_address, t, derivation, output_index)) { outs.emplace_back(output_index, o.amount); // TODO: @#@# consider this //don't cout this money } VARIANT_CASE_CONST(txout_htlc, htlc) htlc_info hi = AUTO_VAL_INIT(hi); - if (is_out_to_acc(acc, htlc.pkey_redeem, derivation, output_index)) + if (is_out_to_acc(acc.account_address, htlc.pkey_redeem, derivation, output_index)) { hi.hltc_our_out_is_before_expiration = true; htlc_info_list.push_back(hi); } - else if (is_out_to_acc(acc, htlc.pkey_refund, derivation, output_index)) + else if (is_out_to_acc(acc.account_address, htlc.pkey_refund, derivation, output_index)) { hi.hltc_our_out_is_before_expiration = false; htlc_info_list.push_back(hi); @@ -2664,7 +2632,7 @@ namespace currency VARIANT_CASE_CONST(tx_out_zarcanum, zo) uint64_t amount = 0; crypto::scalar_t blinding_mask = 0; - if (is_out_to_acc(acc, zo, derivation, output_index, amount, blinding_mask)) + if (is_out_to_acc(acc.account_address, zo, derivation, output_index, amount, blinding_mask)) { outs.emplace_back(output_index, amount, blinding_mask); open_asset_id v = AUTO_VAL_INIT(v); @@ -3063,6 +3031,7 @@ namespace currency return res; } //--------------------------------------------------------------- + // DEPRECATED: consider using prepare_outputs_entries_for_key_offsets and absolute_sorted_output_offsets_to_relative_in_place instead std::vector absolute_output_offsets_to_relative(const std::vector& off) { std::vector res = off; @@ -3106,6 +3075,34 @@ namespace currency return res; } //--------------------------------------------------------------- + bool absolute_sorted_output_offsets_to_relative_in_place(std::vector& offsets) noexcept + { + if (offsets.size() < 2) + return true; + + size_t i = offsets.size() - 1; + while (i != 0 && offsets[i].type() == typeid(ref_by_id)) + --i; + + try + { + for (; i != 0; i--) + { + uint64_t& offset_i = boost::get(offsets[i]); + uint64_t& offset_im1 = boost::get(offsets[i - 1]); + if (offset_i <= offset_im1) + return false; // input was not properly sorted + offset_i -= offset_im1; + } + } + catch(...) + { + return false; // unexpected type in boost::get (all ref_by_id's must be at the end of 'offsets') + } + + return true; + } + //--------------------------------------------------------------- bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) { return parse_and_validate_object_from_blob(b_blob, b); @@ -3978,6 +3975,11 @@ namespace currency return false; } //-------------------------------------------------------------------------------- + bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b) + { + return a.n == b.n && a.tx_id == b.tx_id; + } + //-------------------------------------------------------------------------------- bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs) { if (range_proofs.empty()) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 54a6b983..a5419b20 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -60,6 +60,7 @@ namespace currency bool operator ==(const currency::void_sig& a, const currency::void_sig& b); bool operator ==(const currency::ZC_sig& a, const currency::ZC_sig& b); bool operator ==(const currency::zarcanum_sig& a, const currency::zarcanum_sig& b); + bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b); typedef boost::multiprecision::uint128_t uint128_tl; @@ -243,7 +244,9 @@ namespace currency const blobdata& extra_nonce = blobdata(), size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS, bool pos = false, - const pos_entry& pe = pos_entry()); + const pos_entry& pe = pos_entry(), + crypto::scalar_t* blinding_masks_sum_ptr = nullptr, + const keypair* tx_one_time_key_to_use = nullptr); //--------------------------------------------------------------- uint64_t get_string_uint64_hash(const std::string& str); bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, crypto::scalar_t& out_blinding_mask, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); @@ -288,7 +291,7 @@ namespace currency bool shuffle = true, uint64_t flags = 0); - uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd); + uint64_t get_tx_version(uint64_t tx_expected_block_height, const hard_forks_descriptor& hfd); // returns tx version based on the height of the block where the transaction is expected to be bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& param, finalized_tx& result); crypto::hash get_asset_id_from_descriptor(const asset_descriptor_base& adb); @@ -306,9 +309,9 @@ namespace currency bool add_tx_extra_userdata(transaction& tx, const blobdata& extra_nonce); crypto::hash get_multisig_out_id(const transaction& tx, size_t n); - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index); - bool is_out_to_acc(const account_keys& acc, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index); - bool is_out_to_acc(const account_keys& acc, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount); + bool is_out_to_acc(const account_public_address& addr, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index); + bool is_out_to_acc(const account_public_address& addr, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index); + bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation, std::list& htlc_info_list); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered, crypto::key_derivation& derivation); @@ -363,7 +366,11 @@ namespace currency uint64_t get_block_height(const transaction& coinbase); uint64_t get_block_height(const block& b); std::vector relative_output_offsets_to_absolute(const std::vector& off); + // DEPRECATED: consider using prepare_outputs_entries_for_key_offsets and absolute_sorted_output_offsets_to_relative_in_place instead std::vector absolute_output_offsets_to_relative(const std::vector& off); + bool absolute_sorted_output_offsets_to_relative_in_place(std::vector& offsets) noexcept; + + // prints amount in format "3.14", "0.0" std::string print_money_brief(uint64_t amount); @@ -813,6 +820,17 @@ namespace currency return boost::apply_visitor(input_amount_getter(), v); } //--------------------------------------------------------------- + struct output_amount_getter : public boost::static_visitor + { + template + uint64_t operator()(const out_t&) const { return 0; } + uint64_t operator()(const tx_out_bare& ob) const { return ob.amount; } + }; + inline uint64_t get_amount_from_variant(const tx_out_v& out_v) + { + return boost::apply_visitor(output_amount_getter(), out_v); + } + //--------------------------------------------------------------- inline const tx_out_bare& get_tx_out_bare_from_out_v(const tx_out_v& o) { //this function will throw if type is not matching diff --git a/src/currency_core/currency_format_utils_abstract.h b/src/currency_core/currency_format_utils_abstract.h index 7355c84f..152407c6 100644 --- a/src/currency_core/currency_format_utils_abstract.h +++ b/src/currency_core/currency_format_utils_abstract.h @@ -129,8 +129,9 @@ namespace currency return boost::get(ai); } } - ASSERT_MES_AND_THROW("Objec not found"); + ASSERT_MES_AND_THROW("Object with type " << typeid(specific_type_t).name() << " was not found in a container"); } + //--------------------------------------------------------------- // if cb returns true, it means "continue", false -- means "stop" template bool process_type_in_variant_container(const variant_container_t& av, callback_t& cb, bool return_value_if_none_found = true) diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index ffb6d813..c0e06a3b 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -215,12 +215,35 @@ namespace currency return total; } //--------------------------------------------------------------- + inline size_t get_input_expected_signature_size_local(const txin_v& tx_in, bool last_input_in_separately_signed_tx) + { + struct txin_signature_size_visitor : public boost::static_visitor + { + txin_signature_size_visitor(size_t add) : a(add) {} + size_t a; + size_t operator()(const txin_gen& /*txin*/) const { return 0; } + size_t operator()(const txin_to_key& txin) const { return tools::get_varint_packed_size(txin.key_offsets.size() + a) + sizeof(crypto::signature) * (txin.key_offsets.size() + a); } + size_t operator()(const txin_multisig& txin) const { return tools::get_varint_packed_size(txin.sigs_count + a) + sizeof(crypto::signature) * (txin.sigs_count + a); } + size_t operator()(const txin_htlc& txin) const { return tools::get_varint_packed_size(1 + a) + sizeof(crypto::signature) * (1 + a); } + size_t operator()(const txin_zc_input& txin) const { return 96 + tools::get_varint_packed_size(txin.key_offsets.size()) + txin.key_offsets.size() * 32; } + }; + + return boost::apply_visitor(txin_signature_size_visitor(last_input_in_separately_signed_tx ? 1 : 0), tx_in); + } + //--------------------------------------------------------------- size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size) { size_t tx_blob_size = prefix_blob_size; if (is_coinbase(t)) + { + if (is_pos_miner_tx(t) && t.version > TRANSACTION_VERSION_PRE_HF4) + { + // Zarcanum + return tx_blob_size; + } return tx_blob_size; + } // for purged tx, with empty signatures and attachments, this function should return the blob size // which the tx would have if the signatures and attachments were correctly filled with actual data @@ -229,10 +252,12 @@ namespace currency bool separately_signed_tx = get_tx_flags(t) & TX_FLAG_SIGNATURE_MODE_SEPARATE; tx_blob_size += tools::get_varint_packed_size(t.vin.size()); // size of transaction::signatures (equals to total inputs count) + if (t.version > TRANSACTION_VERSION_PRE_HF4) + tx_blob_size += t.vin.size(); // for HF4 txs 'signatures' is a verctor of variants, so it's +1 byte per signature (assuming sigs count equals to inputs count) for (size_t i = 0; i != t.vin.size(); i++) { - size_t sig_size = get_input_expected_signature_size(t.vin[i], separately_signed_tx && i == t.vin.size() - 1); + size_t sig_size = get_input_expected_signature_size_local(t.vin[i], separately_signed_tx && i == t.vin.size() - 1); tx_blob_size += sig_size; } @@ -298,4 +323,65 @@ namespace currency } return true; } + //--------------------------------------------------------------- + // Prepapres vector of output_entry to be used in key_offsets in a transaction input: + // 1) sort all entries by gindex (while moving all ref_by_id to the end, keeping they relative order) + // 2) convert absolute global indices to relative key_offsets + std::vector prepare_outputs_entries_for_key_offsets(const std::vector& outputs, size_t old_real_index, size_t& new_real_index) noexcept + { + TRY_ENTRY() + + std::vector result = outputs; + if (outputs.size() < 2) + { + new_real_index = old_real_index; + return result; + } + + std::sort(result.begin(), result.end(), [](const tx_source_entry::output_entry& lhs, const tx_source_entry::output_entry& rhs) + { + if (lhs.out_reference.type() == typeid(uint64_t)) + { + if (rhs.out_reference.type() == typeid(uint64_t)) + return boost::get(lhs.out_reference) < boost::get(rhs.out_reference); + if (rhs.out_reference.type() == typeid(ref_by_id)) + return true; + CHECK_AND_ASSERT_THROW_MES(false, "unexpected type in out_reference 1: " << rhs.out_reference.type().name()); + } + else if (lhs.out_reference.type() == typeid(ref_by_id)) + { + if (rhs.out_reference.type() == typeid(uint64_t)) + return false; + if (rhs.out_reference.type() == typeid(ref_by_id)) + return false; // don't change the order of ref_by_id elements + CHECK_AND_ASSERT_THROW_MES(false, "unexpected type in out_reference 2: " << rhs.out_reference.type().name()); + } + return false; + }); + + // restore index of the selected element, if needed + if (old_real_index != SIZE_MAX) + { + CHECK_AND_ASSERT_THROW_MES(old_real_index < outputs.size(), "old_real_index is OOB"); + auto it = std::find(result.begin(), result.end(), outputs[old_real_index]); + CHECK_AND_ASSERT_THROW_MES(it != result.end(), "internal error: cannot find old_real_index"); + new_real_index = it - result.begin(); + } + + // find the last uint64_t entry - skip ref_by_id entries goint from the end to the beginnning + size_t i = result.size() - 1; + while (i != 0 && result[i].out_reference.type() == typeid(ref_by_id)) + --i; + + for (; i != 0; i--) + { + boost::get(result[i].out_reference) -= boost::get(result[i - 1].out_reference); + } + + return result; + + CATCH_ENTRY2(std::vector{}); + } + //--------------------------------------------------------------- + } \ No newline at end of file diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index c38c9cc4..29b70bc4 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -22,14 +22,16 @@ namespace currency output_entry(const output_entry &) = default; output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address) : out_reference(out_reference), stealth_address(stealth_address), concealing_point(null_pkey), amount_commitment(null_pkey) {} - //output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment) - // : out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment) {} + output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment) + : out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment) {} txout_ref_v out_reference; // either global output index or ref_by_id crypto::public_key stealth_address; // a.k.a output's one-time public key crypto::public_key concealing_point; // only for zarcaum outputs crypto::public_key amount_commitment; // only for zarcaum outputs + bool operator==(const output_entry& rhs) const { return out_reference == rhs.out_reference; } // used in prepare_outputs_entries_for_key_offsets, it's okay to do partially comparison + BEGIN_SERIALIZE_OBJECT() FIELD(out_reference) FIELD(stealth_address) @@ -161,5 +163,5 @@ namespace currency bool read_keyimages_from_tx(const transaction& tx, std::list& kil); bool validate_inputs_sorting(const transaction& tx); - + std::vector prepare_outputs_entries_for_key_offsets(const std::vector& outputs, size_t old_real_index, size_t& new_real_index) noexcept; } diff --git a/src/currency_core/pos_mining.cpp b/src/currency_core/pos_mining.cpp new file mode 100644 index 00000000..8ecc7af4 --- /dev/null +++ b/src/currency_core/pos_mining.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2022 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +#include "currency_basic.h" +#include "difficulty.h" +#include "pos_mining.h" +#include "wallet/wallet2.h" +#include "crypto/zarcanum.h" +#include "crypto_config.h" + +namespace currency +{ + void pos_mining_context::init(const wide_difficulty_type& pos_diff, const stake_modifier_type& sm, bool is_zarcanum) + { + this->basic_diff = pos_diff; + this->sk.stake_modifier = sm; + this->zarcanum = is_zarcanum; + + if (is_zarcanum) + { + this->last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, this->sk.stake_modifier.last_pow_id); + this->z_l_div_z_D = crypto::zarcanum_precalculate_z_l_div_z_D(this->basic_diff); + } + } + + void pos_mining_context::prepare_entry(uint64_t stake_amount, const crypto::key_image& stake_out_ki, const crypto::public_key& stake_source_tx_pub_key, uint64_t stake_out_in_tx_index, + const crypto::scalar_t& stake_out_blinding_mask, const crypto::secret_key& view_secret) + { + this->stake_amount = stake_amount; + this->sk.kimage = stake_out_ki; + + if (this->zarcanum) + { + crypto::scalar_t v = view_secret; + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + bool r = crypto::generate_key_derivation(stake_source_tx_pub_key, view_secret, derivation); // 8 * v * R + CHECK_AND_ASSERT_MES_NO_RET(r, "generate_key_derivation failed"); + crypto::scalar_t h = AUTO_VAL_INIT(h); + crypto::derivation_to_scalar(derivation, stake_out_in_tx_index, h.as_secret_key()); // h = Hs(8 * v * R, i) + + // q = Hs(domain_sep, Hs(8 * v * R, i) ) * 8 * v + this->secret_q = v * 8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h); + this->stake_out_blinding_mask = stake_out_blinding_mask; + } + } + + bool pos_mining_context::do_iteration(uint64_t ts) + { + // update stake kernel and calculate it's hash + this->sk.block_timestamp = ts; + { + PROFILE_FUNC("calc_hash"); + this->kernel_hash = crypto::cn_fast_hash(&this->sk, sizeof(this->sk)); + } + + bool found = false; + + if (this->zarcanum /* && td.is_zc() */) + { + crypto::mp::uint256_t lhs; + crypto::mp::uint512_t rhs; + { + PROFILE_FUNC("check_zarcanum"); + found = crypto::zarcanum_check_main_pos_inequality(this->kernel_hash, this->stake_out_blinding_mask, this->secret_q, this->last_pow_block_id_hashed, this->z_l_div_z_D, this->stake_amount, lhs, rhs); + } + if (found) + { + found = true; + LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(this->stake_amount) << /* ", gindex: " << td.m_global_output_index << */ ENDL + << "difficulty: " << this->basic_diff << ENDL + << "kernel info: " << ENDL + << print_stake_kernel_info(this->sk) + << "kernel_hash: " << this->kernel_hash << ENDL + << "lhs: " << lhs << ENDL + << "rhs: " << rhs + , LOG_LEVEL_0); + + } + } + else + { + // old PoS with non-hidden amounts + currency::wide_difficulty_type final_diff = this->basic_diff / this->stake_amount; + { + PROFILE_FUNC("check_hash"); + found = currency::check_hash(this->kernel_hash, final_diff); + } + if (found) + { + LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(this->stake_amount)<< /* ", gindex: " << td.m_global_output_index << */ ENDL + << "difficulty: " << this->basic_diff << ", final_diff: " << final_diff << ENDL + << "kernel info: " << ENDL + << print_stake_kernel_info(this->sk) + << "kernel_hash(proof): " << this->kernel_hash, + LOG_LEVEL_0); + } + } + + return found; + } + +}; // namespace currency diff --git a/src/currency_core/pos_mining.h b/src/currency_core/pos_mining.h new file mode 100644 index 00000000..988eaf57 --- /dev/null +++ b/src/currency_core/pos_mining.h @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +#pragma once + +namespace currency +{ + + struct pos_mining_context + { + // Zarcanum notation: + wide_difficulty_type basic_diff; // D + stake_kernel sk; + crypto::scalar_t last_pow_block_id_hashed; // f' + crypto::scalar_t secret_q; // q + boost::multiprecision::uint256_t z_l_div_z_D; // z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252) + crypto::hash kernel_hash; // h + crypto::scalar_t stake_out_blinding_mask; // f + uint64_t stake_amount; // a + + bool zarcanum; // false for pre-HF4 classic PoS with explicit amounts + + void init(const wide_difficulty_type& pos_diff, const stake_modifier_type& sm, bool is_zarcanum); + + void prepare_entry(uint64_t stake_amount, const crypto::key_image& stake_out_ki, const crypto::public_key& stake_source_tx_pub_key, uint64_t stake_out_in_tx_index, + const crypto::scalar_t& stake_out_blinding_mask, const crypto::secret_key& view_secret); + + bool do_iteration(uint64_t ts); + }; + +}; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9402a875..948472c0 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -863,6 +863,7 @@ namespace currency blobdata block_blob = t_serializable_object_to_blob(resp.b); res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); res.prev_hash = string_tools::pod_to_hex(resp.b.prev_id); + res.blinding_masks_sum = resp.blinding_mask_sum; res.height = resp.height; //calculate epoch seed res.seed = currency::ethash_epoch_to_seed(currency::ethash_height_to_epoch(res.height)); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index f6c87873..4b737eba 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -332,6 +332,10 @@ namespace currency #pragma pack (push, 1) struct out_entry { + out_entry() = default; + out_entry(uint64_t global_amount_index, const crypto::public_key& stealth_address, const crypto::public_key& amount_commitment, const crypto::public_key& concealing_point) + : global_amount_index(global_amount_index), stealth_address(stealth_address), amount_commitment(amount_commitment), concealing_point(concealing_point) + {} uint64_t global_amount_index; crypto::public_key stealth_address; crypto::public_key concealing_point; @@ -805,28 +809,17 @@ namespace currency blobdata explicit_transaction; std::string extra_text; std::string wallet_address; - std::string stakeholder_address; - bool pos_block; //is pos block - //uint64_t pos_amount; //do we still need it? - //uint64_t pos_g_index; // - //crypto::hash tx_id; - //uint64_t tx_out_index; - //uint64_t stake_unlock_time; - - pos_entry pe; // for making PoS blocks + std::string stakeholder_address; // address for stake return (PoS blocks) + pos_entry pe; // for PoS blocks + bool pos_block; // is pos block BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_BLOB_AS_HEX_STRING(explicit_transaction) KV_SERIALIZE(extra_text) KV_SERIALIZE(wallet_address) KV_SERIALIZE(stakeholder_address); - KV_SERIALIZE(pos_block) - //KV_SERIALIZE(pos_amount) - //KV_SERIALIZE(pos_g_index) - //KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) - //KV_SERIALIZE(tx_out_index) - //KV_SERIALIZE(stake_unlock_time) KV_SERIALIZE(pe) + KV_SERIALIZE(pos_block) END_KV_SERIALIZE_MAP() }; @@ -837,6 +830,7 @@ namespace currency crypto::hash seed; blobdata blocktemplate_blob; std::string prev_hash; + crypto::scalar_t blinding_masks_sum; // sum of outputs' blinding masks (for zc outs) std::string status; BEGIN_KV_SERIALIZE_MAP() @@ -845,6 +839,7 @@ namespace currency KV_SERIALIZE_POD_AS_HEX_STRING(seed) KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(prev_hash) + KV_SERIALIZE(blinding_masks_sum) KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 029e97d8..f51c6d7e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3168,7 +3168,7 @@ bool wallet2::generate_packing_transaction_if_needed(currency::transaction& tx, //---------------------------------------------------------------------------------------------------- std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/) const { - static const char* header = "index amount g_index flags block tx out# key image"; + static const char* header = "index amount g_index flags block tx out# key image"; std::stringstream ss; ss << header << ENDL; size_t count = 0; @@ -3567,8 +3567,11 @@ bool wallet2::get_transfer_address(const std::string& adr_str, currency::account return m_core_proxy->get_transfer_address(adr_str, addr, payment_id); } //---------------------------------------------------------------------------------------------------- -bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time) const +bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const { + if (is_zarcanum_hf && !tr.is_zc()) + return false; + if (!tr.is_spendable()) return false; @@ -3603,6 +3606,7 @@ void wallet2::get_mining_history(wallet_public::mining_history& hist, uint64_t t //---------------------------------------------------------------------------------------------------- size_t wallet2::get_pos_entries_count() { + bool is_zarcanum_hf = is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM); size_t counter = 0; for (size_t i = 0, size = m_transfers.size(); i < size; i++) @@ -3610,7 +3614,7 @@ size_t wallet2::get_pos_entries_count() auto& tr = m_transfers[i]; uint64_t stake_unlock_time = 0; - if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) + if (!is_transfer_okay_for_pos(tr, is_zarcanum_hf, stake_unlock_time)) continue; ++counter; @@ -3621,12 +3625,13 @@ size_t wallet2::get_pos_entries_count() //---------------------------------------------------------------------------------------------------- bool wallet2::get_pos_entries(std::vector& entries) { + bool is_zarcanum_hf = is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM); for (size_t i = 0; i != m_transfers.size(); i++) { auto& tr = m_transfers[i]; uint64_t stake_unlock_time = 0; - if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) + if (!is_transfer_okay_for_pos(tr, is_zarcanum_hf, stake_unlock_time)) continue; pos_entry pe = AUTO_VAL_INIT(pe); @@ -3647,7 +3652,7 @@ bool wallet2::is_in_hardfork_zone(uint64_t hardfork_index) const return m_core_runtime_config.is_hardfork_active_for_height(hardfork_index, get_blockchain_current_size()); } //---------------------------------------------------------------------------------------------------- -bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const pos_entry& pe) const +bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const pos_entry& pe, const crypto::scalar_t& blinding_masks_sum) const { bool r = false; WLT_CHECK_AND_ASSERT_MES(pe.wallet_index < m_transfers.size(), false, "invalid pe.wallet_index: " << pe.wallet_index); @@ -3656,53 +3661,53 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl const crypto::public_key source_tx_pub_key = get_tx_pub_key_from_extra(source_tx); WLT_CHECK_AND_ASSERT_MES(pe.tx_out_index < source_tx.vout.size(), false, "invalid pe.tx_out_index: " << pe.tx_out_index); const currency::tx_out_v& stake_out_v = source_tx.vout[pe.tx_out_index]; + + // calculate stake_out_derivation and secret_x (derived ephemeral secret key) + crypto::key_derivation stake_out_derivation = AUTO_VAL_INIT(stake_out_derivation); + r = crypto::generate_key_derivation(source_tx_pub_key, m_account.get_keys().view_secret_key, stake_out_derivation); // d = 8 * v * R + WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, tid: " << pe.wallet_index << ", pe.tx_id: " << pe.tx_id); + crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x); + crypto::derive_secret_key(stake_out_derivation, pe.tx_out_index, m_account.get_keys().spend_secret_key, secret_x); // x = Hs(8 * v * R, i) + s + if (!cxt.zarcanum) { // old PoS with non-hidden amounts WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(currency::txin_gen), false, "Wrong input 0 type in transaction: " << b.miner_tx.vin[0].type().name()); WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(currency::txin_to_key), false, "Wrong input 1 type in transaction: " << b.miner_tx.vin[1].type().name()); - auto& txin = boost::get(b.miner_tx.vin[1]); - txin.k_image = pe.keyimage; - - WLT_CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1 && - b.miner_tx.signatures[0].type() == typeid(NLSAG_sig) && - boost::get(b.miner_tx.signatures[0]).s.size() == txin.key_offsets.size(), - false, "Wrong signatures amount in coinbase transacton"); - - //derive secret key - crypto::key_derivation pos_coin_derivation = AUTO_VAL_INIT(pos_coin_derivation); - bool r = crypto::generate_key_derivation(source_tx_pub_key, - m_account.get_keys().view_secret_key, - pos_coin_derivation); - - WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, pe.tx_id: " << pe.tx_id); - - crypto::secret_key derived_secret_ephemeral_key = AUTO_VAL_INIT(derived_secret_ephemeral_key); - crypto::derive_secret_key(pos_coin_derivation, - pe.tx_out_index, - m_account.get_keys().spend_secret_key, - derived_secret_ephemeral_key); + WLT_CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1 && b.miner_tx.signatures[0].type() == typeid(NLSAG_sig), false, "wrong sig prepared in a PoS block"); + WLT_CHECK_AND_ASSERT_MES(stake_out_v.type() == typeid(tx_out_bare), false, "unexpected stake output type: " << stake_out_v.type().name() << ", expected: tx_out_bare"); + const tx_out_bare& stake_out = boost::get(stake_out_v); + WLT_CHECK_AND_ASSERT_MES(stake_out.target.type() == typeid(txout_to_key), false, "unexpected stake output target type: " << stake_out.target.type().name() << ", expected: txout_to_key"); + + NLSAG_sig& sig = boost::get(b.miner_tx.signatures[0]); + txin_to_key& stake_input = boost::get(b.miner_tx.vin[1]); + const txout_to_key& stake_out_target = boost::get(stake_out.target); + + // fill stake input + stake_input.k_image = pe.keyimage; + stake_input.amount = pe.amount; + stake_input.key_offsets.push_back(pe.g_index); // sign block actually in coinbase transaction crypto::hash block_hash = currency::get_block_hash(b); // get stake output pub key (stealth address) for ring signature generation std::vector keys_ptrs; - TRY_ENTRY() - keys_ptrs.push_back(&boost::get(boost::get(stake_out_v).target).key); - CATCH_ENTRY_CUSTOM("wallet2::prepare_and_sign_pos_block", { LOG_PRINT_RED_L0("unable to get output's pub key because of the exception"); }, false); + keys_ptrs.push_back(&stake_out_target.key); + // generate sring signature + sig.s.resize(1); crypto::generate_ring_signature(block_hash, - txin.k_image, + stake_input.k_image, keys_ptrs, - derived_secret_ephemeral_key, + secret_x, 0, - &boost::get(b.miner_tx.signatures[0]).s[0]); + &sig.s[0]); - WLT_LOG_L4("GENERATED RING SIGNATURE: block_id " << block_hash - << "txin.k_image" << txin.k_image + WLT_LOG_L4("GENERATED RING SIGNATURE for PoS block coinbase: block_id " << block_hash + << "txin.k_image" << stake_input.k_image << "key_ptr:" << *keys_ptrs[0] - << "signature:" << boost::get(b.miner_tx.signatures[0]).s); + << "signature:" << sig.s); return true; } @@ -3724,7 +3729,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl uint64_t secret_index = 0; // index of the real stake output // get decoys outputs and construct miner tx - static size_t required_decoys_count = 8; // TODO @#@# set them somewhere else + static size_t required_decoys_count = 4; // TODO @#@# set them somewhere else static bool use_only_forced_to_mix = false; // TODO @#@# set them somewhere else if (required_decoys_count > 0) { @@ -3743,8 +3748,10 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys_resp.outs[0].outs.size() == required_decoys_count + 1, "for PoS stake tx got less decoys to mix than requested: " << decoys_resp.outs[0].outs.size() << " < " << required_decoys_count + 1); auto& decoys = decoys_resp.outs[0].outs; - std::unordered_set used_gindices{ td.m_global_output_index }; - size_t good_decoys_count = 0; + decoys.emplace_front(td.m_global_output_index, stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point); + + std::unordered_set used_gindices; + size_t good_outs_count = 0; for(auto it = decoys.begin(); it != decoys.end(); ) { if (used_gindices.count(it->global_amount_index) != 0) @@ -3753,33 +3760,29 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl continue; } used_gindices.insert(it->global_amount_index); - if (++good_decoys_count == required_decoys_count) + if (++good_outs_count == required_decoys_count + 1) { decoys.erase(++it, decoys.end()); break; } ++it; } - WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys.size() == required_decoys_count, "for PoS stake got less good decoys than required: " << decoys.size() << " < " << required_decoys_count); + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys.size() == required_decoys_count + 1, "for PoS stake got less good decoys than required: " << decoys.size() << " < " << required_decoys_count); + + decoys.sort([](auto& l, auto& r){ return l.global_amount_index < r.global_amount_index; }); // sort them now (note absolute_sorted_output_offsets_to_relative_in_place() below) - secret_index = crypto::rand() % (decoys.size()); uint64_t i = 0; for(auto& el : decoys) { - if (i++ == secret_index) - { - ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point); - stake_input.key_offsets.push_back(td.m_global_output_index); - } + uint64_t gindex = el.global_amount_index; + if (gindex == td.m_global_output_index) + secret_index = i; + ++i; ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point); stake_input.key_offsets.push_back(el.global_amount_index); } - if (i == secret_index) - { - ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point); - stake_input.key_offsets.push_back(td.m_global_output_index); - } - stake_input.key_offsets = absolute_output_offsets_to_relative(stake_input.key_offsets); + r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets); + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "absolute_sorted_output_offsets_to_relative_in_place failed"); } else { @@ -3793,24 +3796,15 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl { crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * td.m_amount * crypto::c_point_H + crypto::c_scalar_1div8 * *td.m_opt_blinding_mask * crypto::c_point_G; CHECK_AND_ASSERT_MES(stake_out.amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed"); + CHECK_AND_ASSERT_MES(ring[secret_index].amount_commitment == stake_out.amount_commitment, false, "ring secret member doesn't match with the stake output"); } #endif - crypto::scalar_t pseudo_out_blinding_mask = crypto::scalar_t::random(); - crypto::point_t pseudo_out_amount_commitment = td.m_amount * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G; - sig.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key(); - - crypto::hash tx_hash_for_sig = get_transaction_hash(b.miner_tx); - - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); - r = crypto::generate_key_derivation(source_tx_pub_key, m_account.get_keys().view_secret_key, derivation); - WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, tid: " << pe.wallet_index << ", pe.tx_id: " << pe.tx_id); - crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x); - crypto::derive_secret_key(derivation, pe.tx_out_index, m_account.get_keys().spend_secret_key, secret_x); + crypto::hash tx_hash_for_sig = get_transaction_hash(b.miner_tx); // TODO @#@# change to block hash after the corresponding test is made uint8_t err = 0; - r = crypto::zarcanum_generate_proof(tx_hash_for_sig, cxt.kernel_hash, ring, pseudo_out_amount_commitment, cxt.last_pow_block_id_hashed, - pe.keyimage, secret_x, cxt.secret_q, secret_index, pseudo_out_blinding_mask, td.m_amount, *td.m_opt_blinding_mask, + r = crypto::zarcanum_generate_proof(tx_hash_for_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage, + secret_x, cxt.secret_q, secret_index, blinding_masks_sum, cxt.stake_amount, cxt.stake_out_blinding_mask, static_cast(sig), &err); WLT_CHECK_AND_ASSERT_MES(r, false, "zarcanum_generate_proof failed, err: " << (int)err); @@ -3821,26 +3815,17 @@ bool wallet2::fill_mining_context(mining_context& ctx) { currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request pos_details_req = AUTO_VAL_INIT(pos_details_req); currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response pos_details_resp = AUTO_VAL_INIT(pos_details_resp); - ctx.status = API_RETURN_CODE_NOT_FOUND; m_core_proxy->call_COMMAND_RPC_GET_POS_MINING_DETAILS(pos_details_req, pos_details_resp); if (pos_details_resp.status != API_RETURN_CODE_OK) return false; - ctx.basic_diff.assign(pos_details_resp.pos_basic_difficulty); - ctx.sk = AUTO_VAL_INIT(ctx.sk); - ctx.sk.stake_modifier = pos_details_resp.sm; - if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)) - { - // Zarcanum (PoS with hidden amounts) - ctx.zarcanum = true; - ctx.last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, ctx.sk.stake_modifier.last_pow_id); - ctx.z_l_div_z_D = crypto::zarcanum_precalculate_z_l_div_z_D(ctx.basic_diff); - } + ctx = mining_context{}; + ctx.init(wide_difficulty_type(pos_details_resp.pos_basic_difficulty), pos_details_resp.sm, is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)); ctx.last_block_hash = pos_details_resp.last_block_hash; ctx.is_pos_allowed = pos_details_resp.pos_mining_allowed; ctx.starter_timestamp = pos_details_resp.starter_timestamp; - ctx.status = API_RETURN_CODE_OK; + ctx.status = API_RETURN_CODE_NOT_FOUND; return true; } //------------------------------------------------------------------ @@ -3887,76 +3872,17 @@ void wallet2::do_pos_mining_prepare_entry(mining_context& context, size_t transf CHECK_AND_ASSERT_MES_NO_RET(transfer_index < m_transfers.size(), "transfer_index is out of bounds: " << transfer_index); const transfer_details& td = m_transfers[transfer_index]; - // pre build kernel - context.sk.kimage = td.m_key_image; + crypto::scalar_t blinding_mask{}; + if (td.m_opt_blinding_mask) + blinding_mask = *td.m_opt_blinding_mask; - if (context.zarcanum) - { - crypto::point_t R(get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx)); - crypto::scalar_t v = m_account.get_keys().view_secret_key; - context.secret_q = v * crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_SECRET_Q, v * R); - } + context.prepare_entry(td.amount(), td.m_key_image, get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx), td.m_internal_output_index, + blinding_mask, m_account.get_keys().view_secret_key); } //------------------------------------------------------------------ bool wallet2::do_pos_mining_iteration(mining_context& context, size_t transfer_index, uint64_t ts) { - CHECK_AND_NO_ASSERT_MES(transfer_index < m_transfers.size(), false, "transfer_index is out of bounds: " << transfer_index); - const transfer_details& td = m_transfers[transfer_index]; - - // update stake kernel and calculate it's hash - context.sk.block_timestamp = ts; - { - PROFILE_FUNC("calc_hash"); - context.kernel_hash = crypto::cn_fast_hash(&context.sk, sizeof(context.sk)); - } - - const uint64_t stake_amount = td.amount(); - bool found = false; - - if (context.zarcanum && td.is_zc()) - { - crypto::mp::uint256_t lhs; - crypto::mp::uint512_t rhs; - { - PROFILE_FUNC("check_zarcanum"); - found = crypto::zarcanum_check_main_pos_inequality(context.kernel_hash, *td.m_opt_blinding_mask, context.secret_q, context.last_pow_block_id_hashed, context.z_l_div_z_D, stake_amount, lhs, rhs); - ++context.iterations_processed; - } - if (found) - { - found = true; - LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(stake_amount) << ", gindex: " << td.m_global_output_index << ENDL - << "difficulty: " << context.basic_diff << ENDL - << "kernel info: " << ENDL - << print_stake_kernel_info(context.sk) - << "kernel_hash: " << context.kernel_hash << ENDL - << "lhs: " << lhs << ENDL - << "rhs: " << rhs - , LOG_LEVEL_0); - - } - } - else - { - // old PoS with non-hidden amounts - currency::wide_difficulty_type final_diff = context.basic_diff / stake_amount; - { - PROFILE_FUNC("check_hash"); - found = currency::check_hash(context.kernel_hash, final_diff); - ++context.iterations_processed; - } - if (found) - { - LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(stake_amount)<< ", gindex: " << td.m_global_output_index << ENDL - << "difficulty: " << context.basic_diff << ", final_diff: " << final_diff << ENDL - << "kernel info: " << ENDL - << print_stake_kernel_info(context.sk) - << "kernel_hash(proof): " << context.kernel_hash, - LOG_LEVEL_0); - } - } - - return found; + return context.do_iteration(ts); } //------------------------------- bool wallet2::reset_history() @@ -4039,7 +3965,7 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco //else //{ // old fashioned non-hidden amount PoS scheme - res = prepare_and_sign_pos_block(cxt, b, tmpl_req.pe); + res = prepare_and_sign_pos_block(cxt, b, tmpl_req.pe, tmpl_rsp.blinding_masks_sum); WLT_CHECK_AND_ASSERT_MES(res, false, "Failed to prepare_and_sign_pos_block"); //} @@ -4375,7 +4301,7 @@ void wallet2::dump_trunsfers(std::stringstream& ss, bool verbose) const else { boost::io::ios_flags_saver ifs(ss); - ss << "index amount spent_h g_index block block_ts flg tx out# key image" << ENDL; + ss << "index amount spent_h g_index block block_ts flg tx out# key image" << ENDL; for (size_t i = 0; i != m_transfers.size(); i++) { const transfer_details& td = m_transfers[i]; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7a05c0ad..6d1d470a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -43,6 +43,7 @@ #include "common/pod_array_file_container.h" #include "wallet_chain_shortener.h" #include "tor-connect/torlib/tor_lib_iface.h" +#include "currency_core/pos_mining.h" #define WALLET_DEFAULT_TX_SPENDABLE_AGE 10 @@ -412,28 +413,18 @@ namespace tools uint64_t m_unlock_time = 0; }; - struct mining_context + struct mining_context : public currency::pos_mining_context { std::string status; - bool is_pos_allowed = false;; - bool zarcanum = false; + bool is_pos_allowed = false; uint64_t index = 0; // index in m_transfers uint64_t stake_unlock_time = 0; - //uint64_t block_timestamp; uint64_t height = 0; uint64_t starter_timestamp = 0; crypto::hash last_block_hash = currency::null_hash; - crypto::scalar_t last_pow_block_id_hashed; // Zarcanum notation: f' - crypto::scalar_t secret_q; // Zarcanum notation: q - boost::multiprecision::uint256_t z_l_div_z_D; // Zarcanum notation: z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252) - crypto::hash kernel_hash; // Zarcanum notation: h - - currency::wide_difficulty_type basic_diff; - currency::stake_kernel sk; - uint64_t iterations_processed = 0; uint64_t total_items_checked = 0; uint64_t total_amount_checked = 0; @@ -699,7 +690,7 @@ namespace tools bool fill_mining_context(mining_context& ctx); void get_transfers(wallet2::transfer_container& incoming_transfers) const; - std::string get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/) const; + std::string get_transfers_str(bool include_spent = true, bool include_unspent = true) const; // Returns all payments by given id in unspecified order void get_payments(const std::string& payment_id, std::list& payments, uint64_t min_height = 0) const; @@ -840,7 +831,7 @@ namespace tools //next functions in public area only becausce of test_generator //TODO: Need refactoring - remove it back to private zone void set_genesis(const crypto::hash& genesis_hash); - bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe) const; + bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe, const crypto::scalar_t& blinding_masks_sum) const; void process_new_blockchain_entry(const currency::block& b, const currency::block_direct_data_entry& bche, const crypto::hash& bl_id, @@ -968,7 +959,7 @@ private: std::string get_alias_for_address(const std::string& addr); std::vector get_aliases_for_address(const std::string& addr); bool is_connected_to_net(); - bool is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time) const; + bool is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const; bool scan_unconfirmed_outdate_tx(); const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash); void rise_on_transfer2(const wallet_public::wallet_transfer_info& wti); @@ -1342,7 +1333,7 @@ namespace tools auto& tr = m_transfers[transfer_index]; uint64_t stake_unlock_time = 0; - if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) + if (!is_transfer_okay_for_pos(tr, cxt.zarcanum, stake_unlock_time)) continue; diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index e47b4bce..e9ec72c1 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -87,7 +87,7 @@ bool block_template_against_txs_size::c1(currency::core& c, size_t ev_index, con wide_difficulty_type diff = 0; uint64_t height = 0; g_block_txs_total_size = txs_total_size; // passing an argument to custom_fill_block_template_func via global variable (not perfect but works well) - r = bcs.create_block_template(b, miner_addr, miner_addr, diff, height, ex_nonce, is_pos != 0, pe, &custom_fill_block_template_func); + r = bcs.create_block_template(miner_addr, miner_addr, ex_nonce, is_pos != 0, pe, &custom_fill_block_template_func, b, diff, height); CHECK_AND_ASSERT_MES(r, false, "create_block_template failed, txs_total_size = " << txs_total_size); CHECK_AND_ASSERT_MES(height == top_block_height + 1, false, "Incorrect height: " << height << ", expected: " << top_block_height + 1 << ", txs_total_size = " << txs_total_size); diff --git a/tests/core_tests/chain_switch_pow_pos.cpp b/tests/core_tests/chain_switch_pow_pos.cpp index 21fb28ea..73673098 100644 --- a/tests/core_tests/chain_switch_pow_pos.cpp +++ b/tests/core_tests/chain_switch_pow_pos.cpp @@ -136,7 +136,7 @@ bool gen_chain_switch_pow_pos::generate(std::vector& events) c crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), alice.get_public_address()); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 11c2dbce..d1f93321 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -25,8 +25,6 @@ #include "wallet_test_core_proxy.h" #include "pos_block_builder.h" -//using namespace std; - using namespace epee; using namespace currency; @@ -34,7 +32,7 @@ using namespace currency; #define POS_DIFF_UP_TIMESTAMP_DELTA (DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*2/3) std::atomic test_core_time::m_time_shift; -test_gentime_settings test_generator::m_test_gentime_settings_default = test_gentime_settings(tests_digits_split_strategy, CURRENCY_MINER_TX_MAX_OUTS, WALLET_MAX_ALLOWED_OUTPUT_AMOUNT, DEFAULT_DUST_THRESHOLD); +const test_gentime_settings test_generator::m_test_gentime_settings_default{}; test_gentime_settings test_generator::m_test_gentime_settings = test_generator::m_test_gentime_settings_default; crypto::signature create_invalid_signature() @@ -276,6 +274,7 @@ bool test_generator::construct_block(currency::block& blk, blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx); size_t target_block_size = txs_size + 0; // zero means no cost for ordinary coinbase + crypto::scalar_t blinding_masks_sum = 0; while (true) { r = construct_miner_tx(height, misc_utils::median(block_sizes), @@ -289,7 +288,8 @@ bool test_generator::construct_block(currency::block& blk, blobdata(), test_generator::get_test_gentime_settings().miner_tx_max_outs, static_cast(coin_stake_sources.size()), - pe); + pe, + &blinding_masks_sum); CHECK_AND_ASSERT_MES(r, false, "construct_miner_tx failed"); size_t coinbase_size = get_object_blobsize(blk.miner_tx); @@ -330,7 +330,7 @@ bool test_generator::construct_block(currency::block& blk, else { //need to build pos block - r = sign_block(blk, pe, *wallets[won_walled_index].wallet, wallets[won_walled_index].mining_context, blocks, oi); + r = sign_block(wallets[won_walled_index].mining_context, pe, *wallets[won_walled_index].wallet, blinding_masks_sum, blk); CHECK_AND_ASSERT_MES(r, false, "Failed to find_kernel_and_sign()"); } @@ -347,14 +347,13 @@ bool test_generator::construct_block(currency::block& blk, return true; } -bool test_generator::sign_block(currency::block& b, - pos_entry& pe, - tools::wallet2& w, - const tools::wallet2::mining_context& mining_context, - const std::vector& blocks, - const outputs_index& oi) +bool test_generator::sign_block(const tools::wallet2::mining_context& mining_context, + const pos_entry& pe, + const tools::wallet2& w, + const crypto::scalar_t& blinding_masks_sum, + currency::block& b) { - bool r = w.prepare_and_sign_pos_block(mining_context, b, pe); + bool r = w.prepare_and_sign_pos_block(mining_context, b, pe, blinding_masks_sum); CHECK_AND_ASSERT_MES(r, false, "prepare_and_sign_pos_block failed"); return true; } @@ -576,8 +575,9 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo std::vector& coinbase_outs = txs_outs[currency::get_transaction_hash(blocks[h]->b.miner_tx)]; for (size_t out_i = 0; out_i != blocks[h]->b.miner_tx.vout.size(); out_i++) { - coinbase_outs.push_back(index[boost::get(blocks[h]->b.miner_tx.vout[out_i]).amount].size()); - index[boost::get(blocks[h]->b.miner_tx.vout[out_i]).amount].push_back(std::tuple(h, 0, out_i)); + uint64_t amount = get_amount_from_variant(blocks[h]->b.miner_tx.vout[out_i]); + coinbase_outs.push_back(index[amount].size()); + index[amount].push_back(std::tuple(h, 0, out_i)); } for (size_t tx_index = 0; tx_index != blocks[h]->m_transactions.size(); tx_index++) @@ -585,14 +585,16 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo std::vector& tx_outs_indx = txs_outs[currency::get_transaction_hash(blocks[h]->m_transactions[tx_index])]; for (size_t out_i = 0; out_i != blocks[h]->m_transactions[tx_index].vout.size(); out_i++) { - tx_outs_indx.push_back(index[boost::get(blocks[h]->m_transactions[tx_index].vout[out_i]).amount].size()); - index[boost::get(blocks[h]->m_transactions[tx_index].vout[out_i]).amount].push_back(std::tuple(h, tx_index + 1, out_i)); + uint64_t amount = get_amount_from_variant(blocks[h]->m_transactions[tx_index].vout[out_i]); + tx_outs_indx.push_back(index[amount].size()); + index[amount].push_back(std::tuple(h, tx_index + 1, out_i)); } } } return true; } //------------------------------------------------------------------ +/* not used, consider removing bool test_generator::get_output_details_by_global_index(const test_generator::blockchain_vector& blck_chain, const test_generator::outputs_index& indexes, uint64_t amount, @@ -628,6 +630,7 @@ bool test_generator::get_output_details_by_global_index(const test_generator::bl output_key = boost::get(boost::get(tx->vout[tx_out_index]).target).key; return true; } +*/ //------------------------------------------------------------------ bool test_generator::build_stake_modifier(stake_modifier_type& sm, const test_generator::blockchain_vector& blck_chain) @@ -926,42 +929,44 @@ bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const accou //------------------------------------------------------------------------------ -struct output_index { - const currency::txout_target_v out; - uint64_t amount; - size_t blk_height; // block height - size_t tx_no; // index of transaction in block - size_t out_no; // index of out in transaction - size_t idx; - bool spent; - const currency::block *p_blk; - const currency::transaction *p_tx; +struct output_index +{ + const currency::tx_out_v out_v; + uint64_t amount; + size_t tx_no; // index of transaction in block + size_t out_no; // index of out in transaction + size_t idx; // global index + bool spent; // was it spent? + bool zc_out; // is it a ZC output? + const currency::block *p_blk; + const currency::transaction *p_tx; + crypto::scalar_t blinding_mask; // zc outs only - output_index(const currency::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const currency::block *_pb, const currency::transaction *_pt) - : out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { } + output_index(const currency::tx_out_v &_out_v, uint64_t _a, size_t tno, size_t ono, const currency::block *_pb, const currency::transaction *_pt) + : out_v(_out_v), amount(_a), tx_no(tno), out_no(ono), idx(0), spent(false), zc_out(false), p_blk(_pb), p_tx(_pt), blinding_mask(0) + {} - output_index(const output_index &other) - : out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { } + output_index(const output_index &other) = default; - const std::string toString() const { - std::stringstream ss; + const std::string to_string() const + { + std::stringstream ss; + ss << "output_index{" + << " tx_no=" << tx_no + << " out_no=" << out_no + << " amount=" << amount + << " idx=" << idx + << " spent=" << spent + << " zc_out=" << zc_out + << "}"; + return ss.str(); + } - ss << "output_index{blk_height=" << blk_height - << " tx_no=" << tx_no - << " out_no=" << out_no - << " amount=" << amount - << " idx=" << idx - << " spent=" << spent - << "}"; - - return ss.str(); - } - - output_index& operator=(const output_index& other) - { - new(this) output_index(other); - return *this; - } + output_index& operator=(const output_index& other) = default; + /*{ + new(this) output_index(other); + return *this; + }*/ }; typedef std::map > map_output_t; // amount -> [N -> global out index] @@ -984,16 +989,18 @@ namespace bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const currency::account_keys& acc_keys) { + bool r = false; + for (const block& blk : blockchain) { - + uint64_t height = get_block_height(blk); std::vector vtx; vtx.push_back(&blk.miner_tx); for (const crypto::hash &h : blk.tx_hashes) { const map_hash2tx_t::const_iterator cit = mtx.find(h); - CHECK_AND_ASSERT_MES(cit != mtx.end(), false, "block at height " << get_block_height(blk) << " contains a reference to unknown tx " << h); + CHECK_AND_ASSERT_MES(cit != mtx.end(), false, "block at height " << height << " contains a reference to unknown tx " << h); vtx.push_back(cit->second); } @@ -1006,18 +1013,37 @@ bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const for (size_t j = 0; j < tx.vout.size(); ++j) { - const tx_out_bare &out = boost::get(tx.vout[j]); - output_index oi(out.target, out.amount, boost::get(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]); + VARIANT_SWITCH_BEGIN(tx.vout[j]) + VARIANT_CASE_CONST(tx_out_bare, out) + if (out.target.type() == typeid(txout_to_key)) + { + std::vector& outs_vec = outs[out.amount]; + size_t out_global_idx = outs_vec.size(); + output_index oi(out, out.amount, i, j, &blk, vtx[i]); + oi.idx = out_global_idx; + outs_vec.emplace_back(std::move(oi)); + // Is out to me? + if (is_out_to_acc(acc_keys.account_address, boost::get(out.target), derivation, j)) + outs_mine[out.amount].push_back(out_global_idx); + } + VARIANT_CASE_CONST(tx_out_zarcanum, out) + std::vector& outs_vec = outs[0]; // amount = 0 for ZC outs + size_t out_global_idx = outs_vec.size(); - if (out.target.type() == typeid(txout_to_key)) - { - outs[out.amount].push_back(oi); - size_t tx_global_idx = outs[out.amount].size() - 1; - outs[out.amount][tx_global_idx].idx = tx_global_idx; - // Is out to me? - if (is_out_to_acc(acc_keys, boost::get(out.target), derivation, j)) - outs_mine[out.amount].push_back(tx_global_idx); - } + output_index oi(out, 0 /* amount */, i, j, &blk, vtx[i]); + oi.zc_out = true; + oi.idx = out_global_idx; + outs_vec.emplace_back(std::move(oi)); + + uint64_t decoded_amount = 0; + crypto::scalar_t decoded_blinding_mask{}; + if (is_out_to_acc(acc_keys.account_address, out, derivation, j, decoded_amount, decoded_blinding_mask)) + { + outs_vec.back().amount = decoded_amount; + outs_vec.back().blinding_mask = decoded_blinding_mask; + outs_mine[0].push_back(out_global_idx); + } + VARIANT_SWITCH_END() } } } @@ -1027,59 +1053,56 @@ bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const currency::account_keys& from) { - for(const map_output_t::value_type &o : outs_mine) + // 1. make a hashset of spend key images + std::unordered_set spent_key_images; + auto add_key_images_from_tx = [&](const transaction& tx) -> bool { + for(const txin_v& in: tx.vin) { - for (size_t i = 0; i < o.second.size(); ++i) - { - output_index &oi = outs[o.first][o.second[i]]; - - // construct key image for this output - crypto::key_image out_ki; - keypair in_ephemeral; - generate_key_image_helper(from, get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, out_ki); - - // lookup for this key image in the events std::vector - for(auto& tx_pair : mtx) - { - const transaction& tx = *tx_pair.second; - for(const txin_v &in : tx.vin) - { - if (typeid(txin_to_key) == in.type()) - { - const txin_to_key &itk = boost::get(in); - if (itk.k_image == out_ki) - oi.spent = true; - } - } - } - - // check whether this key image has been spent in miner tx of a PoS block - // TODO change this check to simply adding PoS miner tx to mtx map - for (auto& b : blockchain) - { - if (!is_pos_block(b)) - continue; - for (const txin_v &in : b.miner_tx.vin) - { - if (in.type() == typeid(txin_to_key)) - { - const txin_to_key &itk = boost::get(in); - if (itk.k_image == out_ki) - oi.spent = true; - } - } - } - } + crypto::key_image ki{}; + if (get_key_image_from_txin_v(in, ki)) + if (!spent_key_images.insert(ki).second) + return false; } - return true; + }; + + for(auto& tx_pair : mtx) + add_key_images_from_tx(*tx_pair.second); // some key images may be added more than once (because invalid txs can't be detected here), ignore that + + for (auto& b : blockchain) + { + if (is_pos_block(b)) + CHECK_AND_ASSERT_MES(add_key_images_from_tx(b.miner_tx), false, "insertion of spent key image failed for miner tx " << get_transaction_hash(b.miner_tx)); + } + + // 2. check outputs from outs_mine against spent key images + if (spent_key_images.empty()) + return true; + + for(const map_output_t::value_type &o : outs_mine) + { + for (size_t i = 0; i < o.second.size(); ++i) + { + output_index &oi = outs[o.first][o.second[i]]; + + // construct key image for this output + crypto::key_image out_ki; + keypair in_ephemeral; + generate_key_image_helper(from, get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, out_ki); // TODO: store ki and secret ephemeral for further use + + if (spent_key_images.count(out_ki) != 0) + oi.spent = true; + } + } + + return true; } -bool fill_output_entries(std::vector& out_indices, - size_t sender_out, size_t nmix, uint64_t& real_entry_idx, - std::vector& output_entries, - bool use_ref_by_id) +bool fill_output_entries(const std::vector& out_indices, size_t real_out_index, size_t nmix, bool check_for_unlocktime, bool use_ref_by_id, + uint64_t next_block_height, uint64_t head_block_ts, uint64_t& real_entry_idx, std::vector& output_entries) { + // use_ref_by_id = true; // <-- HINT: this could be used to enforce using ref_by_id across all the tests if needed + if (out_indices.size() <= nmix) return false; @@ -1092,7 +1115,7 @@ bool fill_output_entries(std::vector& out_indices, continue; bool append = false; - if (i == sender_out) + if (i == real_out_index) { append = true; sender_out_found = true; @@ -1100,8 +1123,30 @@ bool fill_output_entries(std::vector& out_indices, } else if (0 < rest) { - if(boost::get(oi.out).mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX || boost::get(oi.out).mix_attr > nmix+1) - continue; + uint8_t mix_attr = 0; + if (get_mix_attr_from_tx_out_v(oi.out_v, mix_attr)) + { + if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX || mix_attr > nmix + 1) + continue; + + if (check_for_unlocktime) + { + uint64_t unlock_time = get_tx_max_unlock_time(*oi.p_tx); + if (unlock_time < CURRENCY_MAX_BLOCK_NUMBER) + { + //interpret as block index + if (unlock_time > next_block_height) + continue; + } + else + { + //interpret as time + if (unlock_time > head_block_ts + DIFFICULTY_TOTAL_TARGET) + continue; + } + } + + } --rest; append = true; @@ -1109,20 +1154,28 @@ bool fill_output_entries(std::vector& out_indices, if (append) { - tx_source_entry::output_entry oe = AUTO_VAL_INIT(oe); - const txout_to_key& otk = boost::get(oi.out); - if (use_ref_by_id) // <-- HINT: this could be replaced by 'true' to enforce using ref_by_id across all the tests if needed + txout_ref_v out_ref_v{}; + if (use_ref_by_id) { ref_by_id rbi = AUTO_VAL_INIT(rbi); rbi.n = oi.out_no; rbi.tx_id = get_transaction_hash(*oi.p_tx); - oe = tx_source_entry::output_entry(rbi, otk.key); + out_ref_v = rbi; } else { - oe = tx_source_entry::output_entry(oi.idx, otk.key); + out_ref_v = oi.idx; } - output_entries.push_back(oe); + + VARIANT_SWITCH_BEGIN(oi.out_v) + VARIANT_CASE_CONST(tx_out_bare, ob) + VARIANT_SWITCH_BEGIN(ob.target) + VARIANT_CASE_CONST(txout_to_key, otk) + output_entries.emplace_back(out_ref_v, otk.key); + VARIANT_SWITCH_END() + VARIANT_CASE_CONST(tx_out_zarcanum, ozc) + output_entries.emplace_back(out_ref_v, ozc.stealth_address, ozc.concealing_point, ozc.amount_commitment); + VARIANT_SWITCH_END() } } @@ -1141,110 +1194,117 @@ bool fill_tx_sources(std::vector& sources, const std: const currency::block& blk_head, const currency::account_keys& from, uint64_t amount, size_t nmix, const std::vector& sources_to_avoid, bool check_for_spends, bool check_for_unlocktime, bool use_ref_by_id, uint64_t* p_sources_amount_found /* = nullptr */) { - map_output_idx_t outs; - map_output_t outs_mine; + map_output_idx_t outs; + map_output_t outs_mine; - std::vector blockchain; - map_hash2tx_t mtx; - if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head))) - return false; + std::vector blockchain; + map_hash2tx_t mtx; + if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head))) + return false; - if (!init_output_indices(outs, outs_mine, blockchain, mtx, from)) - return false; + if (!init_output_indices(outs, outs_mine, blockchain, mtx, from)) + return false; - if(check_for_spends) + if(check_for_spends) + { + if (!init_spent_output_indices(outs, outs_mine, blockchain, mtx, from)) + return false; + } + + // mark some outputs as spent to avoid their using + for (const auto& s : sources_to_avoid) + { + for (const auto& s_outputs_el : s.outputs) // avoid all outputs, including fake mix-ins { - if (!init_spent_output_indices(outs, outs_mine, blockchain, mtx, from)) - return false; - } - - // mark some outputs as spent to avoid their using - for (const auto& s : sources_to_avoid) - { - for (const auto& s_outputs_el : s.outputs) // avoid all outputs, including fake mix-ins + txout_ref_v sout = s_outputs_el.out_reference; + if (sout.type().hash_code() == typeid(uint64_t).hash_code()) // output by global index { - txout_ref_v sout = s_outputs_el.out_reference; - if (sout.type().hash_code() == typeid(uint64_t).hash_code()) // output by global index + uint64_t gindex = boost::get(sout); + auto& outs_by_amount = outs[s.amount]; + if (gindex >= outs_by_amount.size()) + return false; + outs_by_amount[gindex].spent = true; + } + else if (sout.type().hash_code() == typeid(ref_by_id).hash_code()) // output by ref_by_id + { + ref_by_id out_ref_by_id = boost::get(sout); + const auto it = mtx.find(out_ref_by_id.tx_id); + if (it == mtx.end()) + return false; + const transaction* p_tx = it->second; + for (auto& e : outs[s.amount]) // linear search by transaction among all outputs with such amount { - uint64_t gindex = boost::get(sout); - auto& outs_by_amount = outs[s.amount]; - if (gindex >= outs_by_amount.size()) - return false; - outs_by_amount[gindex].spent = true; - } - else if (sout.type().hash_code() == typeid(ref_by_id).hash_code()) // output by ref_by_id - { - ref_by_id out_ref_by_id = boost::get(sout); - const auto it = mtx.find(out_ref_by_id.tx_id); - if (it == mtx.end()) - return false; - const transaction* p_tx = it->second; - for (auto& e : outs[s.amount]) // linear search by transaction among all outputs with such amount + if (e.p_tx == p_tx) { - if (e.p_tx == p_tx) - { - e.spent = true; - p_tx = nullptr; // means 'found' - break; - } + e.spent = true; + p_tx = nullptr; // means 'found' + break; } - if (p_tx != nullptr) - return false; // output, referring by ref_by_id was not found - } - else - { - return false; // unknown output type } + if (p_tx != nullptr) + return false; // output, referring by ref_by_id was not found + } + else + { + return false; // unknown output type } } + } - // Iterate in reverse is more efficiency - uint64_t sources_amount = 0; - bool sources_found = false; - BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine) + uint64_t head_block_ts = get_actual_timestamp(blk_head); + uint64_t next_block_height = blockchain.size(); + + // Iterate in reverse is more efficiency + uint64_t sources_amount = 0; + bool sources_found = false; + BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine) + { + for (size_t i = 0; i < o.second.size() && !sources_found; ++i) { - for (size_t i = 0; i < o.second.size() && !sources_found; ++i) + size_t sender_out = o.second[i]; + const output_index& oi = outs[o.first][sender_out]; + if (oi.spent) + continue; + if (check_for_unlocktime) + { + uint64_t unlock_time = currency::get_tx_max_unlock_time(*oi.p_tx); + if (unlock_time < CURRENCY_MAX_BLOCK_NUMBER) { - size_t sender_out = o.second[i]; - const output_index& oi = outs[o.first][sender_out]; - if (oi.spent) - continue; - if (check_for_unlocktime) - { - if (currency::get_tx_max_unlock_time(*oi.p_tx) < CURRENCY_MAX_BLOCK_NUMBER) - { - //interpret as block index - if (currency::get_tx_max_unlock_time(*oi.p_tx) > blockchain.size()) - continue; - } - else - { - //interpret as time - } - } - - - currency::tx_source_entry ts = AUTO_VAL_INIT(ts); - ts.amount = oi.amount; - ts.real_output_in_tx_index = oi.out_no; - ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key - if (!fill_output_entries(outs[o.first], sender_out, nmix, ts.real_output, ts.outputs, use_ref_by_id)) - continue; - - sources.push_back(ts); - - sources_amount += ts.amount; - sources_found = amount <= sources_amount; + //interpret as block index + if (unlock_time > next_block_height) + continue; } + else + { + //interpret as time + if (unlock_time > head_block_ts + DIFFICULTY_TOTAL_TARGET) + continue; + } + } - if (sources_found) - break; + + currency::tx_source_entry ts = AUTO_VAL_INIT(ts); + ts.amount = oi.amount; + ts.real_out_amount_blinding_mask = oi.blinding_mask; + ts.real_output_in_tx_index = oi.out_no; + ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // source tx public key + if (!fill_output_entries(outs[o.first], sender_out, nmix, check_for_unlocktime, use_ref_by_id, next_block_height, head_block_ts, ts.real_output, ts.outputs)) + continue; + + sources.push_back(ts); + + sources_amount += ts.amount; + sources_found = amount <= sources_amount; } - if (p_sources_amount_found != nullptr) - *p_sources_amount_found = sources_amount; + if (sources_found) + break; + } - return sources_found; + if (p_sources_amount_found != nullptr) + *p_sources_amount_found = sources_amount; + + return sources_found; } bool fill_tx_sources_and_destinations(const std::vector& events, const block& blk_head, @@ -1264,9 +1324,9 @@ bool fill_tx_sources_and_destinations(const std::vector& event uint64_t source_amount_found = 0; bool r = fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix, std::vector(), check_for_spends, check_for_unlocktime, use_ref_by_id, &source_amount_found); - CHECK_AND_ASSERT_MES(r, false, "couldn't fill transaction sources: " << ENDL << + CHECK_AND_ASSERT_MES(r, false, "couldn't fill transaction sources (nmix = " << nmix << "): " << ENDL << " required: " << print_money(amount + fee) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * (amount + fee) / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" << ENDL << - " unspent coins: " << print_money(source_amount_found) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * source_amount_found / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" << ENDL << + " found coins: " << print_money(source_amount_found) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * source_amount_found / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" << ENDL << " lack of coins: " << print_money(amount + fee - source_amount_found) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * (amount + fee - source_amount_found) / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" ); @@ -1301,6 +1361,18 @@ bool fill_tx_sources_and_destinations(const std::vector& event case tests_digits_split_strategy: tools::detail::digit_split_strategy(dsts, change_dst, tgs.dust_threshold, destinations, dust, tgs.tx_max_out_amount); break; + case tests_random_split_strategy: + { + size_t outs_count = cache_back > 0 ? 2 : 1; + if (outs_count < tgs.rss_min_number_of_outputs) + { + // decompose both target and cache back amounts + // TODO: support tgs.tx_max_out_amount + decompose_amount_randomly(amount, [&](uint64_t a){ destinations.emplace_back(a, to.back()); }, tgs.rss_min_number_of_outputs, tgs.rss_num_digits_to_keep); + decompose_amount_randomly(cache_back, [&](uint64_t a){ destinations.emplace_back(a, from.account_address); }, tgs.rss_min_number_of_outputs, tgs.rss_num_digits_to_keep); + } + } + break; default: CHECK_AND_ASSERT_MES(false, false, "Invalid split strategy set in gentime settings"); } @@ -1436,7 +1508,7 @@ bool construct_tx_to_key(const currency::hard_forks_descriptor& hf, std::vector destinations; if (!fill_tx_sources_and_destinations(events, blk_head, from.get_keys(), to.get_public_address(), amount, fee, nmix, sources, destinations, check_for_spends, check_for_unlocktime)) return false; - uint64_t tx_version = currency::get_tx_version(get_block_height(blk_head), hf); + uint64_t tx_version = currency::get_tx_version(get_block_height(blk_head) + 1, hf); // assuming the tx will be in the next block (blk_head + 1) return construct_tx(from.get_keys(), sources, destinations, extr, att, tx, tx_version, sk, 0, mix_attr); } @@ -1506,33 +1578,36 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s return construct_tx(keys_from, sources, destinations, empty_attachment, tx, tx_version, 0); } -uint64_t get_balance(const currency::account_keys& addr, const std::vector& blockchain, const map_hash2tx_t& mtx, bool dbg_log) { - uint64_t res = 0; - std::map > outs; - std::map > outs_mine; +uint64_t get_balance(const currency::account_keys& addr, const std::vector& blockchain, const map_hash2tx_t& mtx, bool dbg_log) +{ + uint64_t res = 0; + std::map > outs; + std::map > outs_mine; - map_hash2tx_t confirmed_txs; - get_confirmed_txs(blockchain, mtx, confirmed_txs); + map_hash2tx_t confirmed_txs; + get_confirmed_txs(blockchain, mtx, confirmed_txs); - if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr)) - return false; + if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr)) + return false; - if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr)) - return false; + if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr)) + return false; - BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) { - for (size_t i = 0; i < o.second.size(); ++i) { - if (outs[o.first][o.second[i]].spent) - continue; + for (const map_output_t::value_type &o : outs_mine) + { + for (size_t i = 0; i < o.second.size(); ++i) + { + if (outs[o.first][o.second[i]].spent) + continue; - output_index& oiv = outs[o.first][o.second[i]]; - res += oiv.amount; - if (dbg_log) - LOG_PRINT_L0(oiv.toString()); - } + output_index& oiv = outs[o.first][o.second[i]]; + res += oiv.amount; + if (dbg_log) + LOG_PRINT_L0(oiv.to_string()); } + } - return res; + return res; } uint64_t get_balance(const currency::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx, bool dbg_log) @@ -1562,9 +1637,14 @@ void get_confirmed_txs(const std::vector& blockchain, const map bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { + size_t invalid_tx_index = UINT64_MAX; + size_t invalid_block_index = UINT64_MAX; std::unordered_map block_index; for(size_t i = 0, sz = events.size(); i < sz; ++i) { + if (invalid_tx_index == i || invalid_block_index == i) + continue; + const test_event_entry& ev = events[i]; if (typeid(currency::block) == ev.type()) { @@ -1584,6 +1664,14 @@ bool find_block_chain(const std::vector& events, std::vector(ev); mtx[get_transaction_hash(tx)] = &tx; } + else if (test_chain_unit_enchanced::is_event_mark_invalid_block(ev)) + { + invalid_block_index = i + 1; + } + else if (test_chain_unit_enchanced::is_event_mark_invalid_tx(ev)) + { + invalid_tx_index = i + 1; + } } bool b_success = false; @@ -1626,7 +1714,7 @@ bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name, uint64_t total, unlocked, awaiting_in, awaiting_out, mined; balance_via_wallet(w, &total, &unlocked, &awaiting_in, &awaiting_out, &mined); - LOG_PRINT_CYAN("Balance for wallet " << account_name << ":" << ENDL << + LOG_PRINT_CYAN("Balance for wallet " << account_name << " @ height " << w.get_top_block_height() << ":" << ENDL << "unlocked: " << print_money(unlocked) << ENDL << "awaiting in: " << print_money(awaiting_in) << ENDL << "awaiting out: " << print_money(awaiting_out) << ENDL << @@ -1925,7 +2013,7 @@ bool generate_pos_block_with_given_coinstake(test_generator& generator, const st crypto::public_key stake_output_pubkey = boost::get(boost::get(stake_tx.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner.get_public_address()); @@ -1992,16 +2080,36 @@ bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, c for (size_t i = 0; i < ptce->tx.vin.size(); ++i) { auto& input = ptce->tx.vin[i]; - if (input.type() == typeid(txin_to_key)) - { - auto& intk = boost::get(input); - CHECK_AND_ASSERT_MES(intk.key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << intk.key_offsets.size() - 1 << ", expected is " << mixin); - } + const std::vector& key_offsets = get_key_offsets_from_txin_v(input); + CHECK_AND_ASSERT_MES(key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << key_offsets.size() - 1 << ", expected is " << mixin); } return true; } +// randomly shuffles tx_source_entry, restores the correct real_output afterwards +bool shuffle_source_entry(tx_source_entry& se) +{ + if (se.outputs.size() < 2) + return true; + tx_source_entry::output_entry real_out_entry = se.outputs[se.real_output]; // store the real one + std::shuffle(se.outputs.begin(), se.outputs.end(), crypto::uniform_random_bit_generator{}); // shuffle + auto it = std::find(se.outputs.begin(), se.outputs.end(), real_out_entry); // where is the real one now? + CHECK_AND_ASSERT_MES(it != se.outputs.end(), false, "cannot find the real one output entry"); + se.real_output = it - se.outputs.begin(); // restore the real output index + return true; +} + +// randomly shuffles std::vector, restores the correct real_output afterwards +bool shuffle_source_entries(std::vector& sources) +{ + for(auto& se : sources) + if (!shuffle_source_entry(se)) + return false; + return true; +} + + //------------------------------------------------------------------------------ void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb) @@ -2033,6 +2141,11 @@ bool test_chain_unit_base::verify(const std::string& cb_name, currency::core& c, return cb_it->second(c, ev_index, events); } +void test_chain_unit_base::on_test_generator_created(test_generator& gen) const +{ + gen.set_hardforks(m_hardforks); +} + //------------------------------------------------------------------------------ test_chain_unit_enchanced::test_chain_unit_enchanced() @@ -2055,6 +2168,7 @@ test_chain_unit_enchanced::test_chain_unit_enchanced() REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, remove_stuck_txs); REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_offers_count); REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_hardfork_active); + REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_hardfork_inactive); } bool test_chain_unit_enchanced::configure_core(currency::core& c, size_t ev_index, const std::vector& events) @@ -2191,12 +2305,51 @@ bool test_chain_unit_enchanced::check_hardfork_active(currency::core& c, size_t const std::string& params = boost::get(events[ev_index]).callback_params; CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, hardfork_id_to_check), false, "hex_to_pod failed, params = " << params); - uint64_t top_block_height = c.get_top_block_height(); - if (!c.get_blockchain_storage().get_core_runtime_config().is_hardfork_active_for_height(hardfork_id_to_check, top_block_height)) + if (!c.get_blockchain_storage().is_hardfork_active(hardfork_id_to_check)) { - LOG_ERROR("Hardfork #" << hardfork_id_to_check << " is not active yet (top block height is " << top_block_height << ")"); + LOG_ERROR("Hardfork #" << hardfork_id_to_check << " is not active yet (top block height is " << c.get_top_block_height() << ")"); return false; } return true; } + +bool test_chain_unit_enchanced::check_hardfork_inactive(currency::core& c, size_t ev_index, const std::vector& events) +{ + size_t hardfork_id_to_check = 0; + const std::string& params = boost::get(events[ev_index]).callback_params; + CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, hardfork_id_to_check), false, "hex_to_pod failed, params = " << params); + + if (c.get_blockchain_storage().is_hardfork_active(hardfork_id_to_check)) + { + LOG_ERROR("Hardfork #" << hardfork_id_to_check << " is active, which is not expected (top block height is " << c.get_top_block_height() << ")"); + return false; + } + + return true; +} + +/*static*/ bool test_chain_unit_enchanced::is_event_mark_invalid_block(const test_event_entry& ev, bool use_global_gentime_settings /* = true */) +{ + if (use_global_gentime_settings && !test_generator::get_test_gentime_settings().ignore_invalid_blocks) + return false; + + if (typeid(callback_entry) != ev.type()) + return false; + + const callback_entry& ce = boost::get(ev); + return ce.callback_name == "mark_invalid_block"; +} + +/*static*/ bool test_chain_unit_enchanced::is_event_mark_invalid_tx(const test_event_entry& ev, bool use_global_gentime_settings /* = true */) +{ + if (use_global_gentime_settings && !test_generator::get_test_gentime_settings().ignore_invalid_txs) + return false; + + if (typeid(callback_entry) != ev.type()) + return false; + + const callback_entry& ce = boost::get(ev); + return ce.callback_name == "mark_invalid_tx"; +} + diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 9a9ea641..32fa8f86 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -212,17 +212,21 @@ VARIANT_TAG(binary_archive, event_core_time, 0xd1); typedef boost::variant test_event_entry; typedef std::unordered_map map_hash2tx_t; -enum test_tx_split_strategy { tests_void_split_strategy, tests_null_split_strategy, tests_digits_split_strategy }; +enum test_tx_split_strategy { tests_void_split_strategy, tests_null_split_strategy, tests_digits_split_strategy, tests_random_split_strategy }; struct test_gentime_settings { - test_gentime_settings(test_tx_split_strategy split_strategy, size_t miner_tx_max_outs, uint64_t tx_max_out_amount, uint64_t dust_threshold) - : split_strategy(split_strategy), miner_tx_max_outs(miner_tx_max_outs), tx_max_out_amount(tx_max_out_amount), dust_threshold(dust_threshold) {} - test_tx_split_strategy split_strategy; - size_t miner_tx_max_outs; - uint64_t tx_max_out_amount; - uint64_t dust_threshold; + test_tx_split_strategy split_strategy = tests_digits_split_strategy; + size_t miner_tx_max_outs = CURRENCY_MINER_TX_MAX_OUTS; + uint64_t tx_max_out_amount = WALLET_MAX_ALLOWED_OUTPUT_AMOUNT; + uint64_t dust_threshold = DEFAULT_DUST_THRESHOLD; + size_t rss_min_number_of_outputs = CURRENCY_TX_MIN_ALLOWED_OUTS; // for random split strategy: min (target) number of tx outputs, one output will be split into this many parts + size_t rss_num_digits_to_keep = CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP; // for random split strategy: number of digits to keep + bool ignore_invalid_blocks = true; // gen-time blockchain building: don't take into account blocks marked as invalid ("mark_invalid_block") + bool ignore_invalid_txs = true; // gen-time blockchain building: don't take into account txs marked as invalid ("mark_invalid_txs") }; +class test_generator; + class test_chain_unit_base { public: @@ -236,6 +240,8 @@ public: void set_core_proxy(std::shared_ptr) { /* do nothing */ } uint64_t get_tx_version_from_events(const std::vector &events) const; + void on_test_generator_created(test_generator& generator) const; // tests can override this for special initialization + private: callbacks_map m_callbacks; @@ -313,8 +319,10 @@ public: bool remove_stuck_txs(currency::core& c, size_t ev_index, const std::vector& events); bool check_offers_count(currency::core& c, size_t ev_index, const std::vector& events); bool check_hardfork_active(currency::core& c, size_t ev_index, const std::vector& events); + bool check_hardfork_inactive(currency::core& c, size_t ev_index, const std::vector& events); - + static bool is_event_mark_invalid_block(const test_event_entry& ev, bool use_global_gentime_settings = true); + static bool is_event_mark_invalid_tx(const test_event_entry& ev, bool use_global_gentime_settings = true); protected: struct params_top_block @@ -434,14 +442,13 @@ public: bool init_test_wallet(const currency::account_base& account, const crypto::hash& genesis_hash, std::shared_ptr &result); bool refresh_test_wallet(const std::vector& events, tools::wallet2* w, const crypto::hash& top_block_hash, size_t expected_blocks_to_be_fetched = std::numeric_limits::max()); - bool sign_block(currency::block& b, - currency::pos_entry& pe, - tools::wallet2& w, - const tools::wallet2::mining_context& mining_context, - const blockchain_vector& blocks, - const outputs_index& oi); + bool sign_block(const tools::wallet2::mining_context& mining_context, + const currency::pos_entry& pe, + const tools::wallet2& w, + const crypto::scalar_t& blinding_masks_sum, + currency::block& b); - bool get_output_details_by_global_index(const test_generator::blockchain_vector& blck_chain, + /*bool get_output_details_by_global_index(const test_generator::blockchain_vector& blck_chain, const test_generator::outputs_index& indexes, uint64_t amount, uint64_t global_index, @@ -449,7 +456,7 @@ public: const currency::transaction* tx, uint64_t& tx_out_index, crypto::public_key& tx_pub_key, - crypto::public_key& output_key); + crypto::public_key& output_key);*/ @@ -538,9 +545,16 @@ private: std::unordered_map m_blocks_info; static test_gentime_settings m_test_gentime_settings; - static test_gentime_settings m_test_gentime_settings_default; + static const test_gentime_settings m_test_gentime_settings_default; }; // class class test_generator +struct test_gentime_settings_restorer +{ + test_gentime_settings_restorer() : m_settings(test_generator::get_test_gentime_settings()) {} + ~test_gentime_settings_restorer() { test_generator::set_test_gentime_settings(m_settings); } + test_gentime_settings m_settings; +}; + extern const crypto::signature invalid_signature; // invalid non-null signature for test purpose static const std::vector empty_extra; static const std::vector empty_attachment; @@ -680,6 +694,9 @@ bool check_ring_signature_at_gen_time(const std::vector& event bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, currency::core& c); +bool shuffle_source_entry(currency::tx_source_entry& se); +bool shuffle_source_entries(std::vector& sources); + //-------------------------------------------------------------------------- template auto do_check_tx_verification_context(const currency::tx_verification_context& tvc, bool tx_added, size_t event_index, const currency::transaction& tx, t_test_class& validator, int) @@ -790,7 +807,7 @@ bool construct_broken_tx(const currency::account_keys& sender_account_keys, cons BOOST_FOREACH(const currency::tx_source_entry::output_entry& out_entry, src_entr.outputs) input_to_key.key_offsets.push_back(out_entry.out_reference); - input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets); + input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets); // TODO @#@# tx.vin.push_back(input_to_key); } @@ -1006,6 +1023,7 @@ void append_vector_by_another_vector(U& dst, const V& src) #define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \ PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_GENESIS_BLOCK(" << #BLK_NAME << ")"); \ test_generator generator; \ + this->on_test_generator_created(generator); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ generator.construct_genesis_block(BLK_NAME, MINER_ACC, TS); \ VEC_EVENTS.push_back(BLK_NAME) @@ -1019,19 +1037,19 @@ void append_vector_by_another_vector(U& dst, const V& src) #define MAKE_NEXT_BLOCK_TIMESTAMP_ADJUSTMENT(ADJ, VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TIMESTAMP_ADJUSTMENT(" << #BLK_NAME << ")"); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ generator.construct_block(ADJ, VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC); \ VEC_EVENTS.push_back(BLK_NAME) #define MAKE_NEXT_POS_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, MINERS_ACC_LIST) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_POS_BLOCK(" << #BLK_NAME << ")"); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, std::list(), MINERS_ACC_LIST); \ VEC_EVENTS.push_back(BLK_NAME) #define MAKE_NEXT_POS_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, MINERS_ACC_LIST, TX_1) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_POS_BLOCK_TX1(" << #BLK_NAME << ")"); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ { \ std::listtx_list; \ @@ -1041,7 +1059,7 @@ void append_vector_by_another_vector(U& dst, const V& src) VEC_EVENTS.push_back(BLK_NAME) #define MAKE_NEXT_BLOCK_NO_ADD(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_NO_ADD(" << #BLK_NAME << ")"); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC); \ VEC_EVENTS.push_back(event_special_block(BLK_NAME, event_special_block::flag_skip)) @@ -1051,7 +1069,7 @@ void append_vector_by_another_vector(U& dst, const V& src) VEC_EVENTS.push_back(BLK_NAME) #define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \ - PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TX1(" << #BLK_NAME << ")"); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TX1(" << #BLK_NAME << ", " << #TX1 << ")"); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ { \ std::list tx_list; \ @@ -1062,7 +1080,7 @@ void append_vector_by_another_vector(U& dst, const V& src) #define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TX_LIST(" << #BLK_NAME << ", " << #TXLIST << ")"); \ currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \ generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \ VEC_EVENTS.push_back(BLK_NAME) @@ -1095,7 +1113,7 @@ void append_vector_by_another_vector(U& dst, const V& src) #define MAKE_TX_MIX_ATTR_EXTRA(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD, MIX_ATTR, EXTRA, CHECK_SPENDS) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "transaction " << #TX_NAME); \ currency::transaction TX_NAME; \ { \ bool txr = construct_tx_to_key(generator.get_hardforks(), VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, generator.last_tx_generated_secret_key, MIX_ATTR, EXTRA, std::vector(), CHECK_SPENDS); \ @@ -1104,7 +1122,7 @@ void append_vector_by_another_vector(U& dst, const V& src) VEC_EVENTS.push_back(TX_NAME) #define MAKE_TX_FEE_MIX_ATTR_EXTRA(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, FEE, NMIX, HEAD, MIX_ATTR, EXTRA, CHECK_SPENDS) \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "transaction " << #TX_NAME); \ currency::transaction TX_NAME; \ { \ bool txr = construct_tx_to_key(generator.get_hardforks(), VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, FEE, NMIX, generator.last_tx_generated_secret_key, MIX_ATTR, EXTRA, std::vector(), CHECK_SPENDS); \ @@ -1129,7 +1147,7 @@ void append_vector_by_another_vector(U& dst, const V& src) #define MAKE_TX_MIX_LIST_EXTRA_MIX_ATTR(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, MIX_ATTR, EXTRA, ATTACH) \ { \ - PRINT_EVENT_N(VEC_EVENTS); \ + PRINT_EVENT_N_TEXT(VEC_EVENTS, "transaction list " << #SET_NAME); \ currency::transaction t; \ bool r = construct_tx_to_key(generator.get_hardforks(), VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, generator.last_tx_generated_secret_key, MIX_ATTR, EXTRA, ATTACH); \ if (!r) { LOG_PRINT_YELLOW("ERROR in tx @ EVENT #" << VEC_EVENTS.size(), LOG_LEVEL_0); } \ @@ -1185,10 +1203,14 @@ void append_vector_by_another_vector(U& dst, const V& src) // Adjust gentime and playtime "time" at once #define ADJUST_TEST_CORE_TIME(desired_time) \ - PRINT_EVENT_N(events); \ + PRINT_EVENT_N_TEXT(events, "ADJUST_TEST_CORE_TIME(" << desired_time << ")"); \ test_core_time::adjust(desired_time); \ events.push_back(event_core_time(desired_time)) +#define ADD_CUSTOM_EVENT_CODE(VEC_EVENTS, CODE) PRINT_EVENT_N_TEXT(VEC_EVENTS, #CODE); CODE + +#define ADD_CUSTOM_EVENT(VEC_EVENTS, EVENT_OBJ) PRINT_EVENT_N_TEXT(VEC_EVENTS, #EVENT_OBJ); VEC_EVENTS.push_back(EVENT_OBJ) + // --- gentime wallet helpers ----------------------------------------------------------------------- #define CREATE_TEST_WALLET(WLT_VAR, ACCOUNT, GENESIS_BLOCK) \ diff --git a/tests/core_tests/chaingen_helpers.h b/tests/core_tests/chaingen_helpers.h index 77877b5b..83d5da23 100644 --- a/tests/core_tests/chaingen_helpers.h +++ b/tests/core_tests/chaingen_helpers.h @@ -87,7 +87,7 @@ inline bool mine_next_pow_block_in_playtime_with_given_txs(const currency::accou { CRITICAL_REGION_LOCAL(s_locker); loc_helper::txs_accessor() = &txs; - r = c.get_blockchain_storage().create_block_template(b, miner_addr, miner_addr, diff, height_from_template, extra, false, pe, loc_helper::fill_block_template_func); + r = c.get_blockchain_storage().create_block_template(miner_addr, miner_addr, extra, false, pe, loc_helper::fill_block_template_func, b, diff, height_from_template); } CHECK_AND_ASSERT_MES(r, false, "get_block_template failed"); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 129198f9..87a767a2 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -106,8 +106,9 @@ bool generate_and_play(const char* const genclass_name) { std::vector events; bool generated = false; - bool result = true; - std::cout << ENDL << concolor::bright_white << "#TEST# " << genclass_name << concolor::normal << ENDL << ENDL; + bool result = false; + std::cout << ENDL << concolor::bright_white << "#TEST# >>>> " << genclass_name << " <<<<" << ENDL << ENDL; + LOG_PRINT2("get_object_blobsize.log", "#TEST# " << genclass_name, LOG_LEVEL_3); if (!clean_data_directory()) @@ -118,29 +119,36 @@ bool generate_and_play(const char* const genclass_name) genclass g; try { - generated = g.generate(events);; + generated = g.generate(events); + if (generated) + { + std::cout << concolor::bright_white << std::string(100, '=') << std::endl << + "#TEST# >>>> " << genclass_name << " <<<< start replaying events" << std::endl << + std::string(100, '=') << concolor::normal << std::endl; + + result = do_replay_events(events, g); + } } catch (const std::exception& ex) { - LOG_ERROR(genclass_name << " generation failed: what=" << ex.what()); + LOG_ERROR("got an exception during " << genclass_name << (generated ? " replaying: " : " generation: ") << ex.what()); } catch (...) { - LOG_ERROR(genclass_name << " generation failed: generic exception"); + LOG_ERROR("got an unknown exception during " << genclass_name << (generated ? " replaying" : " generation")); } - std::cout << concolor::bright_white << std::string(100, '=') << std::endl << - "#TEST# >>>> " << genclass_name << " <<<< start replaying events" << std::endl << - std::string(100, '=') << concolor::normal << std::endl; - - if (generated && do_replay_events(events, g)) + if (result) { - std::cout << concolor::green << "#TEST# Succeeded " << genclass_name << concolor::normal << std::endl; + std::cout << concolor::green << std::string(100, '=') << std::endl << + "#TEST# >>>> " << genclass_name << " <<<< Succeeded" << std::endl << + std::string(100, '=') << concolor::normal << std::endl; } else { - std::cout << concolor::magenta << "#TEST# Failed " << genclass_name << concolor::normal << std::endl; - LOG_PRINT_RED_L0("#TEST# Failed " << genclass_name); + std::cout << concolor::red << std::string(100, '=') << std::endl << + "#TEST# >>>> " << genclass_name << " <<<< FAILED" << std::endl << + std::string(100, '=') << concolor::normal << std::endl; result = false; } std::cout << std::endl; @@ -667,6 +675,7 @@ int main(int argc, char* argv[]) po::options_description desc_options("Allowed options"); command_line::add_arg(desc_options, command_line::arg_help); + command_line::add_arg(desc_options, command_line::arg_log_level); command_line::add_arg(desc_options, arg_test_data_path); command_line::add_arg(desc_options, arg_generate_test_data); command_line::add_arg(desc_options, arg_play_test_data); @@ -697,6 +706,16 @@ int main(int argc, char* argv[]) return 0; } + if (command_line::has_arg(g_vm, command_line::arg_log_level)) + { + int new_log_level = command_line::get_arg(g_vm, command_line::arg_log_level); + if (new_log_level >= LOG_LEVEL_MIN && new_log_level <= LOG_LEVEL_MAX && log_space::get_set_log_detalisation_level(false) != new_log_level) + { + log_space::get_set_log_detalisation_level(true, new_log_level); + LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level); + } + } + if (command_line::has_arg(g_vm, arg_stop_on_fail)) { stop_on_first_fail = command_line::get_arg(g_vm, arg_stop_on_fail); @@ -761,8 +780,6 @@ int main(int argc, char* argv[]) MARK_TEST_AS_POSTPONED(before_hard_fork_1_cumulative_difficulty); MARK_TEST_AS_POSTPONED(inthe_middle_hard_fork_1_cumulative_difficulty); - MARK_TEST_AS_POSTPONED(zarcanum_basic_test); - #undef MARK_TEST_AS_POSTPONED @@ -1063,6 +1080,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(multiassets_basic_test); GENERATE_AND_PLAY(zarcanum_test_n_inputs_validation); + GENERATE_AND_PLAY(zarcanum_gen_time_balance); + GENERATE_AND_PLAY(zarcanum_txs_with_big_shuffled_decoy_set_shuffled); // GENERATE_AND_PLAY(gen_block_reward); // END OF TESTS */ diff --git a/tests/core_tests/checkpoints_tests.cpp b/tests/core_tests/checkpoints_tests.cpp index 3e7b0b76..87218fe0 100644 --- a/tests/core_tests/checkpoints_tests.cpp +++ b/tests/core_tests/checkpoints_tests.cpp @@ -559,7 +559,7 @@ bool gen_checkpoints_pos_validation_on_altchain::generate(std::vector(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), miner_acc.get_public_address()); @@ -588,7 +588,7 @@ bool gen_checkpoints_pos_validation_on_altchain::generate(std::vector(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), miner_acc.get_public_address()); @@ -654,7 +654,7 @@ bool gen_no_attchments_in_coinbase::init_config_set_cp(currency::core& c, size_t crc.pos_minimum_heigh = 1; c.get_blockchain_storage().set_core_runtime_config(crc); - m_checkpoints.add_checkpoint(12, "8cce274f3ade893fa407a5215b90c595d2ecb036e76ad934b3410da4e8bc7c2c"); + m_checkpoints.add_checkpoint(12, "ac57db2582acdd076f92aa8dfcb88d216f60e35b805c16b6256ca26e023bfc3c"); c.set_checkpoints(currency::checkpoints(m_checkpoints)); return true; diff --git a/tests/core_tests/emission_test.cpp b/tests/core_tests/emission_test.cpp index 5722ac0e..f5aca21b 100644 --- a/tests/core_tests/emission_test.cpp +++ b/tests/core_tests/emission_test.cpp @@ -92,7 +92,7 @@ bool emission_test::c1(currency::core& c, size_t ev_index, const std::vectorm_global_output_indexes.size(), false, ""); @@ -105,11 +105,11 @@ bool emission_test::c1(currency::core& c, size_t ev_index, const std::vector(boost::get(stake.vout[stake_output_idx]).target).key; - difficulty = c.get_blockchain_storage().get_next_diff_conditional(true); + difficulty = bcs.get_next_diff_conditional(true); //size_t median_size = 0; // little hack: we're using small blocks (only coinbase tx), so we're in CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE - don't need to calc median size pb.clear(); - pb.step1_init_header(get_block_height(b) + 1, prev_id); + pb.step1_init_header(bcs.get_core_runtime_config().hard_forks, get_block_height(b) + 1, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, timestamp); pb.step4_generate_coinbase_tx(0, already_generated_coins, m_miner_acc.get_public_address()); diff --git a/tests/core_tests/hard_fork_1.cpp b/tests/core_tests/hard_fork_1.cpp index 9134c831..fa12a578 100644 --- a/tests/core_tests/hard_fork_1.cpp +++ b/tests/core_tests/hard_fork_1.cpp @@ -397,7 +397,7 @@ bool hard_fork_1_checkpoint_basic_test::generate(std::vector& crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION; pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); @@ -573,7 +573,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector& e crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address()); @@ -612,7 +612,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector& e crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION; pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); @@ -660,7 +660,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector& e crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION; pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); @@ -757,7 +757,7 @@ bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION; pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); @@ -802,7 +802,7 @@ bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION; pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); @@ -846,7 +846,7 @@ bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION; pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); diff --git a/tests/core_tests/hard_fork_2.cpp b/tests/core_tests/hard_fork_2.cpp index 5935968a..892cb713 100644 --- a/tests/core_tests/hard_fork_2.cpp +++ b/tests/core_tests/hard_fork_2.cpp @@ -60,7 +60,6 @@ bool hard_fork_2_tx_payer_in_wallet::generate(std::vector& eve account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); - set_hard_fork_heights_to_generator(generator); DO_CALLBACK(events, "configure_core"); REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); @@ -307,7 +306,6 @@ bool hard_fork_2_tx_receiver_in_wallet::generate(std::vector& account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); - set_hard_fork_heights_to_generator(generator); DO_CALLBACK(events, "configure_core"); REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); @@ -448,7 +446,6 @@ bool hard_fork_2_tx_extra_alias_entry_in_wallet::generate(std::vector& events) const { + test_gentime_settings tgs = test_generator::get_test_gentime_settings(); + tgs.ignore_invalid_txs = false; // this test pushes originally invalid tx_0, tx_1 and tx_2 which are good after HF2, so we'd like to avoid mess with the sources among txs + test_generator::set_test_gentime_settings(tgs); + bool r = false; m_accounts.resize(TOTAL_ACCS_COUNT); account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); - set_hard_fork_heights_to_generator(generator); DO_CALLBACK(events, "configure_core"); REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); @@ -830,9 +829,9 @@ bool hard_fork_2_no_new_structures_before_hf::generate(std::vector::generate(std::vector::generate(std::vector::generate(std::vector& events) c // noramal input -> multisig output with unlock time std::vector sources; std::vector destinations; - r = fill_tx_sources_and_destinations(events, blk_0r, miner_acc.get_keys(), ms_addr_list, amount, TESTS_DEFAULT_FEE, 1, sources, destinations, true, true, 1); + r = fill_tx_sources_and_destinations(events, blk_0r, miner_acc.get_keys(), ms_addr_list, amount, TESTS_DEFAULT_FEE, 1 /*nmix*/, sources, destinations, true, true, 1 /* minimum sigs */); CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed"); uint64_t unlock_time = blk_0r.timestamp + DIFFICULTY_TOTAL_TARGET * 3 + CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS; @@ -1251,10 +1251,17 @@ bool multisig_and_unlock_time::generate(std::vector& events) c ADJUST_TEST_CORE_TIME(unlock_time_2 - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS - 1); DO_CALLBACK(events, "mark_invalid_tx"); - MAKE_TX(events, tx_3, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, blk_2); + // instead of MAKE_TX use manual construction to set check_for_unlocktime = false, old: MAKE_TX(events, tx_3, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, blk_2); + r = fill_tx_sources_and_destinations(events, blk_2, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, TESTS_DEFAULT_FEE, 0 /*nmix*/, sources, destinations, true, false /* check_for_unlocktime */); + CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed"); + transaction tx_3{}; + r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_3, get_tx_version_from_events(events), 0); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + ADD_CUSTOM_EVENT(events, tx_3); + ADJUST_TEST_CORE_TIME(unlock_time_2 - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS + 1); - events.push_back(tx_3); + ADD_CUSTOM_EVENT(events, tx_3); MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_3); DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(BOB_ACC_IDX, amount - TESTS_DEFAULT_FEE * 2)); @@ -1362,11 +1369,11 @@ bool multisig_and_coinbase::generate(std::vector& events) cons keypair tx_key = keypair::generate(); pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address(), - blobdata(), CURRENCY_MINER_TX_MAX_OUTS, extra_alias_entry(), tx_key); + blobdata(), CURRENCY_MINER_TX_MAX_OUTS, &tx_key); // The builder creates PoS miner tx with normal outputs. // Replace all miner_tx outputs with one multisig output and re-sign it. diff --git a/tests/core_tests/pos_block_builder.cpp b/tests/core_tests/pos_block_builder.cpp index acdd9322..9231b200 100644 --- a/tests/core_tests/pos_block_builder.cpp +++ b/tests/core_tests/pos_block_builder.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,23 +9,17 @@ using namespace epee; using namespace currency; -pos_block_builder::pos_block_builder() -{ - clear(); -} - void pos_block_builder::clear() { - m_block = AUTO_VAL_INIT(m_block); - m_stake_kernel = AUTO_VAL_INIT(m_stake_kernel); - m_step = 0; + *this = pos_block_builder{}; } -void pos_block_builder::step1_init_header(size_t block_height, crypto::hash& prev_block_hash) + +void pos_block_builder::step1_init_header(const hard_forks_descriptor& hardforks, size_t block_height, crypto::hash& prev_block_hash) { CHECK_AND_ASSERT_THROW_MES(m_step == 0, "pos_block_builder: incorrect step sequence"); m_block.minor_version = CURRENT_BLOCK_MINOR_VERSION; - m_block.major_version = BLOCK_MAJOR_VERSION_INITIAL; + m_block.major_version = hardforks.get_block_major_version_by_height(block_height); m_block.timestamp = 0; // to be set at step 3 m_block.prev_id = prev_block_hash; m_block.flags = CURRENCY_BLOCK_FLAG_POS_BLOCK; @@ -33,9 +27,12 @@ void pos_block_builder::step1_init_header(size_t block_height, crypto::hash& pre m_height = block_height; + m_context.zarcanum = hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, m_height); + m_step = 1; } + void pos_block_builder::step2_set_txs(const std::vector& txs) { CHECK_AND_ASSERT_THROW_MES(m_step == 1, "pos_block_builder: incorrect step sequence"); @@ -57,6 +54,7 @@ void pos_block_builder::step2_set_txs(const std::vector& m_step = 2; } + void pos_block_builder::step3_build_stake_kernel( uint64_t stake_output_amount, size_t stake_output_gindex, @@ -68,78 +66,119 @@ void pos_block_builder::step3_build_stake_kernel( uint64_t timestamp_window, uint64_t timestamp_step) { - CHECK_AND_ASSERT_THROW_MES(m_step == 2, "pos_block_builder: incorrect step sequence"); - m_pos_stake_amount = stake_output_amount; - m_pos_stake_output_gindex = stake_output_gindex; + step3a(difficulty, last_pow_block_hash, last_pos_block_kernel_hash); - m_stake_kernel.kimage = stake_output_key_image; - m_stake_kernel.block_timestamp = m_block.timestamp; - m_stake_kernel.stake_modifier.last_pow_id = last_pow_block_hash; - m_stake_kernel.stake_modifier.last_pos_kernel_id = last_pos_block_kernel_hash; + crypto::public_key stake_source_tx_pub_key {}; + uint64_t stake_out_in_tx_index = UINT64_MAX; + crypto::scalar_t stake_out_blinding_mask {}; + crypto::secret_key view_secret {}; + + step3b(stake_output_amount, stake_output_key_image, stake_source_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, view_secret, stake_output_gindex, + timestamp_lower_bound, timestamp_window, timestamp_step); +} + + +void pos_block_builder::step3a( + currency::wide_difficulty_type difficulty, + const crypto::hash& last_pow_block_hash, + const crypto::hash& last_pos_block_kernel_hash + ) +{ + CHECK_AND_ASSERT_THROW_MES(m_step == 2, "pos_block_builder: incorrect step sequence"); + + stake_modifier_type sm{}; + sm.last_pow_id = last_pow_block_hash; + sm.last_pos_kernel_id = last_pos_block_kernel_hash; if (last_pos_block_kernel_hash == null_hash) { - bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, m_stake_kernel.stake_modifier.last_pos_kernel_id); + bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, sm.last_pos_kernel_id); CHECK_AND_ASSERT_THROW_MES(r, "Failed to parse POS_STARTER_KERNEL_HASH"); } - wide_difficulty_type stake_difficulty = difficulty / stake_output_amount; + m_context.init(difficulty, sm, m_context.zarcanum); + m_step = 31; +} + + +void pos_block_builder::step3b( + uint64_t stake_output_amount, + const crypto::key_image& stake_output_key_image, + const crypto::public_key& stake_source_tx_pub_key, // zarcanum only + uint64_t stake_out_in_tx_index, // zarcanum only + const crypto::scalar_t& stake_out_blinding_mask, // zarcanum only + const crypto::secret_key& view_secret, // zarcanum only + size_t stake_output_gindex, + uint64_t timestamp_lower_bound, + uint64_t timestamp_window, + uint64_t timestamp_step) +{ + CHECK_AND_ASSERT_THROW_MES(m_step == 31, "pos_block_builder: incorrect step sequence"); + + m_pos_stake_output_gindex = stake_output_gindex; + + m_context.prepare_entry(stake_output_amount, stake_output_key_image, stake_source_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, view_secret); + // align timestamp_lower_bound up to timestamp_step boundary if needed if (timestamp_lower_bound % timestamp_step != 0) timestamp_lower_bound = timestamp_lower_bound - (timestamp_lower_bound % timestamp_step) + timestamp_step; bool sk_found = false; for (uint64_t ts = timestamp_lower_bound; !sk_found && ts < timestamp_lower_bound + timestamp_window; ts += timestamp_step) { - m_stake_kernel.block_timestamp = ts; - crypto::hash sk_hash = crypto::cn_fast_hash(&m_stake_kernel, sizeof(m_stake_kernel)); - if (check_hash(sk_hash, stake_difficulty)) - { + if (m_context.do_iteration(ts)) sk_found = true; - } } if (!sk_found) ASSERT_MES_AND_THROW("Could't build stake kernel"); // update block header with found timestamp - m_block.timestamp = m_stake_kernel.block_timestamp; + m_block.timestamp = m_context.sk.block_timestamp; m_step = 3; } + + + + void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, const account_public_address &reward_and_stake_receiver_address, const blobdata& extra_nonce, size_t max_outs, - const extra_alias_entry& alias, - keypair tx_one_time_key) + const keypair* tx_one_time_key_to_use) { - step4_generate_coinbase_tx(median_size, already_generated_coins, reward_and_stake_receiver_address, reward_and_stake_receiver_address, extra_nonce, max_outs, alias, tx_one_time_key); + step4_generate_coinbase_tx(median_size, already_generated_coins, reward_and_stake_receiver_address, reward_and_stake_receiver_address, extra_nonce, max_outs, tx_one_time_key_to_use); } + void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, const account_public_address &reward_receiver_address, const account_public_address &stakeholder_address, const blobdata& extra_nonce, size_t max_outs, - const extra_alias_entry& alias, - keypair tx_one_time_key) + const keypair* tx_one_time_key_to_use) { CHECK_AND_ASSERT_THROW_MES(m_step == 3, "pos_block_builder: incorrect step sequence"); + uint64_t tx_version = m_context.zarcanum ? TRANSACTION_VERSION_POST_HF4 : TRANSACTION_VERSION_PRE_HF4; + pos_entry pe{}; + pe.stake_unlock_time = 0; // TODO + pe.amount = m_context.stake_amount; + // generate miner tx using incorrect current_block_size only for size estimation size_t estimated_block_size = m_txs_total_size; - bool r = construct_homemade_pos_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, m_pos_stake_amount, m_stake_kernel.kimage, - m_pos_stake_output_gindex, reward_receiver_address, stakeholder_address, m_block.miner_tx, extra_nonce, max_outs, tx_one_time_key); - CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed"); + bool r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, + reward_receiver_address, stakeholder_address, m_block.miner_tx, tx_version, extra_nonce, max_outs, true, pe, &m_blinding_masks_sum, tx_one_time_key_to_use); + CHECK_AND_ASSERT_THROW_MES(r, "construct_miner_tx failed"); estimated_block_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx); size_t cumulative_size = 0; for (size_t try_count = 0; try_count != 10; ++try_count) { - r = construct_homemade_pos_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, m_pos_stake_amount, m_stake_kernel.kimage, - m_pos_stake_output_gindex, reward_receiver_address, stakeholder_address, m_block.miner_tx, extra_nonce, max_outs, tx_one_time_key); + r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, + reward_receiver_address, stakeholder_address, m_block.miner_tx, tx_version, extra_nonce, max_outs, true, pe, &m_blinding_masks_sum, tx_one_time_key_to_use); CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed"); cumulative_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx); @@ -159,138 +198,94 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, m_step = 4; } -void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account) +// supports Zarcanum and mixins +void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const currency::account_keys& stakeholder_keys) { + bool r = false; CHECK_AND_ASSERT_THROW_MES(m_step == 4, "pos_block_builder: incorrect step sequence"); - crypto::key_derivation pos_coin_derivation = AUTO_VAL_INIT(pos_coin_derivation); - bool r = crypto::generate_key_derivation(stake_tx_pub_key, stakeholder_account.get_keys().view_secret_key, pos_coin_derivation); // derivation(tx_pub; view_sec) + // calculate stake_out_derivation and secret_x (derived ephemeral secret key) + crypto::key_derivation stake_out_derivation = AUTO_VAL_INIT(stake_out_derivation); + r = crypto::generate_key_derivation(se.real_out_tx_key, stakeholder_keys.view_secret_key, stake_out_derivation); // d = 8 * v * R CHECK_AND_ASSERT_THROW_MES(r, "generate_key_derivation failed"); + crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x); + crypto::derive_secret_key(stake_out_derivation, se.real_output_in_tx_index, stakeholder_keys.spend_secret_key, secret_x); // x = Hs(8 * v * R, i) + s - crypto::secret_key derived_secret_ephemeral_key = AUTO_VAL_INIT(derived_secret_ephemeral_key); - crypto::derive_secret_key(pos_coin_derivation, stake_tx_out_index, stakeholder_account.get_keys().spend_secret_key, derived_secret_ephemeral_key); // derivation.derive(spend_sec, out_idx) => input ephemeral secret key + if (m_context.zarcanum) + { + // Zarcanum + zarcanum_sig& sig = boost::get(m_block.miner_tx.signatures[0]); + txin_zc_input& stake_input = boost::get(m_block.miner_tx.vin[1]); - // sign block actually in coinbase transaction - crypto::hash block_hash = currency::get_block_hash(m_block); - std::vector keys_ptrs(1, &stake_tx_out_pub_key); - crypto::generate_ring_signature(block_hash, m_stake_kernel.kimage, keys_ptrs, derived_secret_ephemeral_key, 0, &boost::get(m_block.miner_tx.signatures[0]).s[0]); + stake_input.k_image = m_context.sk.kimage; + + std::vector ring; + for(const auto& el : se.outputs) + { + stake_input.key_offsets.push_back(el.out_reference); + ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point); + } + r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets); + CHECK_AND_ASSERT_THROW_MES(r, "absolute_sorted_output_offsets_to_relative_in_place failed"); + + crypto::hash tx_hash_for_sig = get_transaction_hash(m_block.miner_tx); // TODO @#@# change to block hash after the corresponding test is made + + uint8_t err = 0; + r = crypto::zarcanum_generate_proof(tx_hash_for_sig, m_context.kernel_hash, ring, m_context.last_pow_block_id_hashed, m_context.sk.kimage, + secret_x, m_context.secret_q, se.real_output, m_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_blinding_mask, + static_cast(sig), &err); + CHECK_AND_ASSERT_THROW_MES(r, "zarcanum_generate_proof failed, err: " << (int)err); + } + else + { + // old PoS with non-hidden amounts + NLSAG_sig& sig = boost::get(m_block.miner_tx.signatures[0]); + txin_to_key& stake_input = boost::get(m_block.miner_tx.vin[1]); + + stake_input.k_image = m_context.sk.kimage; + stake_input.amount = m_context.stake_amount; + stake_input.key_offsets.push_back(m_pos_stake_output_gindex); + + crypto::hash block_hash = currency::get_block_hash(m_block); + std::vector keys_ptrs(1, &se.outputs.front().stealth_address); + sig.s.resize(1); + crypto::generate_ring_signature(block_hash, m_context.sk.kimage, keys_ptrs, secret_x, 0, sig.s.data()); + } m_step = 5; } -bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, - size_t current_block_size, - uint64_t fee, - uint64_t pos_stake_amount, - crypto::key_image pos_stake_keyimage, - size_t pos_stake_gindex, - const account_public_address &reward_receiving_address, - const account_public_address &stakeholder_address, - transaction& tx, - const blobdata& extra_nonce /*= blobdata()*/, - size_t max_outs /*= CURRENCY_MINER_TX_MAX_OUTS*/, - keypair tx_one_time_key /*= keypair::generate()*/) + +void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, + const currency::account_base& stakeholder_account) { - boost::value_initialized new_tx; - tx = new_tx; + CHECK_AND_ASSERT_THROW_MES(!m_context.zarcanum, "for zarcanum use another overloading"); - tx.version = TRANSACTION_VERSION_PRE_HF4; - set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + tx_source_entry se{}; - // calculate block reward - uint64_t block_reward; - bool r = get_block_reward(true, median_size, current_block_size, already_generated_coins, block_reward, height); - CHECK_AND_ASSERT_MES(r, false, "Block is too big"); - block_reward += fee; + se.real_out_tx_key = stake_tx_pub_key; + se.real_output_in_tx_index = stake_tx_out_index; + se.outputs.emplace_back(m_pos_stake_output_gindex, stake_tx_out_pub_key); - // decompose reward into outputs and populate tx.vout - std::vector out_amounts; - decompose_amount_into_digits(block_reward, DEFAULT_DUST_THRESHOLD, - [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, - [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); - - CHECK_AND_ASSERT_MES(2 <= max_outs, false, "max_out must be greather than 1"); - while (out_amounts.size() + 1 > max_outs) - { - out_amounts[out_amounts.size() - 2] += out_amounts.back(); - out_amounts.resize(out_amounts.size() - 1); - } - - // reward - bool burn_money = reward_receiving_address.spend_public_key == null_pkey && reward_receiving_address.view_public_key == null_pkey; // if true, burn reward, so no one on Earth can spend them - for (size_t output_index = 0; output_index < out_amounts.size(); ++output_index) - { - txout_to_key tk; - tk.key = null_pkey; // null means burn money - tk.mix_attr = 0; - - if (!burn_money) - { - r = currency::derive_public_key_from_target_address(reward_receiving_address, tx_one_time_key.sec, output_index, tk.key); // derivation(view_pub; tx_sec).derive(output_index, spend_pub) => output pub key - CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address"); - } - - tx_out_bare out; - out.amount = out_amounts[output_index]; - out.target = tk; - tx.vout.push_back(out); - } - - // stake - burn_money = stakeholder_address.spend_public_key == null_pkey && stakeholder_address.view_public_key == null_pkey; // if true, burn stake - { - txout_to_key tk; - tk.key = null_pkey; // null means burn money - tk.mix_attr = 0; - - if (!burn_money) - { - r = currency::derive_public_key_from_target_address(stakeholder_address, tx_one_time_key.sec, tx.vout.size(), tk.key); - CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address"); - } - - tx_out_bare out; - out.amount = pos_stake_amount; - out.target = tk; - tx.vout.push_back(out); - } - - // take care about extra - add_tx_pub_key_to_extra(tx, tx_one_time_key.pub); - if (extra_nonce.size()) - if (!add_tx_extra_userdata(tx, extra_nonce)) - return false; - - // populate ins with 1) money-generating and 2) PoS - txin_gen in; - in.height = height; - tx.vin.push_back(in); - - txin_to_key posin; - posin.amount = pos_stake_amount; - posin.key_offsets.push_back(pos_stake_gindex); - posin.k_image = pos_stake_keyimage; - tx.vin.push_back(posin); - //reserve place for ring signature - tx.signatures.resize(1); - boost::get(tx.signatures[0]).s.resize(posin.key_offsets.size()); - - return true; + step5_sign(se, stakeholder_account.get_keys()); } + bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc, std::function before_sign_cb, currency::block& output) { + blockchain_storage& bcs = c.get_blockchain_storage(); + // these values (median and diff) are correct only for the next main chain block, it's incorrect for altblocks, especially for old altblocks // but for now we assume they will work fine - uint64_t block_size_median = c.get_blockchain_storage().get_current_comulative_blocksize_limit() / 2; - currency::wide_difficulty_type difficulty = c.get_blockchain_storage().get_next_diff_conditional(true); + uint64_t block_size_median = bcs.get_current_comulative_blocksize_limit() / 2; + currency::wide_difficulty_type difficulty = bcs.get_next_diff_conditional(true); crypto::hash prev_id = get_block_hash(prev_block); size_t height = get_block_height(prev_block) + 1; block_extended_info bei = AUTO_VAL_INIT(bei); - bool r = c.get_blockchain_storage().get_block_extended_info_by_hash(prev_id, bei); + bool r = bcs.get_block_extended_info_by_hash(prev_id, bei); CHECK_AND_ASSERT_MES(r, false, "get_block_extended_info_by_hash failed for hash = " << prev_id); @@ -305,7 +300,7 @@ bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency:: crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(bcs.get_core_runtime_config().hard_forks, height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(block_size_median, bei.already_generated_coins, acc.get_public_address()); diff --git a/tests/core_tests/pos_block_builder.h b/tests/core_tests/pos_block_builder.h index 8ea965c2..d42c67e1 100644 --- a/tests/core_tests/pos_block_builder.h +++ b/tests/core_tests/pos_block_builder.h @@ -1,16 +1,20 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once -#pragma once +namespace currency +{ + struct hard_forks_descriptor; +} struct pos_block_builder { - pos_block_builder(); + pos_block_builder() = default; void clear(); - void step1_init_header(size_t block_height, crypto::hash& prev_block_hash); + void step1_init_header(const currency::hard_forks_descriptor& hardforks, size_t block_height, crypto::hash& prev_block_hash); void step2_set_txs(const std::vector& txs); @@ -23,6 +27,26 @@ struct pos_block_builder uint64_t timestamp_lower_bound, uint64_t timestamp_window = POS_SCAN_WINDOW, uint64_t timestamp_step = POS_SCAN_STEP); + + + void step3a( + currency::wide_difficulty_type difficulty, + const crypto::hash& last_pow_block_hash, + const crypto::hash& last_pos_block_kernel_hash + ); + + void step3b( + uint64_t stake_output_amount, + const crypto::key_image& stake_output_key_image, + const crypto::public_key& stake_source_tx_pub_key, + uint64_t stake_out_in_tx_index, + const crypto::scalar_t& stake_out_blinding_mask, + const crypto::secret_key& view_secret, + size_t stake_output_gindex, + uint64_t timestamp_lower_bound, + uint64_t timestamp_window, + uint64_t timestamp_step); + void step4_generate_coinbase_tx(size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, @@ -30,30 +54,38 @@ struct pos_block_builder const currency::account_public_address &stakeholder_address, const currency::blobdata& extra_nonce = currency::blobdata(), size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS, - const currency::extra_alias_entry& alias = currency::extra_alias_entry(), - currency::keypair tx_one_time_key = currency::keypair::generate()); + const currency::keypair* tx_one_time_key_to_use = nullptr + ); void step4_generate_coinbase_tx(size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, const currency::account_public_address &reward_and_stake_receiver_address, const currency::blobdata& extra_nonce = currency::blobdata(), size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS, - const currency::extra_alias_entry& alias = currency::extra_alias_entry(), - currency::keypair tx_one_time_key = currency::keypair::generate()); + const currency::keypair* tx_one_time_key_to_use = nullptr + ); + + void step5_sign(const currency::tx_source_entry& se, const currency::account_keys& stakeholder_keys); void step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account); - currency::block m_block; - size_t m_step; - size_t m_txs_total_size; - uint64_t m_total_fee; - currency::stake_kernel m_stake_kernel; - size_t m_height; - size_t m_pos_stake_output_gindex; - uint64_t m_pos_stake_amount; + //void step5_sign_zarcanum(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const currency::account_base& stakeholder_account); + + + currency::block m_block {}; + size_t m_step = 0; + size_t m_txs_total_size = 0; + uint64_t m_total_fee = 0; + //currency::stake_kernel m_stake_kernel {}; + size_t m_height = 0; + size_t m_pos_stake_output_gindex = 0; + //uint64_t m_pos_stake_amount = 0; + crypto::scalar_t m_blinding_masks_sum {}; // bliding masks of zc outputs of miner tx + + currency::pos_mining_context m_context {}; }; -bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, +/* bool construct_homemade_pos_miner_tx(bool zarcanum, size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t current_block_size, uint64_t fee, uint64_t pos_stake_amount, @@ -64,7 +96,7 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo currency::transaction& tx, const currency::blobdata& extra_nonce = currency::blobdata(), size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS, - currency::keypair tx_one_time_key = currency::keypair::generate()); + currency::keypair tx_one_time_key = currency::keypair::generate()); */ bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc, std::function before_sign_cb, currency::block& output); diff --git a/tests/core_tests/pos_validation.cpp b/tests/core_tests/pos_validation.cpp index c4b1ff34..2c7ef529 100644 --- a/tests/core_tests/pos_validation.cpp +++ b/tests/core_tests/pos_validation.cpp @@ -79,7 +79,7 @@ bool gen_pos_coinstake_already_spent::generate(std::vector& ev crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), alice.get_public_address()); @@ -130,7 +130,7 @@ bool gen_pos_incorrect_timestamp::generate(std::vector& events crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); // use incorrect timestamp_step pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp - 1, POS_SCAN_WINDOW, POS_SCAN_STEP - 1); @@ -148,7 +148,7 @@ bool gen_pos_incorrect_timestamp::generate(std::vector& events // Now try PoS timestamp window boundaries. pb.clear(); - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); // move timestamp to the future pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, ts + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT + 1, POS_SCAN_WINDOW, POS_SCAN_STEP); @@ -162,7 +162,7 @@ bool gen_pos_incorrect_timestamp::generate(std::vector& events // lower limit pb.clear(); - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); // move timestamp to the future pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, genesis_ts - POS_SCAN_WINDOW, POS_SCAN_WINDOW, POS_SCAN_STEP); @@ -247,17 +247,17 @@ bool gen_pos_extra_nonce::generate(std::vector& events) const crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp); // use biggest possible extra nonce (255 bytes) + largest alias - currency::blobdata extra_none(255, 'x'); - currency::extra_alias_entry alias = AUTO_VAL_INIT(alias); - alias.m_alias = std::string(255, 'a'); - alias.m_address = miner.get_keys().account_address; - alias.m_text_comment = std::string(255, 'y'); - pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), alice.get_public_address(), extra_none, CURRENCY_MINER_TX_MAX_OUTS, alias); + currency::blobdata extra_nonce(255, 'x'); + //currency::extra_alias_entry alias = AUTO_VAL_INIT(alias); // TODO: this alias entry was ignored for a long time, now I commented it out, make sure it's okay -- sowle + //alias.m_alias = std::string(255, 'a'); + //alias.m_address = miner.get_keys().account_address; + //alias.m_text_comment = std::string(255, 'y'); + pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), alice.get_public_address(), extra_nonce, CURRENCY_MINER_TX_MAX_OUTS); pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, miner); block blk_1 = pb.m_block; @@ -301,7 +301,7 @@ bool gen_pos_min_allowed_height::generate(std::vector& events) crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector(1, tx_1)); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), alice.get_public_address()); @@ -353,7 +353,7 @@ bool gen_pos_invalid_coinbase::generate(std::vector& events) c crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; pos_block_builder pb; - pb.step1_init_header(height, prev_id); + pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), alice_acc.get_public_address()); diff --git a/tests/core_tests/tx_builder.h b/tests/core_tests/tx_builder.h index 17828e5b..22ff3f5c 100644 --- a/tests/core_tests/tx_builder.h +++ b/tests/core_tests/tx_builder.h @@ -37,7 +37,7 @@ struct tx_builder for(const currency::tx_source_entry::output_entry& out_entry : src_entr.outputs) input_to_key.key_offsets.push_back(out_entry.out_reference); - input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets); + input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets); // TODO @#@# m_tx.vin.push_back(input_to_key); } } diff --git a/tests/core_tests/wallet_test_core_proxy.cpp b/tests/core_tests/wallet_test_core_proxy.cpp index ee9d1363..e303bc8c 100644 --- a/tests/core_tests/wallet_test_core_proxy.cpp +++ b/tests/core_tests/wallet_test_core_proxy.cpp @@ -32,9 +32,16 @@ bool wallet_test_core_proxy::update_blockchain(const std::vectorb.tx_hashes.begin(), b->b.tx_hashes.end(), [&confirmed_txs](const crypto::hash& h) { confirmed_txs.insert(h); }); - for (auto e : events) + size_t invalid_tx_index = UINT64_MAX; + for (size_t i = 0; i < events.size(); ++i) { - if (e.type() != typeid(currency::transaction)) + const test_event_entry& e = events[i]; + if (test_chain_unit_enchanced::is_event_mark_invalid_tx(e)) + { + invalid_tx_index = i + 1; + continue; + } + if (e.type() != typeid(currency::transaction) || i == invalid_tx_index) continue; const currency::transaction& tx = boost::get(e); diff --git a/tests/core_tests/wallet_tests_basic.cpp b/tests/core_tests/wallet_tests_basic.cpp index 6d3c9292..c4cca86d 100644 --- a/tests/core_tests/wallet_tests_basic.cpp +++ b/tests/core_tests/wallet_tests_basic.cpp @@ -49,7 +49,7 @@ bool wallet_test::check_balance_via_build_wallets(currency::core& c, size_t ev_i r = generator.build_wallets(get_block_hash(*top_block), accounts, w, c.get_blockchain_storage().get_core_runtime_config()); CHECK_AND_ASSERT_MES(r && w.size() == 1 && w[0].wallet != 0, false, "check_balance: failed to build wallets"); - if (!check_balance_via_wallet(*w[0].wallet, epee::string_tools::num_to_string_fast(pcb.account_index).c_str(), pcb.total_balance, pcb.mined_balance, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out)) + if (!check_balance_via_wallet(*w[0].wallet, get_test_account_name_by_id(pcb.account_index).c_str(), pcb.total_balance, pcb.mined_balance, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out)) return false; return true; @@ -67,12 +67,25 @@ bool wallet_test::check_balance(currency::core& c, size_t ev_index, const std::v bool has_aliases = false; w->scan_tx_pool(has_aliases); - if (!check_balance_via_wallet(*w.get(), epee::string_tools::num_to_string_fast(pcb.account_index).c_str(), pcb.total_balance, pcb.mined_balance, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out)) + if (!check_balance_via_wallet(*w.get(), get_test_account_name_by_id(pcb.account_index).c_str(), pcb.total_balance, pcb.mined_balance, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out)) return false; return true; } + std::string wallet_test::get_test_account_name_by_id(size_t acc_id) + { + switch(acc_id) + { + case MINER_ACC_IDX: return "miner"; + case ALICE_ACC_IDX: return "Alice"; + case BOB_ACC_IDX: return "Bob"; + case CAROL_ACC_IDX: return "Carol"; + case DAN_ACC_IDX: return "Dan"; + default: return "unknown"; + } + } + std::shared_ptr wallet_test::init_playtime_test_wallet(const std::vector& events, currency::core& c, const account_base& acc) const { CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning"); diff --git a/tests/core_tests/wallet_tests_basic.h b/tests/core_tests/wallet_tests_basic.h index b23f19a5..425c81f6 100644 --- a/tests/core_tests/wallet_tests_basic.h +++ b/tests/core_tests/wallet_tests_basic.h @@ -17,6 +17,8 @@ struct wallet_test : virtual public test_chain_unit_enchanced bool check_balance_via_build_wallets(currency::core& c, size_t ev_index, const std::vector& events); bool check_balance(currency::core& c, size_t ev_index, const std::vector& events); + static std::string get_test_account_name_by_id(size_t acc_id); + protected: struct params_check_balance diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index ffad0c0b..15bf75ba 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,6 +9,7 @@ #include "random_helper.h" #include "tx_builder.h" +#include "pos_block_builder.h" #define AMOUNT_TO_TRANSFER_ZARCANUM_BASIC (TESTS_DEFAULT_FEE*10) @@ -17,6 +18,7 @@ using namespace currency; //------------------------------------------------------------------------------ + zarcanum_basic_test::zarcanum_basic_test() { REGISTER_CALLBACK_METHOD(zarcanum_basic_test, configure_core); @@ -30,13 +32,15 @@ zarcanum_basic_test::zarcanum_basic_test() bool zarcanum_basic_test::generate(std::vector& events) const { - m_accounts.resize(MINER_ACC_IDX+1); - account_base& miner_acc = m_accounts[MINER_ACC_IDX]; - miner_acc.generate(); + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + //account_base& carol_acc = m_accounts[CAROL_ACC_IDX]; carol_acc.generate(); carol_acc.set_createtime(ts); - MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks - set_hard_fork_heights_to_generator(generator); //TODO: Need to make sure REWIND_BLOCKS_N and other coretests codebase are capable of following hardfork4 rules //in this test hardfork4 moment moved to runtime section REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3); @@ -50,58 +54,46 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect { bool r = false; std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); - account_base alice_acc; - alice_acc.generate(); - std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, alice_acc); - - - //pass over hardfork + // check passing over the hardfork + CHECK_AND_ASSERT_MES(!c.get_blockchain_storage().is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM), false, "ZANO_HARDFORK_04_ZARCANUM is active"); r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 2); CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); - + CHECK_AND_ASSERT_MES(c.get_blockchain_storage().is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM), false, "ZANO_HARDFORK_04_ZARCANUM is not active"); miner_wlt->refresh(); alice_wlt->refresh(); - CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool"); //create transfer from pre-zarcanum inputs to post-zarcanum inputs uint64_t transfer_amount = AMOUNT_TO_TRANSFER_ZARCANUM_BASIC + TESTS_DEFAULT_FEE; - miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address()); - LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0); - miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address()); - LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0); - miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address()); - LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0); - miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address()); - LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0); + const size_t batches_to_Alice_count = 4; + for(size_t i = 0; i < batches_to_Alice_count; ++i) + { + miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address()); + LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << print_money_brief(transfer_amount), LOG_LEVEL_0); + } - - CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 4, false, "Incorrect txs count in the pool"); + CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == batches_to_Alice_count, false, "Incorrect txs count in the pool"); r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); - //miner_wlt->refresh(); + CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool"); + alice_wlt->refresh(); - //uint64_t unlocked = 0; - //uint64_t balance = alice_wlt->balance(unlocked); - CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", transfer_amount * 4, UINT64_MAX, transfer_amount * 4), false, ""); - - account_base bob_acc; - bob_acc.generate(); - std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, bob_acc); - + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", transfer_amount * batches_to_Alice_count, UINT64_MAX, transfer_amount * batches_to_Alice_count), false, ""); //create transfer from post-zarcanum inputs to post-zarcanum inputs uint64_t transfer_amount2 = AMOUNT_TO_TRANSFER_ZARCANUM_BASIC; - alice_wlt->transfer(transfer_amount2, bob_acc.get_public_address()); - LOG_PRINT_MAGENTA("Zarcanum-2-zarcanum transaction sent from Alice to Bob " << transfer_amount2, LOG_LEVEL_0); + alice_wlt->transfer(transfer_amount2, m_accounts[BOB_ACC_IDX].get_public_address()); + LOG_PRINT_MAGENTA("Zarcanum-2-zarcanum transaction sent from Alice to Bob " << print_money_brief(transfer_amount2), LOG_LEVEL_0); CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool"); @@ -110,8 +102,6 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); bob_wlt->refresh(); - //balance = bob_wlt->balance(unlocked); - //CHECK_AND_ASSERT_MES(unlocked == transfer_amount2, false, "wrong amount"); CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", transfer_amount2, UINT64_MAX, transfer_amount2), false, ""); account_base staker_benefeciary_acc; @@ -122,27 +112,36 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect miner_benefeciary_acc.generate(); std::shared_ptr miner_benefeciary_acc_wlt = init_playtime_test_wallet(events, c, miner_benefeciary_acc); - alice_wlt->refresh(); - size_t pos_entries_count = 0; //do staking - for(size_t i = 0; i != CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4; i++) + for(size_t i = 0; i < batches_to_Alice_count - 1; i++) { + alice_wlt->refresh(); r = mine_next_pos_block_in_playtime_with_wallet(*alice_wlt.get(), staker_benefeciary_acc_wlt->get_account().get_public_address(), pos_entries_count); - CHECK_AND_ASSERT_MES(r, false, "mine_next_pos_block_in_playtime_with_wallet failed"); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pos_block_in_playtime_with_wallet failed, pos_entries_count = " << pos_entries_count); r = mine_next_pow_block_in_playtime(miner_benefeciary_acc.get_public_address(), c); CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); } + // make sure all mined coins in staker_benefeciary_acc_wlt and miner_benefeciary_acc_wlt are now spendable + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + //attempt to spend staked and mined coinbase outs staker_benefeciary_acc_wlt->refresh(); miner_benefeciary_acc_wlt->refresh(); - staker_benefeciary_acc_wlt->transfer(transfer_amount2, bob_acc.get_public_address()); + uint64_t mined_amount = (batches_to_Alice_count - 1) * COIN; + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*staker_benefeciary_acc_wlt, "staker_benefeciary", mined_amount, mined_amount, mined_amount), false, ""); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_benefeciary_acc_wlt, "miner_benefeciary", mined_amount, mined_amount, mined_amount), false, ""); + + + staker_benefeciary_acc_wlt->transfer(transfer_amount2, bob_wlt->get_account().get_public_address()); LOG_PRINT_MAGENTA("Zarcanum(pos-coinbase)-2-zarcanum transaction sent from Staker to Bob " << print_money_brief(transfer_amount2), LOG_LEVEL_0); - miner_benefeciary_acc_wlt->transfer(transfer_amount2, bob_acc.get_public_address()); + miner_benefeciary_acc_wlt->transfer(transfer_amount2, bob_wlt->get_account().get_public_address()); LOG_PRINT_MAGENTA("Zarcanum(pow-coinbase)-2-zarcanum transaction sent from Staker to Bob " << print_money_brief(transfer_amount2), LOG_LEVEL_0); CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 2, false, "Incorrect txs count in the pool"); @@ -154,13 +153,14 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect bob_wlt->refresh(); - //balance = bob_wlt->balance(unlocked); - //CHECK_AND_ASSERT_MES(unlocked == transfer_amount2*3, false, "wrong amount"); CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", transfer_amount2*3, UINT64_MAX, transfer_amount2*3), false, ""); //try to make pre-zarcanum block after hardfork 4 currency::core_runtime_config rc = alice_wlt->get_core_runtime_config(); - rc.hard_forks.set_hardfork_height(4, ZANO_HARDFORK_04_AFTER_HEIGHT); + rc.hard_forks.set_hardfork_height(4, ZANO_HARDFORK_04_AFTER_HEIGHT); // <-- TODO: this won't help to build pre-hardfork block, + // because blocktemplate is created by the core. + // (wallet2::build_minted_block will fail instead) + // We need to use pos block builder or smth -- sowle alice_wlt->set_core_runtime_config(rc); r = mine_next_pos_block_in_playtime_with_wallet(*alice_wlt.get(), staker_benefeciary_acc_wlt->get_account().get_public_address(), pos_entries_count); CHECK_AND_ASSERT_MES(!r, false, "Pre-zarcanum block accepted in post-zarcanum era"); @@ -169,7 +169,7 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect return true; } - +//------------------------------------------------------------------------------ zarcanum_test_n_inputs_validation::zarcanum_test_n_inputs_validation() { @@ -226,4 +226,305 @@ bool zarcanum_test_n_inputs_validation::generate(std::vector& REWIND_BLOCKS_N(events, blk_16, blk_14, miner_account, 2); return true; -} \ No newline at end of file +} + +//------------------------------------------------------------------------------ + +zarcanum_gen_time_balance::zarcanum_gen_time_balance() +{ + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 1); +} + +bool zarcanum_gen_time_balance::generate(std::vector& events) const +{ + // Test idea: make sure post HF4 transactions with ZC inputs and outputs are handled properly by chaingen gen-time routines + // (including balance check) + + bool r = false; + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3); + + std::vector sources; + std::vector destinations; + + // + // tx_0: alice_amount from miner to Alice + uint64_t alice_amount = MK_TEST_COINS(99); + CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_0r, miner_acc, alice_acc, alice_amount, TESTS_DEFAULT_FEE, 0, sources, destinations), false, ""); + + std::vector extra; + transaction tx_0 = AUTO_VAL_INIT(tx_0); + crypto::secret_key tx_sec_key; + r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_0, get_tx_version_from_events(events), tx_sec_key, 0); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + ADD_CUSTOM_EVENT(events, tx_0); + + // add tx_0 to the block + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); + + // rewind and check unlocked balance + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, alice_amount, alice_amount, 0, 0, 0)); + + // do a gen-time balance check + CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0); + REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_1r, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4); + CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, alice_amount); + + // + // tx_1: bob_amount, Alice -> Bob + uint64_t bob_amount = MK_TEST_COINS(15); + + MAKE_TX(events, tx_1, alice_acc, bob_acc, bob_amount, blk_1r); + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1); + + // check Bob's balance in play time... + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(BOB_ACC_IDX, bob_amount, 0, 0, 0, 0)); + + // ... and in gen time + CREATE_TEST_WALLET(bob_wlt, bob_acc, blk_0); + REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_2, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5); + CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(bob_wlt, bob_amount); + + // try to construct tx with only one output (that is wrong for HF4) + test_gentime_settings_restorer tgsr; + test_gentime_settings tgs = test_generator::get_test_gentime_settings(); + tgs.split_strategy = tests_void_split_strategy; // amount won't be splitted by test_genertor::construct_tx() + test_generator::set_test_gentime_settings(tgs); + + DO_CALLBACK(events, "mark_invalid_tx"); + // transfer all Bob's coins -- they should be put in one out + MAKE_TX(events, tx_2_bad, bob_acc, alice_acc, bob_amount - TESTS_DEFAULT_FEE, blk_2); + CHECK_AND_ASSERT_MES(tx_2_bad.vout.size() == 1, false, "tx_2_bad.vout.size() = " << tx_2_bad.vout.size()); + + DO_CALLBACK(events, "mark_invalid_block"); + MAKE_NEXT_BLOCK_TX1(events, blk_3_bad, blk_2, miner_acc, tx_2_bad); + + // now change split strategy and make a tx with more outputs + tgs.split_strategy = tests_random_split_strategy; + test_generator::set_test_gentime_settings(tgs); + + size_t nmix = 7; + MAKE_TX_FEE_MIX(events, tx_2, bob_acc, alice_acc, bob_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, blk_2); + CHECK_AND_ASSERT_MES(tx_2.vout.size() != 1, false, "tx_2.vout.size() = " << tx_2.vout.size()); + MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_2); + + REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_3, 2); + CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, alice_amount - 2 * TESTS_DEFAULT_FEE); + + REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_3, 1); + CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(bob_wlt, 0); + + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, alice_amount - 2 * TESTS_DEFAULT_FEE, 0, 0, 0, 0)); + + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(BOB_ACC_IDX, 0, 0, 0, 0, 0)); + + return true; +} + +//------------------------------------------------------------------------------ + +zarcanum_pos_block_math::zarcanum_pos_block_math() +{ + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 0); +} + +bool zarcanum_pos_block_math::generate(std::vector& events) const +{ + bool r = false; + + GENERATE_ACCOUNT(miner_acc); + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks + MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc); + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + //generator.get_tx_out_gindex + + // try to make a PoS block with locked stake after the hardfork + + block blk_1_pos; + { + const block& prev_block = blk_1r; + const transaction& stake_tx = blk_1.miner_tx; + const account_base& stake_acc = miner_acc; + size_t stake_out_in_tx_index = 0; + size_t stake_output_gindex = 0; + + crypto::hash prev_id = get_block_hash(prev_block); + size_t height = get_block_height(prev_block) + 1; + currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false); + crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake_tx); + + pos_block_builder pb; + pb.step1_init_header(generator.get_hardforks(), height, prev_id); + pb.step2_set_txs(std::vector()); + + pb.step3a(diff, prev_id, null_hash); + + crypto::key_derivation derivation {}; + crypto::scalar_t stake_out_blinding_mask {}; + uint64_t stake_output_amount = 0; + crypto::public_key stealth_address {}; + crypto::secret_key secret_x {}; + crypto::key_image stake_output_key_image {}; + + r = generate_key_derivation(stake_tx_pub_key, stake_acc.get_keys().view_secret_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed"); + r = is_out_to_acc(stake_acc.get_public_address(), boost::get(stake_tx.vout[stake_out_in_tx_index]), derivation, stake_out_in_tx_index, stake_output_amount, stake_out_blinding_mask); + CHECK_AND_ASSERT_MES(r, false, "is_out_to_acc failed"); + r = crypto::derive_public_key(derivation, stake_out_in_tx_index, stake_acc.get_public_address().spend_public_key, stealth_address); + CHECK_AND_ASSERT_MES(r, false, "derive_public_key failed"); + crypto::derive_secret_key(derivation, stake_out_in_tx_index, stake_acc.get_keys().spend_secret_key, secret_x); + crypto::generate_key_image(stealth_address, secret_x, stake_output_key_image); + + + pb.step3b(stake_output_amount, stake_output_key_image, stake_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, stake_acc.get_keys().view_secret_key, + stake_output_gindex, prev_block.timestamp, POS_SCAN_WINDOW, POS_SCAN_STEP); + + pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), stake_acc.get_public_address()); + + pb.step5_sign(stake_tx_pub_key, stake_out_in_tx_index, stake_tx_pub_key, stake_acc); + + blk_1_pos = pb.m_block; + } + + ADD_CUSTOM_EVENT(events, blk_1_pos); + + return true; +} + +//------------------------------------------------------------------------------ + +zarcanum_txs_with_big_shuffled_decoy_set_shuffled::zarcanum_txs_with_big_shuffled_decoy_set_shuffled() +{ + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 24); +} + +bool zarcanum_txs_with_big_shuffled_decoy_set_shuffled::generate(std::vector& events) const +{ + // Test idea: make few txs with a big decoy set, each time shuffling the sources (decoy set) before constructing a tx => to make sure real_output can be any. + // Then do the same after HF4. + + bool r = false; + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks + + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + uint64_t alice_amount = CURRENCY_BLOCK_REWARD; + MAKE_TX(events, tx_0_a, miner_acc, alice_acc, alice_amount, blk_0r); + MAKE_TX(events, tx_0_b, miner_acc, alice_acc, alice_amount, blk_0r); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list({ tx_0_a, tx_0_b })); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // do a gen-time balance check + CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0); + REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_1r, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); + CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, 2 * alice_amount); + + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 2 * alice_amount, 2 * alice_amount, 0, 0, 0)); + + std::vector sources; + std::vector destinations; + + // tx_1: Alice -> miner with big decoy set + size_t nmix = 10; + CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_1r, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, ""); + + // randomly source entries (real_output will be changed accordingly) + CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed"); + + transaction tx_1_a{}; + r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_1_a, get_tx_version_from_events(events), 0 /* unlock time */); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + ADD_CUSTOM_EVENT(events, tx_1_a); + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1_a); + + // tx_2 (the same as tx_1): Alice -> miner, big decoy set, all coins are back now + sources.clear(); + destinations.clear(); + nmix = 10; + CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_2, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, ""); + CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed"); + transaction tx_1_b{}; + r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_1_b, get_tx_version_from_events(events), 0 /* unlock time */); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + ADD_CUSTOM_EVENT(events, tx_1_b); + MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_1_b); + + // make sure Alice has no coins left + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 0, 0, 0, 0, 0)); + + + // + // now do the same after HF4 + // + + + // make sure the hardfork goes well + DO_CALLBACK_PARAMS(events, "check_hardfork_inactive", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_acc); + DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + // tx_3_a, tx_3_b: miner -> Alice, alice_amount x 2 + MAKE_TX(events, tx_2_a, miner_acc, alice_acc, alice_amount, blk_4); + MAKE_TX(events, tx_2_b, miner_acc, alice_acc, alice_amount, blk_4); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_5, blk_4, miner_acc, std::list({ tx_2_a, tx_2_b })); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_5r, blk_5, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // do a gen-time balance check + REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_5r, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4); + CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, 2 * alice_amount); + + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 2 * alice_amount, 2 * alice_amount, 0, 0, 0)); + + // tx_3_a: Alice -> miner, alice_amount - TESTS_DEFAULT_FEE, big decoy set + sources.clear(); + destinations.clear(); + nmix = 10; + CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_5r, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, ""); + CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed"); + transaction tx_3_a{}; + r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_3_a, get_tx_version_from_events(events), 0 /* unlock time */); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + ADD_CUSTOM_EVENT(events, tx_3_a); + MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_acc, tx_3_a); + + // tx_3_b: Alice -> miner, alice_amount - TESTS_DEFAULT_FEE, big decoy set + sources.clear(); + destinations.clear(); + nmix = 10; + CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_6, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, ""); + CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed"); + transaction tx_3_b{}; + r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_3_b, get_tx_version_from_events(events), 0 /* unlock time */); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + ADD_CUSTOM_EVENT(events, tx_3_b); + MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6, miner_acc, tx_3_b); + + // make sure Alice has no coins left + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 0, 0, 0, 0, 0)); + + return true; +} + diff --git a/tests/core_tests/zarcanum_test.h b/tests/core_tests/zarcanum_test.h index 7a40dacf..6ecefca1 100644 --- a/tests/core_tests/zarcanum_test.h +++ b/tests/core_tests/zarcanum_test.h @@ -1,8 +1,7 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2022 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #pragma once #include "chaingen.h" #include "wallet_tests_basic.h" @@ -21,4 +20,22 @@ struct zarcanum_test_n_inputs_validation : public wallet_test bool generate(std::vector& events) const; }; +struct zarcanum_gen_time_balance : public wallet_test +{ + zarcanum_gen_time_balance(); + bool generate(std::vector& events) const; +}; + +struct zarcanum_pos_block_math : public test_chain_unit_enchanced +{ + zarcanum_pos_block_math(); + bool generate(std::vector& events) const; +}; + +struct zarcanum_txs_with_big_shuffled_decoy_set_shuffled : public wallet_test +{ + zarcanum_txs_with_big_shuffled_decoy_set_shuffled(); + bool generate(std::vector& events) const; +}; + diff --git a/tests/functional_tests/core_concurrency_test.cpp b/tests/functional_tests/core_concurrency_test.cpp index e689e0bf..f1133910 100644 --- a/tests/functional_tests/core_concurrency_test.cpp +++ b/tests/functional_tests/core_concurrency_test.cpp @@ -109,7 +109,7 @@ bool generate_events(currency::core& c, cct_events_t& events, const cct_wallets_ wide_difficulty_type diff = 0; if (prev_block.height != 0) test_core_time::adjust(prev_block.bl.timestamp + DIFFICULTY_POW_TARGET); - r = bcs.create_block_template(b, miner_addr, diff, height, ex_nonce); + r = bcs.create_block_template(miner_addr, ex_nonce, b, diff, height); CHECK_AND_ASSERT_MES(r, false, "create_block_template failed"); } else diff --git a/tests/functional_tests/crypto_tests_range_proofs.h b/tests/functional_tests/crypto_tests_range_proofs.h index 5332ef54..6be87120 100644 --- a/tests/functional_tests/crypto_tests_range_proofs.h +++ b/tests/functional_tests/crypto_tests_range_proofs.h @@ -288,5 +288,98 @@ TEST(bppe, two) ASSERT_TRUE(r); + return true; +} + +TEST(bppe, power_128) +{ + std::vector signatures_vector; + signatures_vector.reserve(200); + std::vector> commitments_vector; + commitments_vector.reserve(200); + + std::vector sigs; + uint8_t err = 0; + bool r = false; + + auto gen_rp_for_vec = [&](const scalar_vec_t& values) + { + signatures_vector.resize(signatures_vector.size() + 1); + bppe_signature &bppe_sig = signatures_vector.back(); + commitments_vector.resize(commitments_vector.size() + 1); + std::vector& commitments = commitments_vector.back(); + + scalar_vec_t masks, masks2; + for(auto& el: values) + { + masks.emplace_back(scalar_t::random()); + masks2.emplace_back(scalar_t::random()); + } + + r = bppe_gen>(values, masks, masks2, bppe_sig, commitments, &err); + ASSERT_TRUE(r); + sigs.emplace_back(bppe_sig, commitments); + return true; + }; + + auto gen_rp_for_value = [&](const scalar_t& v) { return gen_rp_for_vec(scalar_vec_t{ v }); }; + + const scalar_t s_128_max = scalar_t(UINT64_MAX, UINT64_MAX, 0, 0); + + LOG_PRINT_L0("1"); + ASSERT_TRUE(gen_rp_for_value(s_128_max)); + ASSERT_TRUE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + + LOG_PRINT_L0("2"); + ASSERT_TRUE(gen_rp_for_value(scalar_t(crypto::rand(), crypto::rand(), 0, 0))); + ASSERT_TRUE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + + LOG_PRINT_L0("3"); + ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 1, 0))); + ASSERT_FALSE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + + LOG_PRINT_L0("4"); + ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, crypto::rand(), 0))); + ASSERT_FALSE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + + LOG_PRINT_L0("5"); + ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 0, crypto::rand()))); + ASSERT_FALSE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + + LOG_PRINT_L0("6"); + ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 0, UINT64_MAX))); + ASSERT_FALSE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + + LOG_PRINT_L0("7"); + ASSERT_TRUE(gen_rp_for_vec(scalar_vec_t{s_128_max, s_128_max, s_128_max, s_128_max})); + LOG_PRINT_L0("simple generated"); + ASSERT_TRUE(bppe_verify>(sigs, &err)); + LOG_PRINT_L0("simple verified"); + for(size_t i = 0; i < 16; ++i) + { + LOG_PRINT_L0(" #" << i << " simple generated"); + scalar_vec_t vec; + for(size_t j = 0, n = crypto::rand() % 4 + 1; j < n; ++j) + vec.emplace_back(scalar_t(crypto::rand(), crypto::rand(), 0, 0)); + ASSERT_TRUE(gen_rp_for_vec(vec)); + } + LOG_PRINT_L0("verification started"); + ASSERT_TRUE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + LOG_PRINT_L0("verification finished" << ENDL); + + LOG_PRINT_L0("8"); + ASSERT_TRUE(gen_rp_for_value(s_128_max)); + ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 0, UINT64_MAX))); + ASSERT_TRUE(gen_rp_for_value(s_128_max)); + ASSERT_FALSE(bppe_verify>(sigs, &err)); + signatures_vector.clear(), commitments_vector.clear(), sigs.clear(); + return true; } diff --git a/tests/unit_tests/basic_struct_packing.cpp b/tests/unit_tests/basic_struct_packing.cpp index 54521528..7d75ff8d 100644 --- a/tests/unit_tests/basic_struct_packing.cpp +++ b/tests/unit_tests/basic_struct_packing.cpp @@ -84,9 +84,10 @@ TEST(tx_signatures_packing, 1) { // empty NLSAG - // v(0) + (1 + v(0) + 0 * 2 * 32) = 3 + // v(1) + (1 + v(0) + 0 * 2 * 32) = 3 sigs.clear(); sigs.emplace_back(std::move(NLSAG_sig())); + ASSERT_EQ(3, t_serializable_object_to_blob(sigs).size()); ASSERT_EQ(3, get_object_blobsize(sigs)); } @@ -96,14 +97,28 @@ TEST(tx_signatures_packing, 1) sigs.clear(); for(size_t i = 0; i < 128; ++i) sigs.emplace_back(std::move(NLSAG_sig())); + ASSERT_EQ(258, t_serializable_object_to_blob(sigs).size()); ASSERT_EQ(258, get_object_blobsize(sigs)); } + { + // 128 10-ring NLSAGs + // v(128) + 128 * (1 + v(10) + 10 * 2 * 32) = 82178 + sigs.clear(); + NLSAG_sig nlsag = AUTO_VAL_INIT(nlsag); + nlsag.s.resize(10); + for(size_t i = 0; i < 128; ++i) + sigs.push_back(nlsag); + ASSERT_EQ(82178, t_serializable_object_to_blob(sigs).size()); + ASSERT_EQ(82178, get_object_blobsize(sigs)); + } + { // empty ZC_sig - // v(0) + (1 + 32 + 32 + (1 + 10*32) + 32) = 99 + // v(1) + (1 + 32 + 32 + (1 + 10*32) + 32) = 99 sigs.clear(); sigs.emplace_back(std::move(ZC_sig())); + ASSERT_EQ(99, t_serializable_object_to_blob(sigs).size()); ASSERT_EQ(99, get_object_blobsize(sigs)); } @@ -113,6 +128,7 @@ TEST(tx_signatures_packing, 1) sigs.clear(); for(size_t i = 0; i < 128; ++i) sigs.emplace_back(std::move(ZC_sig())); + ASSERT_EQ(12546, t_serializable_object_to_blob(sigs).size()); ASSERT_EQ(12546, get_object_blobsize(sigs)); } @@ -124,6 +140,7 @@ TEST(tx_signatures_packing, 1) sigs.clear(); for(size_t i = 0; i < 128; ++i) sigs.emplace_back(zc); + ASSERT_EQ(53506, t_serializable_object_to_blob(sigs).size()); ASSERT_EQ(53506, get_object_blobsize(sigs)); } }