diff --git a/README.md b/README.md index 13558e5f..4d5ca173 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,14 @@ Be sure to clone the repository properly:\ ### Dependencies | component / version | minimum
(not recommended but may work) | recommended | most recent of what we have ever tested | |--|--|--|--| -| gcc (Linux) | 5.4.0 | 7.5.0 | 8.3.0 | +| gcc (Linux) | 7.5.0 | 7.5.0 | 8.3.0 | | llvm/clang (Linux) | UNKNOWN | 7.0.1 | 8.0.0 | -| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2015 (14.0 update 1) | 2017 (15.9.0) | 2022 (17.4.2) | -| [XCode](https://developer.apple.com/downloads/) (macOS) | 9.2 | 12.3 | 12.3 | -| [CMake](https://cmake.org/download/) | 2.8.6 | 3.15.5 | 3.26.3 | +| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2017 (15.9.30) | 2017 (15.9.30) | 2022 (17.6.1) | +| [XCode](https://developer.apple.com/downloads/) (macOS) | 12.3 | 14.3 | 14.3 | +| [CMake](https://cmake.org/download/) | 3.1.6 | 3.15.5 | 3.26.3 | | [Boost](https://www.boost.org/users/download/) | 1.70 | 1.70 | 1.76 | | [OpenSSL](https://www.openssl.org/source/) [(win)](https://slproweb.com/products/Win32OpenSSL.html) | - | 1.1.1n | 1.1.1n | -| [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.15.2 | +| [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.12.12 | 5.15.2 | Note:\ [*server version*] denotes steps required for building command-line tools (daemon, simplewallet, etc.).\ diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index 95cbbc43..238bc300 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -56,7 +56,7 @@ namespace crypto }; - template + template struct bpp_crypto_trait_zano : gen_trait_t { static constexpr size_t c_bpp_n = N; // the upper bound for the witness's range @@ -134,7 +134,7 @@ namespace crypto }; // struct bpp_crypto_trait_zano - typedef bpp_crypto_trait_zano bpp_crypto_trait_ZC_out; + typedef bpp_crypto_trait_zano bpp_crypto_trait_ZC_out; typedef bpp_crypto_trait_zano bpp_crypto_trait_Zarcanum; diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 4419e105..9a8298e4 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -4503,11 +4503,12 @@ bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_hav CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has a non-explicit asset id"); } } - else // otherwise all outputs must have hidden asset id + else // otherwise all outputs must have hidden asset id (unless they burn money by sending them to null pubkey) { for(size_t j = 0, k = tx.vout.size(); j < k; ++j) { - r = crypto::point_t(boost::get(tx.vout[j]).blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id; + const tx_out_zarcanum& zo = boost::get(tx.vout[j]); + r = zo.stealth_address == null_pkey || crypto::point_t(zo.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id; CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has an explicit asset id"); } } @@ -4705,7 +4706,8 @@ struct outputs_visitor { if (!m_bch.is_tx_spendtime_unlocked(source_out_unlock_time)) { - LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(source_tx, out_i)); + uint64_t limit = source_out_unlock_time < CURRENCY_MAX_BLOCK_NUMBER ? m_bch.get_current_blockchain_size() - 1 + CURRENCY_LOCKED_TX_ALLOWED_DELTA_BLOCKS : m_bch.get_core_runtime_config().get_core_time() + CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS; + LOG_PRINT_L0("An output has unlock time value of " << get_tx_unlock_time(source_tx, out_i) << " while the current limit is " << limit); return false; } } diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index b9dde3d9..758b9b43 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -829,7 +829,8 @@ namespace currency if (!vis.handle_output(tx_ptr->tx, validated_tx, o, n)) { - LOG_PRINT_RED_L0("handle_output failed for output #" << n << " in " << tx_id); + size_t verified_input_index = std::find(validated_tx.vin.begin(), validated_tx.vin.end(), verified_input) - validated_tx.vin.begin(); + LOG_PRINT_RED_L0("handle_output failed for output #" << n << " in " << tx_id << " referenced by input #" << verified_input_index << " in tx " << get_transaction_hash(validated_tx)); return false; } TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output); @@ -844,7 +845,8 @@ namespace currency TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output); if (!vis.handle_output(tx_ptr->tx, validated_tx, out_zc, n)) { - LOG_PRINT_RED_L0("handle_output failed for output #" << n << " in " << tx_id); + size_t verified_input_index = std::find(validated_tx.vin.begin(), validated_tx.vin.end(), verified_input) - validated_tx.vin.begin(); + LOG_PRINT_RED_L0("handle_output failed for output #" << n << " in " << tx_id << " referenced by input #" << verified_input_index << " in tx " << get_transaction_hash(validated_tx)); return false; } TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output); diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 7e2b928f..d5693b67 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -1140,6 +1140,13 @@ namespace currency 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); + // TODO: REPLACE all of the following operators to "bool operator==(..) const = default" once we moved to C++20 -- sowle + bool operator ==(const currency::signed_parts& a, const currency::signed_parts& b); + bool operator ==(const currency::txin_gen& a, const currency::txin_gen& b); + bool operator ==(const currency::txin_to_key& a, const currency::txin_to_key& b); + bool operator ==(const currency::txin_multisig& a, const currency::txin_multisig& b); + bool operator ==(const currency::txin_htlc& a, const currency::txin_htlc& b); + bool operator ==(const currency::txin_zc_input& a, const currency::txin_zc_input& b); } // namespace currency POD_MAKE_HASHABLE(currency, account_public_address); diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index e0edf3a4..2c812068 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1162,12 +1162,12 @@ namespace currency 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]; - asset_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + CHECK_AND_ASSERT_MES(~de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); + asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X; out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X) - CHECK_AND_ASSERT_MES(~de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); - asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + amount_blinding_mask = de.flags & tx_destination_entry_flags::tdef_zero_amount_blinding_mask ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) amount_commitment = de.amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G; out.amount_commitment = (crypto::c_scalar_1div8 * amount_commitment).to_public_key(); // E = 1/8 * e * T + 1/8 * y * G @@ -2458,7 +2458,6 @@ namespace currency if (!append_mode && all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) dst_entr.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission) - CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, gen_context.asset_id_blinding_masks[output_index], gen_context.amount_blinding_masks[output_index], gen_context.blinded_asset_ids[output_index], gen_context.amount_commitments[output_index], result, tx_outs_attr); @@ -2964,17 +2963,41 @@ namespace currency return true; } //--------------------------------------------------------------- - uint64_t get_outs_money_amount(const transaction& tx) + uint64_t get_outs_money_amount(const transaction& tx, const currency::account_keys& keys /* = currency::null_acc_keys */) { uint64_t outputs_amount = 0; - for (const auto& o : tx.vout) + + bool process_hidden_amounts = false; + crypto::key_derivation derivation = null_derivation; + if (keys.spend_secret_key != null_skey && keys.view_secret_key != null_skey) { + process_hidden_amounts = true; + bool r = crypto::generate_key_derivation(get_tx_pub_key_from_extra(tx), keys.view_secret_key, derivation); + if (!r) + LOG_PRINT_YELLOW("generate_key_derivation failed in get_outs_money_amount", LOG_LEVEL_0); + } + + for (size_t output_index = 0; output_index < tx.vout.size(); ++output_index) + { + const auto& o = tx.vout[output_index]; VARIANT_SWITCH_BEGIN(o); - VARIANT_CASE_CONST(tx_out_bare, o) - outputs_amount += o.amount; - // ignore outputs with hidden amounts + VARIANT_CASE_CONST(tx_out_bare, bo) + outputs_amount += bo.amount; + VARIANT_CASE_CONST(tx_out_zarcanum, zo) + if (process_hidden_amounts) + { + uint64_t decoded_amount = 0; + crypto::public_key decoded_asset_id{}; + crypto::scalar_t amount_blinding_mask{}, asset_id_blinding_mask{}; + if (is_out_to_acc(keys.account_address, zo, derivation, output_index, decoded_amount, decoded_asset_id, amount_blinding_mask, asset_id_blinding_mask)) + { + if (decoded_asset_id == currency::native_coin_asset_id) + outputs_amount += decoded_amount; + } + } VARIANT_SWITCH_END(); } + return outputs_amount; } //--------------------------------------------------------------- @@ -3282,7 +3305,7 @@ namespace currency } //--------------------------------------------------------------- // NOTE: this function is obsolete and depricated - // PoS block real timestamp is set using a service attachment in mining tx extra since 2021-10 + [[deprecated("PoS block real timestamp is set using a service attachment in mining tx extra since 2021-10")]] uint64_t get_actual_timestamp(const block& b) { uint64_t tes_ts = b.timestamp; @@ -4320,7 +4343,50 @@ namespace currency return a.n == b.n && a.tx_id == b.tx_id; } //-------------------------------------------------------------------------------- - + bool operator ==(const currency::signed_parts& a, const currency::signed_parts& b) + { + return + a.n_extras == b.n_extras && + a.n_outs == b.n_outs; + } + bool operator ==(const currency::txin_gen& a, const currency::txin_gen& b) + { + return a.height == b.height; + } + bool operator ==(const currency::txin_to_key& a, const currency::txin_to_key& b) + { + return + a.amount == b.amount && + a.etc_details == b.etc_details && + a.key_offsets == b.key_offsets && + a.k_image == b.k_image; + } + bool operator ==(const currency::txin_multisig& a, const currency::txin_multisig& b) + { + return + a.amount == b.amount && + a.etc_details == b.etc_details && + a.multisig_out_id == b.multisig_out_id && + a.sigs_count == b.sigs_count; + } + bool operator ==(const currency::txin_htlc& a, const currency::txin_htlc& b) + { + return + a.amount == b.amount && + a.etc_details == b.etc_details && + a.hltc_origin == b.hltc_origin && + a.key_offsets == b.key_offsets && + a.k_image == b.k_image; + } + bool operator ==(const currency::txin_zc_input& a, const currency::txin_zc_input& b) + { + return + a.etc_details == b.etc_details && + a.key_offsets == b.key_offsets && + a.k_image == b.k_image; + } + //-------------------------------------------------------------------------------- + boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point, const wide_difficulty_type& difficulty_pow_at_split_point, diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 8311428e..412f31d8 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -380,7 +380,7 @@ namespace currency bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); uint64_t get_inputs_money_amount(const transaction& tx); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); - uint64_t get_outs_money_amount(const transaction& tx); + uint64_t get_outs_money_amount(const transaction& tx, const currency::account_keys& acc_keys_for_hidden_amounts = currency::null_acc_keys); bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); bool parse_amount(uint64_t& amount, const std::string& str_amount); diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index e4f16d1f..c5c9cfb0 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -62,6 +62,7 @@ namespace currency bool is_multisig() const { return ms_sigs_count > 0; } bool is_zc() const { return !real_out_amount_blinding_mask.is_zero(); } bool is_native_coin() const { return asset_id == currency::native_coin_asset_id; } + uint64_t amount_for_global_output_index() const { return is_zc() ? 0 : amount; } // amount value for global outputs index, it's zero for outputs with hidden amounts BEGIN_SERIALIZE_OBJECT() FIELD(outputs) @@ -96,9 +97,10 @@ namespace currency enum tx_destination_entry_flags { - tdef_none = 0, - tdef_explicit_native_asset_id = 0x0001, - tdef_explicit_amount_to_provide = 0x0002 + tdef_none = 0, + tdef_explicit_native_asset_id = 0x0001, + tdef_explicit_amount_to_provide = 0x0002, + tdef_zero_amount_blinding_mask = 0x0004 // currently it's only used for burning native coins }; struct tx_destination_entry diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 00646db6..c2194866 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1208,16 +1208,8 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_alias_reward(const COMMAND_RPC_GET_ALIAS_REWARD::request& req, COMMAND_RPC_GET_ALIAS_REWARD::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { - - uint64_t default_tx_fee = m_core.get_blockchain_storage().get_core_runtime_config().tx_default_fee; - uint64_t current_median_fee = m_core.get_blockchain_storage().get_tx_fee_median(); - - res.reward = get_alias_coast_from_fee(req.alias, std::max(default_tx_fee, current_median_fee)); - - if (res.reward) - res.status = API_RETURN_CODE_OK; - else - res.status = API_RETURN_CODE_NOT_FOUND; + res.reward = m_core.get_blockchain_storage().get_alias_coast(req.alias); + res.status = API_RETURN_CODE_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/version.h.in b/src/version.h.in index 5c81f31f..c7e36c70 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 213 +#define PROJECT_VERSION_BUILD_NO 217 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3a737704..9bdb27cc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4581,7 +4581,7 @@ uint64_t wallet2::get_alias_cost(const std::string& alias) throw std::runtime_error(std::string("Failed to get alias cost")); } - return rsp.reward + rsp.reward / 10; //add 10% of price to be sure; + return rsp.reward; } //---------------------------------------------------------------------------------------------------- void wallet2::request_alias_registration(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward, const crypto::secret_key& authority_key) @@ -4631,6 +4631,7 @@ void wallet2::request_alias_registration(currency::extra_alias_entry& ai, curren tx_dest_alias_reward.addr.resize(1); get_aliases_reward_account(tx_dest_alias_reward.addr.back()); tx_dest_alias_reward.amount = reward; + tx_dest_alias_reward.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id | tx_destination_entry_flags::tdef_zero_amount_blinding_mask; destinations.push_back(tx_dest_alias_reward); transfer(destinations, 0, 0, fee, extra, attachments, get_current_split_strategy(), tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false); @@ -6480,9 +6481,15 @@ void wallet2::prepare_tx_destinations(const assets_selection_context& needed_mon if (dst.asset_id == currency::null_pkey) final_destinations.emplace_back(dst.amount, dst.addr, dst.asset_id); - // if there's not ehough destinations items (i.e. outputs), split the last one - if (final_destinations.size() > 0 && final_destinations.size() < CURRENCY_TX_MIN_ALLOWED_OUTS) + if (final_destinations.empty()) { + // if there's no destinations -- make CURRENCY_TX_MIN_ALLOWED_OUTS empty destinations + for(size_t i = 0; i < CURRENCY_TX_MIN_ALLOWED_OUTS; ++i) + final_destinations.emplace_back(0, m_account.get_public_address()); + } + else if (final_destinations.size() < CURRENCY_TX_MIN_ALLOWED_OUTS) + { + // if there's not ehough destinations items (i.e. outputs), split the last one tx_destination_entry de = final_destinations.back(); final_destinations.pop_back(); size_t items_to_be_added = CURRENCY_TX_MIN_ALLOWED_OUTS - final_destinations.size(); @@ -6509,7 +6516,7 @@ void wallet2::prepare_tx_destinations(uint64_t needed_money, for(auto& dst : dsts) { if (dst.asset_id == asset_id) - final_destinations.emplace_back(dst.amount, dst.addr, dst.asset_id); + final_destinations.emplace_back(dst); } if (found_money > needed_money) final_destinations.emplace_back(found_money - needed_money, m_account.get_public_address(), asset_id); // returning back the change diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 0572e03f..f4170ebc 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -650,7 +650,7 @@ namespace tools void push_offer(const bc_services::offer_details_ex& od, currency::transaction& res_tx); void cancel_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, uint64_t fee, currency::transaction& tx); void update_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, const bc_services::offer_details_ex& od, currency::transaction& res_tx); - void request_alias_registration(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward, const crypto::secret_key& authority_key = currency::null_skey); + void request_alias_registration(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward = 0, const crypto::secret_key& authority_key = currency::null_skey); // if the given reward is 0, then the actual reward value will be requested via RPC void request_alias_update(currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward); bool check_available_sources(std::list& amounts); diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index ad72a9b4..cf4f3940 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -1394,9 +1394,8 @@ std::string wallets_manager::get_alias_coast(const std::string& a, uint64_t& coa if (!m_rpc_proxy->call_COMMAND_RPC_GET_ALIAS_REWARD(req, rsp)) return API_RETURN_CODE_BAD_ARG; - coast = rsp.reward + rsp.reward/10; //add 10% of price to be sure + coast = rsp.reward; return rsp.status; - } std::string wallets_manager::request_alias_registration(const currency::alias_rpc_details& al, uint64_t wallet_id, uint64_t fee, currency::transaction& res_tx, uint64_t reward) diff --git a/tests/core_tests/alias_tests.cpp b/tests/core_tests/alias_tests.cpp index bdb68673..290d5875 100644 --- a/tests/core_tests/alias_tests.cpp +++ b/tests/core_tests/alias_tests.cpp @@ -100,24 +100,41 @@ gen_alias_tests::gen_alias_tests() bool gen_alias_tests::generate(std::vector& events) const { + bool r = false; GENERATE_ACCOUNT(preminer_account); - GENERATE_ACCOUNT(miner_account); // event index + GENERATE_ACCOUNT(miner_account); m_accounts.push_back(miner_account); - MAKE_GENESIS_BLOCK(events, blk_0, preminer_account, test_core_time::get_time()); // 0 - DO_CALLBACK(events, "configure_core"); // 1 - MAKE_ACCOUNT(events, first_acc); // 2 - MAKE_ACCOUNT(events, second_acc); // 3 - MAKE_ACCOUNT(events, third_acc); // 4 - REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_account, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // 2N+4 (N = CURRENCY_MINED_MONEY_UNLOCK_WINDOW) + MAKE_GENESIS_BLOCK(events, blk_0, preminer_account, test_core_time::get_time()); size_t small_outs_to_transfer = MAX_ALIAS_PER_BLOCK + 10; - transaction tx_1 = AUTO_VAL_INIT(tx_1); - bool r = construct_tx_with_many_outputs(m_hardforks, events, blk_0, preminer_account.get_keys(), miner_account.get_public_address(), small_outs_to_transfer * TESTS_DEFAULT_FEE * 11, small_outs_to_transfer, TESTS_DEFAULT_FEE, tx_1); - CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed"); - events.push_back(tx_1); // 2N+5 - MAKE_NEXT_BLOCK_TX1(events, blk_a, blk_0r, miner_account, tx_1); // 2N+6 - REWIND_BLOCKS_N_WITH_TIME(events, blk_ar, blk_a, miner_account, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // 4N+6 + // rebuild genesis miner tx + std::vector destinations(small_outs_to_transfer, tx_destination_entry(TESTS_DEFAULT_FEE * 11, preminer_account.get_public_address())); + CHECK_AND_ASSERT_MES(replace_coinbase_in_genesis_block(destinations, generator, events, blk_0), false, ""); + + DO_CALLBACK(events, "configure_core"); + MAKE_ACCOUNT(events, first_acc); + MAKE_ACCOUNT(events, second_acc); + MAKE_ACCOUNT(events, third_acc); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, preminer_account, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + size_t outs_in_a_tx = 32 /* <-- TODO change to some constant, meaning max outputs in post HF4 txs */ - 1; + std::vector txs; + while(small_outs_to_transfer > 0) + { + size_t outs_count = std::min(outs_in_a_tx, small_outs_to_transfer); + small_outs_to_transfer -= outs_count; + txs.push_back(transaction{}); + r = construct_tx_with_many_outputs(m_hardforks, events, blk_0r, preminer_account.get_keys(), miner_account.get_public_address(), outs_count * TESTS_DEFAULT_FEE * 11, outs_count, TESTS_DEFAULT_FEE, txs.back()); + CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed"); + ADD_CUSTOM_EVENT(events, txs.back()); + } + // split into two blocks because they won't fit into one + MAKE_NEXT_BLOCK_TX_LIST(events, blk_a, blk_0r, miner_account, std::list(txs.begin(), txs.begin() + txs.size() / 2)); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_a2, blk_a, miner_account, std::list(txs.begin() + txs.size() / 2, txs.end())); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_ar, blk_a2, miner_account, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); MAKE_NEXT_BLOCK(events, blk_1, blk_ar, miner_account); // 4N+7 currency::extra_alias_entry ai = AUTO_VAL_INIT(ai); @@ -926,6 +943,7 @@ gen_alias_too_much_reward::gen_alias_too_much_reward() bool gen_alias_too_much_reward::generate(std::vector& events) const { // pay for alias far too much and see, if it's ok + // UPDATE: since HF4 it's not ok, the reward must be precise uint64_t ts = test_core_time::get_time(); @@ -948,9 +966,32 @@ bool gen_alias_too_much_reward::generate(std::vector& events) bool r = get_aliases_reward_account(const_cast(reward_acc.get_public_address())); CHECK_AND_ASSERT_MES(r, false, "get_aliases_reward_account failed"); - MAKE_TX_FEE_MIX_ATTR_EXTRA(events, tx_0, miner_acc, reward_acc, premine, TESTS_DEFAULT_FEE, 0, blk_0r, CURRENCY_TO_KEY_OUT_RELAXED, extra, false); - MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); - DO_CALLBACK(events, "check_alias"); + std::vector sources; + std::vector destinations; + r = fill_tx_sources_and_destinations(events, blk_0r, miner_acc, reward_acc, premine, TESTS_DEFAULT_FEE, 0, sources, destinations); + CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed"); + for(auto& d : destinations) + if (d.addr.back() == null_pub_addr) + d.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id | tx_destination_entry_flags::tdef_zero_amount_blinding_mask; + transaction tx_0{}; + crypto::secret_key sk{}; + r = construct_tx(miner_acc.get_keys(), sources, destinations, std::vector({ ai }), empty_attachment, tx_0, get_tx_version_from_events(events), sk, 0); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + + if (tx_0.version <= TRANSACTION_VERSION_PRE_HF4) + { + ADD_CUSTOM_EVENT(events, tx_0); + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); + DO_CALLBACK(events, "check_alias"); + } + else + { + // post HF4: alias reward must be precise + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_0); + DO_CALLBACK(events, "mark_invalid_block"); + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); + } return true; } @@ -1039,12 +1080,12 @@ bool gen_alias_too_small_reward::generate(std::vector& events) std::list txs; for (size_t i = 0; i < aliases_count; ++i) { - uint64_t alias_reward = get_alias_coast_from_fee(aliases[i].name, TESTS_DEFAULT_FEE); + uint64_t alias_reward = get_alias_coast_from_fee(aliases[i].name, ALIAS_VERY_INITAL_COAST); transaction tx = AUTO_VAL_INIT(tx); - DO_CALLBACK(events, "mark_invalid_tx"); // should be rejected, because it's paid TX_POOL_MINIMUM_FEE / 10 - if (!make_tx_reg_alias(events, generator, blk_1, aliases[i].name, aliases[i].addr, ALIAS_VERY_INITAL_COAST / 10, miner_acc, tx, used_sources)) - return false; + DO_CALLBACK(events, "mark_invalid_tx"); // should be rejected, because it's paid ALIAS_VERY_INITAL_COAST / 10 + r = make_tx_reg_alias(events, generator, blk_1, aliases[i].name, aliases[i].addr, ALIAS_VERY_INITAL_COAST / 10, miner_acc, tx, used_sources); + CHECK_AND_ASSERT_MES(r, false, "make_tx_reg_alias failed, i: " << i); // this block commented due to new fee median rules, TODO: review // DO_CALLBACK(events, "mark_invalid_tx"); // should be rejected, because it's paid TX_POOL_MINIMUM_FEE / 10 less then required @@ -1052,8 +1093,15 @@ bool gen_alias_too_small_reward::generate(std::vector& events) // return false; // should be accepted - if (!make_tx_reg_alias(events, generator, blk_1, aliases[i].name, aliases[i].addr, alias_reward, miner_acc, tx, used_sources)) - return false; + tx = transaction{}; + r = make_tx_reg_alias(events, generator, blk_1, aliases[i].name, aliases[i].addr, alias_reward, miner_acc, tx, used_sources); + CHECK_AND_ASSERT_MES(r, false, "make_tx_reg_alias failed, i: " << i); + + uint64_t burnt_amount = 0; + CHECK_AND_ASSERT_MES(check_native_coins_amount_burnt_in_outs(tx, alias_reward, &burnt_amount), false, + "registration of alias '" << aliases[i].name << "' failed due to incorrect reward; expected reward: " << print_money_brief(alias_reward) + << "; burnt amount: " << (tx.version <= TRANSACTION_VERSION_PRE_HF4 ? print_money_brief(burnt_amount) : std::string("hidden")) + << "; tx: " << get_transaction_hash(tx)); txs.push_back(tx); } @@ -1106,11 +1154,13 @@ bool gen_alias_too_small_reward::make_tx_reg_alias(std::vector std::vector sources; uint64_t amount = alias_reward + TESTS_DEFAULT_FEE; - r = fill_tx_sources(sources, events, prev_block, miner_acc.get_keys(), amount, 0, used_sources); + r = fill_tx_sources(sources, events, prev_block, miner_acc.get_keys(), amount, 0, used_sources, /* check for spends: */ true, /* check for unlock time: */ true); CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed, requested money: " << print_money_brief(amount)); std::vector destinations; - destinations.push_back(tx_destination_entry(alias_reward, reward_acc.get_public_address())); + tx_destination_entry burn_dst(alias_reward, reward_acc.get_public_address()); + burn_dst.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id | tx_destination_entry_flags::tdef_zero_amount_blinding_mask; // burning outs need to have this flags to facilitate balance check + destinations.push_back(burn_dst); uint64_t sources_amount = get_sources_total_amount(sources); if (sources_amount > alias_reward + TESTS_DEFAULT_FEE) destinations.push_back(tx_destination_entry(sources_amount - (alias_reward + TESTS_DEFAULT_FEE), miner_acc.get_public_address())); // change @@ -1119,6 +1169,7 @@ bool gen_alias_too_small_reward::make_tx_reg_alias(std::vector uint64_t tx_version = get_tx_version(get_block_height(prev_block), m_hardforks); r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx, tx_version, stub, uint64_t(0)); CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + PRINT_EVENT_N_TEXT(events, "make_tx_reg_alias -> construct_tx()"); events.push_back(tx); append_vector_by_another_vector(used_sources, sources); @@ -1217,7 +1268,7 @@ bool gen_alias_switch_and_check_block_template::generate(std::vector& events) for (auto se : sources) input_amount += se.amount; if (input_amount > TESTS_DEFAULT_FEE) - destinations.push_back(tx_destination_entry(input_amount - TESTS_DEFAULT_FEE, miner_acc.get_public_address())); + { + uint64_t d = input_amount - TESTS_DEFAULT_FEE; + destinations.push_back(tx_destination_entry(d / 2, miner_acc.get_public_address())); + d -= d / 2; + destinations.push_back(tx_destination_entry(d, miner_acc.get_public_address())); + } - tx_builder tb; + transaction tx{}; + uint64_t tx_version = currency::get_tx_version(get_block_height(blk_0r) + 1, generator.get_hardforks()); + crypto::secret_key sk{}; + r = construct_tx(miner_acc.get_keys(), sources, destinations, std::vector({ ai }), empty_attachment, tx, tx_version, sk, 0); + + /*tx_builder tb; tb.step1_init(); tb.step2_fill_inputs(miner_acc.get_keys(), sources); tb.step3_fill_outputs(destinations); tb.m_tx.extra.push_back(ai); tb.step4_calc_hash(); - tb.step5_sign(sources); + tb.step5_sign(sources);*/ - events.push_back(tb.m_tx); - MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1, miner_acc, tb.m_tx); + ADD_CUSTOM_EVENT(events, tx); + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1, miner_acc, tx); return true; } @@ -1466,16 +1529,18 @@ bool gen_alias_in_coinbase::generate(std::vector& events) cons set_hard_fork_heights_to_generator(generator); DO_CALLBACK(events, "configure_core"); + MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc); + // reg an alias using coinbase extra_alias_entry ai = AUTO_VAL_INIT(ai); ai.m_alias = "emmanuel.goldstein"; // long enough alias to minimize it's cost ai.m_address = miner_acc.get_public_address(); - block blk_1 = AUTO_VAL_INIT(blk_1); - bool r = generator.construct_pow_block_with_alias_info_in_coinbase(miner_acc, blk_0, ai, blk_1); + block blk_2 = AUTO_VAL_INIT(blk_2); + bool r = generator.construct_pow_block_with_alias_info_in_coinbase(miner_acc, blk_1, ai, blk_2); CHECK_AND_ASSERT_MES(r, false, "construct_block_gentime_with_coinbase_cb failed"); - events.push_back(blk_1); + events.push_back(blk_2); DO_CALLBACK_PARAMS_STR(events, "check", t_serializable_object_to_blob(ai)); @@ -1487,11 +1552,11 @@ bool gen_alias_in_coinbase::generate(std::vector& events) cons r = sign_extra_alias_entry(ai, miner_acc.get_public_address().spend_public_key, miner_acc.get_keys().spend_secret_key); CHECK_AND_ASSERT_MES(r, false, "sign_extra_alias_entry failed"); - block blk_2 = AUTO_VAL_INIT(blk_2); - r = generator.construct_pow_block_with_alias_info_in_coinbase(miner_acc, blk_1, ai, blk_2); + block blk_3 = AUTO_VAL_INIT(blk_3); + r = generator.construct_pow_block_with_alias_info_in_coinbase(miner_acc, blk_2, ai, blk_3); CHECK_AND_ASSERT_MES(r, false, "construct_block_gentime_with_coinbase_cb failed"); - events.push_back(blk_2); + events.push_back(blk_3); DO_CALLBACK_PARAMS_STR(events, "check", t_serializable_object_to_blob(ai)); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index df5ef7f1..fdbb1f48 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1023,18 +1023,90 @@ uint64_t test_generator::get_last_n_blocks_fee_median(const crypto::hash& head_b bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const account_base& acc, const block& prev_block, const extra_alias_entry& ai, block& b) { - return construct_block_gentime_with_coinbase_cb(prev_block, acc, [&acc, &ai](transaction& miner_tx){ + return construct_block_gentime_with_coinbase_cb(prev_block, acc, [&acc, &ai](transaction& miner_tx, const currency::keypair& tx_key){ + bool r = false; miner_tx.extra.push_back(ai); - if (ai.m_sign.empty()) + + uint64_t alias_cost = ai.m_sign.empty() ? get_alias_coast_from_fee(ai.m_alias, ALIAS_VERY_INITAL_COAST) : 0; + uint64_t block_reward = get_outs_money_amount(miner_tx, acc.get_keys()); + CHECK_AND_ASSERT_MES(alias_cost < block_reward, false, "Alias '" << ai.m_alias << "' can't be registered via block coinbase, because it's price: " << print_money(alias_cost) << " is greater than block reward: " << print_money(block_reward)); + uint64_t new_block_reward = block_reward - alias_cost; + if (miner_tx.version > TRANSACTION_VERSION_PRE_HF4) { - // if no alias update - reduce block reward by alias cost - uint64_t alias_cost = get_alias_coast_from_fee(ai.m_alias, TESTS_DEFAULT_FEE); - uint64_t block_reward = get_outs_money_amount(miner_tx); - CHECK_AND_ASSERT_MES(alias_cost <= block_reward, false, "Alias '" << ai.m_alias << "' can't be registered via block coinbase, because it's price: " << print_money(alias_cost) << " is greater than block reward: " << print_money(block_reward)); - block_reward -= alias_cost; + // ZC outs miner_tx.vout.clear(); - if (block_reward > 0) + + tx_generation_context tx_gen_context{}; + tx_gen_context.resize(/* ZC ins: */ 0, /* OUTS: */ alias_cost != 0 ? 3 : 2); + std::set deriv_cache; + finalized_tx fin_tx_stub{}; + size_t output_index = 0; + + // outputs 0, 1: block reward splitted into two + tx_destination_entry de{new_block_reward / 2, acc.get_public_address()}; + de.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; + r = construct_tx_out(de, tx_key.sec, output_index, miner_tx, deriv_cache, account_keys(), + tx_gen_context.asset_id_blinding_masks[output_index], tx_gen_context.amount_blinding_masks[output_index], + tx_gen_context.blinded_asset_ids[output_index], tx_gen_context.amount_commitments[output_index], fin_tx_stub); + CHECK_AND_ASSERT_MES(r, false, "construct_tx_out failed for output " << output_index); + tx_gen_context.amounts[output_index] = de.amount; + tx_gen_context.asset_ids[output_index] = crypto::point_t(de.asset_id); + tx_gen_context.asset_id_blinding_mask_x_amount_sum += tx_gen_context.asset_id_blinding_masks[output_index] * de.amount; + tx_gen_context.amount_blinding_masks_sum += tx_gen_context.amount_blinding_masks[output_index]; + tx_gen_context.amount_commitments_sum += tx_gen_context.amount_commitments[output_index]; + + ++output_index; + de = tx_destination_entry{new_block_reward - new_block_reward / 2, acc.get_public_address()}; + de.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; + r = construct_tx_out(de, tx_key.sec, output_index, miner_tx, deriv_cache, account_keys(), + tx_gen_context.asset_id_blinding_masks[output_index], tx_gen_context.amount_blinding_masks[output_index], + tx_gen_context.blinded_asset_ids[output_index], tx_gen_context.amount_commitments[output_index], fin_tx_stub); + CHECK_AND_ASSERT_MES(r, false, "construct_tx_out failed for output " << output_index); + tx_gen_context.amounts[output_index] = de.amount; + tx_gen_context.asset_ids[output_index] = crypto::point_t(de.asset_id); + tx_gen_context.asset_id_blinding_mask_x_amount_sum += tx_gen_context.asset_id_blinding_masks[output_index] * de.amount; + tx_gen_context.amount_blinding_masks_sum += tx_gen_context.amount_blinding_masks[output_index]; + tx_gen_context.amount_commitments_sum += tx_gen_context.amount_commitments[output_index]; + + if (alias_cost != 0) { + // output 2: alias reward + ++output_index; + de = tx_destination_entry{alias_cost, null_pub_addr}; + de.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id | tx_destination_entry_flags::tdef_zero_amount_blinding_mask; + r = construct_tx_out(de, tx_key.sec, output_index, miner_tx, deriv_cache, account_keys(), + tx_gen_context.asset_id_blinding_masks[output_index], tx_gen_context.amount_blinding_masks[output_index], + tx_gen_context.blinded_asset_ids[output_index], tx_gen_context.amount_commitments[output_index], fin_tx_stub); + CHECK_AND_ASSERT_MES(r, false, "construct_tx_out failed for output " << output_index); + tx_gen_context.amounts[output_index] = de.amount; + tx_gen_context.asset_ids[output_index] = crypto::point_t(de.asset_id); + tx_gen_context.asset_id_blinding_mask_x_amount_sum += tx_gen_context.asset_id_blinding_masks[output_index] * de.amount; + tx_gen_context.amount_blinding_masks_sum += tx_gen_context.amount_blinding_masks[output_index]; + tx_gen_context.amount_commitments_sum += tx_gen_context.amount_commitments[output_index]; + } + + // reconstruct all proofs + crypto::hash tx_id = get_transaction_hash(miner_tx); + miner_tx.proofs.clear(); + // empty asset surjection proof + miner_tx.proofs.emplace_back(std::move(currency::zc_asset_surjection_proof{})); + // range proofs + currency::zc_outs_range_proof range_proofs{}; + r = generate_zc_outs_range_proof(tx_id, 0, tx_gen_context, miner_tx.vout, range_proofs); + CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()"); + miner_tx.proofs.emplace_back(std::move(range_proofs)); + // balance proof + currency::zc_balance_proof balance_proof{}; + r = generate_tx_balance_proof(miner_tx, tx_id, tx_gen_context, block_reward, balance_proof); + CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed"); + miner_tx.proofs.emplace_back(std::move(balance_proof)); + } + else + { + // old behaviour, bare non-hidden outs + if (ai.m_sign.empty()) + { + miner_tx.vout.clear(); bool null_out_key = false; auto f = [&acc, &miner_tx, &null_out_key](uint64_t amount){ keypair kp = AUTO_VAL_INIT(kp); @@ -1043,7 +1115,7 @@ bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const accou otk.key = null_out_key ? null_pkey : kp.pub; miner_tx.vout.push_back(tx_out_bare({ amount, otk })); }; - decompose_amount_into_digits(block_reward, DEFAULT_DUST_THRESHOLD, f, f); + decompose_amount_into_digits(new_block_reward, DEFAULT_DUST_THRESHOLD, f, f); null_out_key = true; f(alias_cost); // add an output for burning alias cost into ashes } @@ -1351,7 +1423,7 @@ bool fill_tx_sources(std::vector& sources, const std: 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]; + auto& outs_by_amount = outs[s.amount_for_global_output_index()]; if (gindex >= outs_by_amount.size()) return false; outs_by_amount[gindex].spent = true; @@ -1703,7 +1775,7 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx, bool use_ref_by_id /* = false */) { std::vector sources; - bool r = fill_tx_sources(sources, events, blk_head, keys_from, total_amount + fee, 0, true, false, use_ref_by_id); + bool r = fill_tx_sources(sources, events, blk_head, keys_from, total_amount + fee, 0, true, true, use_ref_by_id); CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed"); std::vector destinations; @@ -2255,6 +2327,53 @@ bool shuffle_source_entries(std::vector& sources) return true; } +bool replace_coinbase_in_genesis_block(const std::vector& destinations, test_generator& generator, std::vector& events, currency::block& genesis_block) +{ + bool r = false; + generator.remove_block_info(genesis_block); + events.pop_back(); + + // remember premine amount + uint64_t premine_amount = get_outs_money_amount(genesis_block.miner_tx); + + // replace tx key + keypair tx_key = keypair::generate(); + for(auto& el : genesis_block.miner_tx.extra) + { + if (el.type() == typeid(crypto::public_key)) + { + boost::get(el) = tx_key.pub; + break; + } + } + uint64_t total_amount = 0; + + // replace outputs + genesis_block.miner_tx.vout.clear(); + + for(size_t output_index = 0; output_index < destinations.size() + 1; ++output_index) + { + uint64_t amount = output_index < destinations.size() ? destinations[output_index].amount : premine_amount - total_amount; + const account_public_address& addr = output_index < destinations.size() ? destinations[output_index].addr.back() : destinations.back().addr.back(); + + crypto::key_derivation derivation{}; + bool r = crypto::generate_key_derivation(addr.view_public_key, tx_key.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed"); + + txout_to_key target{}; + r = crypto::derive_public_key(derivation, output_index, addr.spend_public_key, target.key); + CHECK_AND_ASSERT_MES(r, false, "derive_public_key failed"); + genesis_block.miner_tx.vout.emplace_back(tx_out_bare{amount, target}); + total_amount += amount; + CHECK_AND_ASSERT_MES(total_amount <= premine_amount, false, "total amount is greater than premine amount"); + } + + events.push_back(genesis_block); + std::vector block_sizes; + generator.add_block(genesis_block, 0, block_sizes, 0, 0, std::list{}, null_hash); + return true; +} + //------------------------------------------------------------------------------ test_chain_unit_base::test_chain_unit_base() diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index b904770a..663593c7 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -745,6 +745,9 @@ bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, c bool shuffle_source_entry(currency::tx_source_entry& se); bool shuffle_source_entries(std::vector& sources); +// one output will be created for each destination entry and one additional output to add up to old coinbase total amount +bool replace_coinbase_in_genesis_block(const std::vector& destinations, test_generator& generator, std::vector& events, currency::block& genesis_block); + //-------------------------------------------------------------------------- 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) @@ -954,10 +957,13 @@ bool test_generator::construct_block_gentime_with_coinbase_cb(const currency::bl uint64_t block_reward_without_fee = 0; - r = construct_miner_tx(height, epee::misc_utils::median(block_sizes), already_generated_coins, 0 /* current_block_size !HACK! */, 0, acc.get_public_address(), acc.get_public_address(), miner_tx, block_reward_without_fee, get_tx_version(height, m_hardforks), currency::blobdata(), 1); + currency::keypair tx_sec_key = currency::keypair::generate(); + r = construct_miner_tx(height, epee::misc_utils::median(block_sizes), already_generated_coins, 0 /* current_block_size !HACK! */, 0, + acc.get_public_address(), acc.get_public_address(), miner_tx, block_reward_without_fee, get_tx_version(height, m_hardforks), currency::blobdata(), /* max outs: */ 1, + /* pos: */ false, currency::pos_entry(), /* ogc_ptr: */ nullptr, &tx_sec_key); CHECK_AND_ASSERT_MES(r, false, "construct_miner_tx failed"); - if (!cb(miner_tx)) + if (!cb(miner_tx, tx_sec_key)) return false; currency::wide_difficulty_type diff = get_difficulty_for_next_block(prev_id, true); diff --git a/tests/core_tests/chaingen_helpers.h b/tests/core_tests/chaingen_helpers.h index b984fde7..a5e59ab8 100644 --- a/tests/core_tests/chaingen_helpers.h +++ b/tests/core_tests/chaingen_helpers.h @@ -283,30 +283,40 @@ inline bool put_alias_via_tx_to_list(const currency::hard_forks_descriptor& hf, const alias_entry_t& ae, test_generator& generator) { - std::vector ex; - ex.push_back(ae); + std::vector extra; + extra.push_back(ae); currency::account_base reward_acc; currency::account_keys& ak = const_cast(reward_acc.get_keys()); currency::get_aliases_reward_account(ak.account_address, ak.view_secret_key); uint64_t alias_reward = 0; if (get_block_height(head_block) < ALIAS_MEDIAN_RECALC_INTERWAL) - alias_reward = get_alias_coast_from_fee(ae.m_alias, ALIAS_VERY_INITAL_COAST); // don't ask why + { + alias_reward = currency::get_alias_coast_from_fee(ae.m_alias, ALIAS_VERY_INITAL_COAST); // don't ask why + } else + { LOCAL_ASSERT(false); // not implemented yet, see also all the mess around blockchain_storage::get_tx_fee_median(), get_tx_fee_median_effective_index() etc. + } - MAKE_TX_MIX_LIST_EXTRA_MIX_ATTR(events, - tx_set, - miner_acc, - reward_acc, - alias_reward, - 0, - head_block, - CURRENCY_TO_KEY_OUT_RELAXED, - ex, - std::vector()); - + std::vector sources; + std::vector destinations; + bool r = fill_tx_sources_and_destinations(events, head_block, miner_acc, reward_acc, alias_reward, TESTS_DEFAULT_FEE, 0, sources, destinations); + CHECK_AND_ASSERT_MES(r, false, "alias: fill_tx_sources_and_destinations failed"); + for(auto& el : destinations) + { + if (el.addr.front() == reward_acc.get_public_address()) + el.flags |= currency::tx_destination_entry_flags::tdef_explicit_native_asset_id | currency::tx_destination_entry_flags::tdef_zero_amount_blinding_mask; // all alias-burn outputs must have explicit native asset id and zero amount mask + } + + uint64_t tx_version = currency::get_tx_version(get_block_height(head_block) + 1, generator.get_hardforks()); // assuming the tx will be in the next block (head_block + 1) + tx_set.emplace_back(); + r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_set.back(), tx_version, generator.last_tx_generated_secret_key, 0); + PRINT_EVENT_N_TEXT(events, "put_alias_via_tx_to_list()"); + events.push_back(tx_set.back()); + + // make sure the tx's amount commitments balance each other correctly uint64_t burnt_amount = 0; if (!check_native_coins_amount_burnt_in_outs(tx_set.back(), alias_reward, &burnt_amount)) { diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 04e21245..1904ce7b 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -118,7 +118,7 @@ bool test_parse_hardfork_str_mask() { static_assert(ZANO_HARDFORKS_TOTAL >= 5, "this test was made in assumption that this condition holds"); auto v_range = [](size_t a, size_t b) -> std::vector { std::vector r; for(size_t i = a; i <= b; ++i) r.push_back(i); return r; }; - auto v_concat = [](const std::vector& a, const std::vector& b) -> std::vector { std::vector r = a; r.insert(r.end(), b.begin(), b.end()); }; + auto v_concat = [](const std::vector& a, const std::vector& b) -> std::vector { std::vector r = a; r.insert(r.end(), b.begin(), b.end()); return r; }; const std::vector res_empty; const std::vector res_all_hf = v_range(0, ZANO_HARDFORKS_TOTAL - 1); std::string hf_total_num_str_m_1 = epee::string_tools::num_to_string_fast(ZANO_HARDFORKS_TOTAL - 1); @@ -386,7 +386,7 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_ #define GENERATE_AND_PLAY(genclass) \ - if((!postponed_tests.count(#genclass) && run_single_test.empty()) || (!run_single_test.empty() && std::string::npos != std::string(#genclass).find(run_single_test))) \ + if (!skip_all_till_the_end && ((!postponed_tests.count(#genclass) && run_single_test.empty()) || (!run_single_test.empty() && std::string::npos != std::string(#genclass).find(run_single_test)))) \ { \ TIME_MEASURE_START_MS(t); \ ++tests_count; \ @@ -396,14 +396,14 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_ failed_tests.insert(#genclass); \ LOCAL_ASSERT(false); \ if (stop_on_first_fail) \ - return 1; \ + skip_all_till_the_end = true; \ } \ TIME_MEASURE_FINISH_MS(t); \ tests_running_time.push_back(std::make_pair(#genclass, t)); \ } #define GENERATE_AND_PLAY_INTERMITTED_BY_BLOCKCHAIN_SAVELOAD(genclass) \ - if(run_single_test.empty() || run_single_test == #genclass) \ + if (!skip_all_till_the_end && (run_single_test.empty() || run_single_test == #genclass)) \ { \ const char* testname = #genclass " (BC saveload)"; \ TIME_MEASURE_START_MS(t); \ @@ -414,29 +414,29 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_ failed_tests.insert(testname); \ LOCAL_ASSERT(false); \ if (stop_on_first_fail) \ - return 1; \ + skip_all_till_the_end = true; \ } \ TIME_MEASURE_FINISH_MS(t); \ tests_running_time.push_back(std::make_pair(testname, t)); \ } #define GENERATE_AND_PLAY_HF(genclass, hardfork_str_mask) \ - if((!postponed_tests.count(#genclass) && run_single_test.empty()) || (!run_single_test.empty() && std::string::npos != std::string(#genclass).find(run_single_test))) \ + if (!skip_all_till_the_end && ((!postponed_tests.count(#genclass) && run_single_test.empty()) || (!run_single_test.empty() && std::string::npos != std::string(#genclass).find(run_single_test)))) \ { \ std::vector hardforks = parse_hardfork_str_mask(hardfork_str_mask); \ CHECK_AND_ASSERT_MES(!hardforks.empty(), false, "invalid hardforks mask: " << hardfork_str_mask); \ - for(size_t hfid : hardforks) \ + for(size_t i = 0; i < hardforks.size() && !skip_all_till_the_end; ++i) \ { \ - std::string tns = std::string(#genclass) + " @ HF " + epee::string_tools::num_to_string_fast(hfid); \ + std::string tns = std::string(#genclass) + " @ HF " + epee::string_tools::num_to_string_fast(hardforks[i]); \ const char* testname = tns.c_str(); \ TIME_MEASURE_START_MS(t); \ ++tests_count; \ - if (!generate_and_play(testname, hfid)) \ + if (!generate_and_play(testname, hardforks[i])) \ { \ failed_tests.insert(testname); \ LOCAL_ASSERT(false); \ if (stop_on_first_fail) \ - return 1; \ + skip_all_till_the_end = true; \ } \ TIME_MEASURE_FINISH_MS(t); \ tests_running_time.push_back(std::make_pair(testname, t)); \ @@ -889,6 +889,8 @@ int main(int argc, char* argv[]) stop_on_first_fail = command_line::get_arg(g_vm, arg_stop_on_fail); } + + bool skip_all_till_the_end = false; size_t tests_count = 0; size_t unique_tests_count = 0; size_t serious_failures_count = 0; @@ -1031,23 +1033,22 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_HF(gen_no_attchments_in_coinbase, "3"); GENERATE_AND_PLAY(gen_no_attchments_in_coinbase_gentime); - //GENERATE_AND_PLAY(gen_alias_tests); - GENERATE_AND_PLAY_HF(gen_alias_tests, "4,1"); - GENERATE_AND_PLAY(gen_alias_strange_data); - GENERATE_AND_PLAY(gen_alias_concurrency_with_switch); - GENERATE_AND_PLAY(gen_alias_same_alias_in_tx_pool); - GENERATE_AND_PLAY(gen_alias_switch_and_tx_pool); - GENERATE_AND_PLAY(gen_alias_update_after_addr_changed); - GENERATE_AND_PLAY(gen_alias_blocking_reg_by_invalid_tx); - GENERATE_AND_PLAY(gen_alias_blocking_update_by_invalid_tx); + GENERATE_AND_PLAY_HF(gen_alias_tests, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_strange_data, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_concurrency_with_switch, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_same_alias_in_tx_pool, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_switch_and_tx_pool, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_update_after_addr_changed, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_blocking_reg_by_invalid_tx, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_blocking_update_by_invalid_tx, "3-*"); GENERATE_AND_PLAY_HF(gen_alias_reg_with_locked_money, "*"); - GENERATE_AND_PLAY(gen_alias_too_small_reward); - GENERATE_AND_PLAY(gen_alias_too_much_reward); + GENERATE_AND_PLAY_HF(gen_alias_too_small_reward, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_too_much_reward, "3-*"); GENERATE_AND_PLAY_HF(gen_alias_tx_no_outs, "*"); - GENERATE_AND_PLAY(gen_alias_switch_and_check_block_template); - GENERATE_AND_PLAY(gen_alias_too_many_regs_in_block_template); - GENERATE_AND_PLAY(gen_alias_update_for_free); - GENERATE_AND_PLAY(gen_alias_in_coinbase); + GENERATE_AND_PLAY_HF(gen_alias_switch_and_check_block_template, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_too_many_regs_in_block_template, "3"); // disabled in HF4 due to tx outputs count limitation + GENERATE_AND_PLAY_HF(gen_alias_update_for_free, "3-*"); + GENERATE_AND_PLAY_HF(gen_alias_in_coinbase, "3-*"); GENERATE_AND_PLAY(gen_wallet_basic_transfer); GENERATE_AND_PLAY(gen_wallet_refreshing_on_chain_switch); @@ -1062,8 +1063,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_wallet_transfers_and_outdated_unconfirmed_txs); GENERATE_AND_PLAY(gen_wallet_transfers_and_chain_switch); GENERATE_AND_PLAY(gen_wallet_decrypted_attachments); - GENERATE_AND_PLAY(gen_wallet_alias_and_unconfirmed_txs); - GENERATE_AND_PLAY(gen_wallet_alias_via_special_wallet_funcs); + GENERATE_AND_PLAY_HF(gen_wallet_alias_and_unconfirmed_txs, "3-*"); + GENERATE_AND_PLAY_HF(gen_wallet_alias_via_special_wallet_funcs, "3-*"); GENERATE_AND_PLAY(gen_wallet_fake_outputs_randomness); GENERATE_AND_PLAY(gen_wallet_fake_outputs_not_enough); GENERATE_AND_PLAY(gen_wallet_offers_basic); @@ -1284,6 +1285,9 @@ int main(int argc, char* argv[]) } } + if (skip_all_till_the_end) + std::cout << ENDL << concolor::yellow << "(execution interrupted at the first failure; not all tests were run)" << ENDL; + serious_failures_count = failed_tests.size() - failed_postponed_tests_count; if (!postponed_tests.empty()) diff --git a/tests/core_tests/checkpoints_tests.cpp b/tests/core_tests/checkpoints_tests.cpp index 49eeafc2..5692734b 100644 --- a/tests/core_tests/checkpoints_tests.cpp +++ b/tests/core_tests/checkpoints_tests.cpp @@ -827,7 +827,7 @@ bool gen_no_attchments_in_coinbase_gentime::generate(std::vector