diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index ed290387..2d35c703 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1202,3 +1202,151 @@ bool block_reward_in_alt_chain_basic::assert_reward(currency::core& core, size_t return true; } + +//----------------------------------------------------------------------------------------------------- +block_choice_rule_bigger_fee::block_choice_rule_bigger_fee() +{ + REGISTER_CALLBACK("c1", block_choice_rule_bigger_fee::c1); +} + +struct block_choice_rule_bigger_fee::argument_assert +{ + std::list transactions{}; + + argument_assert() = default; + + argument_assert(const std::list& txs) + : transactions(txs) + {} + + BEGIN_SERIALIZE() + FIELD(transactions) + END_SERIALIZE() +}; + +// Test idea: fork-choice rule based on transactions’ median fees +/* Sets up three competing chains: + * - Main(blk_1a): 4 transactions with fee 6 (fees = [6, 6, 6, 6], median = (6 + 6) / 2 = 6, score = 6 * 4 = 24) + * - Alt1(blk_1b): 2 transactions with fee 11 (fees = [11, 11], median = (11 + 11) / 2 = 11, score = 11 * 2 = 22) + * - Alt2(blk_1): 2 transactions with fee 10 (fees = [10, 10], median = (10 + 10) / 2 = 10, score = 10 * 2 = 20) + * + * Fork-choice rule: + * - Even count: median = average of the two middle fees, then multiply by the number of transactions. + * - Odd count: median = fee of the central transaction, then multiply by the number of transactions. + * + * The chain with the highest resulting value wins. In this test, Main(blk_1a) wins (24 > 22 and 24 > 20) + * and remains the preferred chain even after Alt2Alt2(blk_1) appears. + */ +bool block_choice_rule_bigger_fee::generate(std::vector& events) const +{ + GENERATE_ACCOUNT(miner); + MAKE_GENESIS_BLOCK(events, blk_0, miner, test_core_time::get_time()); + DO_CALLBACK(events, "configure_core"); + + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // Main chain + MAKE_TX_FEE(events, tx_1, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE * 10, blk_0r); + MAKE_TX_FEE(events, tx_2, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE * 10, blk_0r); + + std::list txs_1{tx_1, tx_2}; + MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner, txs_1); + + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_1)); + DO_CALLBACK(events, "check_tx_pool_empty"); + + /* 0 10 11 + (blk_0) - ... - (blk_0r) - (blk_1) + {tx0} {tx1, tx2} + */ + + // Alt chain + MAKE_TX_FEE(events, tx_3, miner, miner, MK_TEST_COINS(8), TESTS_DEFAULT_FEE * 6, blk_0r); + MAKE_TX_FEE(events, tx_4, miner, miner, MK_TEST_COINS(8), TESTS_DEFAULT_FEE * 6, blk_0r); + MAKE_TX_FEE(events, tx_5, miner, miner, MK_TEST_COINS(8), TESTS_DEFAULT_FEE * 6, blk_0r); + MAKE_TX_FEE(events, tx_6, miner, miner, MK_TEST_COINS(8), TESTS_DEFAULT_FEE * 6, blk_0r); + + std::list txs_1a{tx_3, tx_4, tx_5, tx_6}; + MAKE_NEXT_BLOCK_TX_LIST(events, blk_1a, blk_0r, miner, txs_1a); + + // tx_1,tx_2 should be in pool + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_1a)); + DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast(2)); + + // Fees are pre-sorted: + // - If count is even: sum the two middle fees -> e.g., 6 + 6 / 2 * 4 = 24 (blk_1a) + // - If the number is odd: take the central transaction, for example tx1 tx2 tx3 - the fee of tx2 will be median + /* 0 10 11 + (blk_0) - ... - (blk_0r) - (blk_1a) - win because 1 + {tx0} {tx_3, tx_4, tx_5, tx_6} + | + | 11 + \ - (blk_1) + */ + + std::list transactions; + for (const auto& tx : txs_1) + { + transactions.push_back(get_transaction_hash(tx)); + } + argument_assert argument_1a{transactions}; + + DO_CALLBACK_PARAMS_STR(events, "c1", t_serializable_object_to_blob(argument_1a)); + + MAKE_TX_FEE(events, tx_7, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE * 11, blk_0r); + MAKE_TX_FEE(events, tx_8, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE * 11, blk_0r); + + std::list txs_1b{tx_7, tx_8}; + MAKE_NEXT_BLOCK_TX_LIST(events, blk_1b, blk_0r, miner, txs_1b); + + /* 0 10 11 + (blk_0) - ... - (blk_0r) - (blk_1a) - won because (6 + 6) / 2 * 4 = 24 > 22(blk_1b) + {tx0} {tx_3, tx_4, tx_5, tx_6} + | + | 11 + \ - (blk_1b) - lost because after sorting the central element has the value (11 + 11) / 2 * 2 = 22 < 24 + | + | 11 + \ - (blk_1) - lost (10 + 10) / 2 * 2 = 20 < 24 + */ + + // tx_1, tx_2, tx_7, tx_8 should be in pool + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_1a)); + DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast(4)); + + for (const auto& tx : txs_1b) + { + transactions.push_back(get_transaction_hash(tx)); + } + argument_assert argument_1b{transactions}; + DO_CALLBACK_PARAMS_STR(events, "c1", t_serializable_object_to_blob(argument_1b)); + + return true; +} + +bool block_choice_rule_bigger_fee::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + argument_assert argument{}; + { + const auto serialized_argument{boost::get(events.at(ev_index)).callback_params}; + + CHECK_AND_ASSERT_EQ(t_unserializable_object_from_blob(argument, serialized_argument), true); + } + + std::list txs; + c.get_pool_transactions(txs); + + CHECK_AND_ASSERT_MES(txs.size() == argument.transactions.size(), false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + std::list hash_txs; + for (const auto& tx : txs) + { + hash_txs.push_back(get_transaction_hash(tx)); + } + + hash_txs.sort(); + argument.transactions.sort(); + CHECK_AND_ASSERT_MES(hash_txs == argument.transactions, false, "Unexpected transactions in the mempool"); + + return true; +} diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 59e4c149..1eac64bd 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -217,3 +217,13 @@ private: bool assert_reward(currency::core& core, size_t event_index, const std::vector& events) const; struct argument_assert; }; + +struct block_choice_rule_bigger_fee : public wallet_test +{ + block_choice_rule_bigger_fee(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + +private: + struct argument_assert; +}; \ No newline at end of file diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index cd886555..fb722a87 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1191,7 +1191,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_HF(gen_block_miner_tx_has_out_to_initiator, "0,3"); GENERATE_AND_PLAY_HF(gen_block_has_invalid_tx, "0,3"); GENERATE_AND_PLAY_HF(gen_block_is_too_big, "0,3"); - GENERATE_AND_PLAY_HF(gen_block_wrong_version_agains_hardfork, "0,3"); + GENERATE_AND_PLAY_HF(gen_block_wrong_version_agains_hardfork, "0,3"); + GENERATE_AND_PLAY_HF(block_choice_rule_bigger_fee, "4-*"); //GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 10