From b836742f664b40fc8f95ea7f6246a2d3a07e6141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=D1=91pa=20Dolgorukov?= Date: Tue, 15 Oct 2024 05:30:05 +0500 Subject: [PATCH] coretests: improve of the test "tx_pool_semantic_validation" (#465) * Coretests: implement the test "tx_pool_semantic_validation" * Play the test "tx_pool_semantic_validation" on the HF 3 * Delete an extra space after the ":" * Remove an extra adding of objects to the transaction * Create the input objects of the specified type in the "unsupported input type" case * Modify the function bodies "inputs_sum", "outputs_sum" to be short * Remove adding output to the transaction in the cases "inputs amount overflow", "two entries of the same type in extra" * Get rid of use the object "image" of the type key_image in the case "equal key images in inputs" --- tests/core_tests/chaingen_main.cpp | 4 +- tests/core_tests/tx_validation.cpp | 225 ++++++++++++++++++++++++++--- tests/core_tests/tx_validation.h | 2 +- 3 files changed, 212 insertions(+), 19 deletions(-) diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 9c3edec0..4d3acee1 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1218,7 +1218,9 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(tx_expiration_time_and_chain_switching); GENERATE_AND_PLAY(tx_key_image_pool_conflict); //GENERATE_AND_PLAY_HF(tx_version_against_hardfork, "4-*"); - GENERATE_AND_PLAY_HF(tx_pool_semantic_validation, "4-*"); + /* To execute the check of bare balance (function "check_tx_bare_balance") we need to run the test "tx_pool_semantic_validation" on the HF 3. By default behaviour bare outputs are disallowed on + the heights >= 10. */ + GENERATE_AND_PLAY_HF(tx_pool_semantic_validation, "3"); // Double spend GENERATE_AND_PLAY(gen_double_spend_in_tx); diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index f82c3a10..ea5f6b38 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1728,7 +1728,7 @@ bool tx_pool_semantic_validation::generate(std::vector& events { transaction tx{}; - tx.vin.emplace_back(); + tx.vin.emplace_back(txin_gen{}); CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); DO_CALLBACK(events, "mark_invalid_tx"); ADD_CUSTOM_EVENT(events, tx); @@ -1750,7 +1750,6 @@ bool tx_pool_semantic_validation::generate(std::vector& events point_t point_public_key{}; txout_to_key target{}; std::array inputs{}; - tx_out_bare output{}; transaction tx{}; CHECK_AND_ASSERT_EQ(point_public_key.from_string("499790c3302b9f0514e2db09b390679283d43d971383d33dc24c7991ea4cf6d7"), true); @@ -1763,10 +1762,6 @@ bool tx_pool_semantic_validation::generate(std::vector& events tx.vin.push_back(input); } - output.amount = 1; - output.target = target; - tx.vout.push_back(output); - CHECK_AND_ASSERT_GREATER(inputs.at(0).amount, inputs.at(0).amount + inputs.at(1).amount); CHECK_AND_ASSERT_GREATER(inputs.at(1).amount, inputs.at(0).amount + inputs.at(1).amount); CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); @@ -1803,41 +1798,38 @@ bool tx_pool_semantic_validation::generate(std::vector& events // Equal key images in inputs. { - tx_out_bare output; + tx_out_bare output{}; transaction tx{}; std::array inputs{}; point_t key_image_point{}; - key_image image{}; output.amount = 1; tx.vout.push_back(output); CHECK_AND_ASSERT_EQ(key_image_point.from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true); - image = key_image_point.to_key_image(); for (int position{}; position < 2; ++position) { - inputs.at(position).k_image = image; - tx.vin.push_back(inputs.at(position)); + auto& input{inputs.at(position)}; + + input.k_image = key_image_point.to_key_image(); + tx.vin.push_back(input); } CHECK_AND_ASSERT_EQ(tx.vin.at(0).type(), typeid(txin_to_key)); CHECK_AND_ASSERT_EQ(tx.vin.at(0).type(), tx.vin.at(1).type()); CHECK_AND_ASSERT_EQ(boost::get(tx.vin.at(0)).k_image, boost::get(tx.vin.at(1)).k_image); CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); ADD_CUSTOM_EVENT(events, tx); } // Two entries of the same type in extra. { - tx_out_bare output; transaction tx{}; std::array inputs{}; std::array key_image_points{}; - key_image image{}; - output.amount = 1; - tx.vout.push_back(output); CHECK_AND_ASSERT_EQ(key_image_points.at(0).from_string("de3c22a62f15e6de8abe6b217085b2aead196daf5ddd67d9c4b366330736fbeb"), true); CHECK_AND_ASSERT_EQ(key_image_points.at(1).from_string("9f3eef913921ca35239e696725595e3686bb0d69e3e805791c5aa93d5754aa5c"), true); @@ -1859,8 +1851,8 @@ bool tx_pool_semantic_validation::generate(std::vector& events // tx.version <= TRANSACTION_VERSION_PRE_HF4. Balance check fail: sum of inputs <= sum of outputs. { + tx_out_bare output{}; transaction tx{}; - tx_out_bare output; std::array key_image_points{}; std::array inputs{}; @@ -1879,7 +1871,10 @@ bool tx_pool_semantic_validation::generate(std::vector& events tx.vin.push_back(input); } - CHECK_AND_ASSERT_GREATER(output.amount, std::accumulate(inputs.begin(), inputs.end(), std::uint64_t{}, [](uint64_t sum, const txin_to_key& input) { return sum + input.amount; })); + const uint64_t sum_inputs{std::accumulate(inputs.begin(), inputs.end(), std::uint64_t{}, [](uint64_t sum, const txin_to_key& input) { return sum + input.amount; })}; + + CHECK_AND_ASSERT_LESS(sum_inputs, output.amount); + CHECK_AND_ASSERT_EQ(output.amount - sum_inputs, 1); CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); DO_CALLBACK(events, "mark_invalid_tx"); ADD_CUSTOM_EVENT(events, tx); @@ -1911,5 +1906,201 @@ bool tx_pool_semantic_validation::generate(std::vector& events ADD_CUSTOM_EVENT(events, tx); } + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // Construct a valid transaction and then modify it so that the transaction is no longer semantically correct. + + // No inputs. + { + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + tx.vin = {}; + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // Unsupported input type. + { + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + tx.vin.emplace_back(txin_gen{}); + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // Unsupported output type. + { + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + tx.vout.emplace_back(); + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // Inputs amount overflow. + { + point_t point_public_key{}; + txout_to_key target{}; + std::array inputs{}; + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + CHECK_AND_ASSERT_EQ(point_public_key.from_string("499790c3302b9f0514e2db09b390679283d43d971383d33dc24c7991ea4cf6d7"), true); + target.key = point_public_key.to_public_key(); + inputs.at(0).amount = 1; + inputs.at(1).amount = UINT64_MAX; + + for (const auto& input : inputs) + { + tx.vin.push_back(input); + } + + CHECK_AND_ASSERT_GREATER(inputs.at(0).amount, inputs.at(0).amount + inputs.at(1).amount); + CHECK_AND_ASSERT_GREATER(inputs.at(1).amount, inputs.at(0).amount + inputs.at(1).amount); + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // Outputs amount overflow. + { + point_t point_public_key{}; + txout_to_key target{}; + std::array outputs{}; + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + CHECK_AND_ASSERT_EQ(point_public_key.from_string("78ef3d9af7b5e3d09556d57820cf68c2b3553a9d8205c01fe40fc70aae86bb4f"), true); + target.key = point_public_key.to_public_key(); + outputs.at(0).amount = 1; + outputs.at(1).amount = UINT64_MAX; + + for (auto& output : outputs) + { + output.target = target; + tx.vout.push_back(output); + } + + tx.vin.push_back(txin_to_key{}); + + CHECK_AND_ASSERT_GREATER(outputs.at(0).amount, outputs.at(0).amount + outputs.at(1).amount); + CHECK_AND_ASSERT_GREATER(outputs.at(1).amount, outputs.at(0).amount + outputs.at(1).amount); + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // Equal key images in inputs. + { + tx_out_bare output{}; + std::array inputs{}; + point_t key_image_point{}; + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + output.amount = 1; + tx.vout.push_back(output); + CHECK_AND_ASSERT_EQ(key_image_point.from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true); + + for (int position{}; position < 2; ++position) + { + inputs.at(position).k_image = key_image_point.to_key_image(); + tx.vin.push_back(inputs.at(position)); + } + + { + const auto& input_preceding_last{tx.vin.at(tx.vin.size() - 2u)}; + + CHECK_AND_ASSERT_EQ(tx.vin.back().type(), typeid(txin_to_key)); + CHECK_AND_ASSERT_EQ(tx.vin.back().type(), input_preceding_last.type()); + CHECK_AND_ASSERT_EQ(boost::get(tx.vin.back()).k_image, boost::get(input_preceding_last).k_image); + } + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // Two entries of the same type in extra. + { + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + tx.extra.push_back(null_pkey); + tx.extra.push_back(null_pkey); + + CHECK_AND_ASSERT_GREATER(tx.extra.size(), 2); + CHECK_AND_ASSERT_EQ(typeid(tx.extra.back()), typeid(tx.extra.at(tx.extra.size() - 2))); + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + + // tx.version <= TRANSACTION_VERSION_PRE_HF4. Balance check fail: sum of inputs <= sum of outputs. + { + tx_out_bare output{}; + std::array key_image_points{}; + std::array inputs{}; + MAKE_TX_FEE(events, tx, miner, miner, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, blk_0r); + + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), true); + output.amount = 10'000'000'003; + tx.vout.push_back(output); + tx.version = 0; + CHECK_AND_ASSERT_EQ(key_image_points.at(0).from_string("8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96"), true); + CHECK_AND_ASSERT_EQ(key_image_points.at(1).from_string("dc48b741dacda5ac026ad0a7d193b816049eb08724907a1ff6f95839cfb0efa5"), true); + + for (int position{}; position < 2; ++position) + { + auto& input{inputs.at(position)}; + + input.amount = 1; + input.k_image = key_image_points.at(position).to_key_image(); + tx.vin.push_back(input); + } + + const auto inputs_sum{[](const uint64_t sum, const txin_v& input) -> uint64_t + { + if (input.type() == typeid(txin_to_key)) + { + return sum + boost::get(input).amount; + } + + if (input.type() == typeid(txin_multisig)) + { + return sum + boost::get(input).amount; + } + } + }; + + const auto outputs_sum{[](const uint64_t sum, const tx_out_v& output) -> uint64_t + { + if (output.type() == typeid(tx_out_bare)) + { + return sum + boost::get(output).amount; + } + } + }; + + const uint64_t inputs_amount{std::accumulate(tx.vin.begin(), tx.vin.end(), std::uint64_t{}, inputs_sum)}; + const uint64_t outputs_amount{std::accumulate(tx.vout.begin(), tx.vout.end(), std::uint64_t{}, outputs_sum)}; + + CHECK_AND_ASSERT_LESS(inputs_amount, outputs_amount); + CHECK_AND_ASSERT_EQ(outputs_amount - inputs_amount, 1); + CHECK_AND_ASSERT(tx.version <= TRANSACTION_VERSION_PRE_HF4, false); + CHECK_AND_ASSERT_EQ(get_block_height(blk_0r), 10); + CHECK_AND_ASSERT_EQ(m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_03, 10), true); + CHECK_AND_ASSERT_EQ(m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, 10), false); + CHECK_AND_ASSERT_EQ(validate_tx_semantic(tx, CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1), false); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx); + } + return true; } diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 5a8f81cd..9888ee3e 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -161,7 +161,7 @@ struct tx_version_against_hardfork : public test_chain_unit_enchanced bool generate(std::vector& events) const; }; -struct tx_pool_semantic_validation : public test_chain_unit_enchanced +struct tx_pool_semantic_validation : public test_chain_unit_enchanced { bool generate(std::vector& events) const; };