diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 1e98aba1..89d6a5b9 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1222,6 +1222,7 @@ int main(int argc, char* argv[]) /* 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"); + GENERATE_AND_PLAY(input_refers_to_incompatible_by_type_output); // 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 14b3db62..dea895ea 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -2121,3 +2121,481 @@ bool tx_pool_semantic_validation::generate(std::vector& events return true; } + +input_refers_to_incompatible_by_type_output::input_refers_to_incompatible_by_type_output() +{ + REGISTER_CALLBACK_METHOD(input_refers_to_incompatible_by_type_output, assert_htlc_input_refers_to_key_output_is_wrong); + REGISTER_CALLBACK_METHOD(input_refers_to_incompatible_by_type_output, assert_to_key_input_refers_zarcanum_output_is_wrong); + REGISTER_CALLBACK_METHOD(input_refers_to_incompatible_by_type_output, assert_htlc_input_refers_zarcanum_output_is_wrong); + REGISTER_CALLBACK_METHOD(input_refers_to_incompatible_by_type_output, assert_zc_input_refers_bare_output_is_wrong); + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 14); +} + +bool input_refers_to_incompatible_by_type_output::generate(std::vector& events) const +{ + // Test idea: ensure that input and output compatibility checks work. + + GENERATE_ACCOUNT(miner); + MAKE_GENESIS_BLOCK(events, blk_0, miner, test_core_time::get_time()); + + DO_CALLBACK(events, "configure_core"); + + REWIND_BLOCKS(events, blk_0r, blk_0, miner); + block& top{blk_0r}; + MAKE_TX_FEE(events, tx_00, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk_1, top, miner, tx_00); + top = blk_1; + } + + // An input of the type "txin_htlc" refers by a "ref_by_id" object to an output with a target of the type "txout_to_key". + { + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + transaction tx_1{}; + + top = blk; + + { + txin_htlc input{}; + + { + ref_by_id reference{}; + + reference.tx_id = get_transaction_hash(tx_0); + reference.n = get_tx_out_index_by_amount(tx_0, MK_TEST_COINS(7)); + CHECK_AND_ASSERT_NEQ(reference.n, UINT64_MAX); + input.key_offsets.push_back(reference); + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 1); + } + + input.k_image = crypto::point_t{{0x59, 0x01, 0xed, 0xcc, 0x3a, 0xe7, 0xaa, 0x83, 0x6c, 0x79, 0xfb, 0xed, 0x5d, 0x60, 0x02, 0xc5, 0xd0, 0xbf, 0x65, 0x85, 0x7b, 0x65, 0x25, 0x0e, 0x22, 0xcb, 0x63, + 0x64, 0x3b, 0x3b, 0x47, 0x30}}.to_key_image(); + input.amount = MK_TEST_COINS(7); + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + { + tx_out_bare output_bare{}; + txout_to_key output_to_key{}; + + output_bare.amount = MK_TEST_COINS(2); + output_to_key.key = crypto::point_t{{0x2c, 0xdc, 0xc4, 0x7c, 0x38, 0x69, 0xe5, 0xe2, 0x4c, 0x5e, 0x10, 0xb2, 0xbe, 0x57, 0xe9, 0x42, 0x72, 0xd8, 0xf8, 0xb5, 0x97, 0xb9, 0x02, 0x41, 0xba, 0xea, + 0x82, 0xb3, 0xaf, 0x0c, 0xf0, 0x09}}.to_public_key(); + output_bare.target = output_to_key; + tx_1.vout.push_back(output_bare); + CHECK_AND_ASSERT_EQ(tx_1.vout.size(), 1); + } + + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_htlc_input_refers_to_key_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + // An input of the type "txin_htlc" refers by a global output index to an output with a target of the type "txout_to_key". + { + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + transaction tx_1{}; + + top = blk; + + { + txin_htlc input{}; + + { + uint64_t global_output_index{}; + + CHECK_AND_ASSERT_EQ(find_global_index_for_output(events, get_block_hash(top), tx_0, get_tx_out_index_by_amount(tx_0, MK_TEST_COINS(7)), global_output_index), true); + CHECK_AND_ASSERT_NEQ(global_output_index, UINT64_MAX); + input.key_offsets.push_back(global_output_index); + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 1); + } + + input.k_image = crypto::point_t{{0xc6, 0x1c, 0xda, 0xf7, 0x9e, 0xb7, 0xd9, 0xc2, 0x46, 0x90, 0x29, 0xc8, 0x8a, 0x8f, 0xb4, 0x3e, 0x8e, 0xa8, 0x3b, 0x33, 0x4c, 0x75, 0xdf, 0xcb, 0x8b, 0x77, 0xf7, + 0x39, 0xa7, 0x17, 0xc9, 0xb4}}.to_key_image(); + input.amount = MK_TEST_COINS(7); + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + { + tx_out_bare output_bare{}; + txout_to_key output_to_key{}; + + output_bare.amount = MK_TEST_COINS(2); + output_to_key.key = crypto::point_t{{0xc4, 0x17, 0xc7, 0x7f, 0xb2, 0x5d, 0xcb, 0x4b, 0x29, 0xdf, 0xea, 0x53, 0x70, 0x11, 0xbb, 0x42, 0x33, 0x0d, 0xf1, 0x22, 0x2d, 0xe4, 0x84, 0x24, 0x36, 0xc0, + 0x06, 0xd5, 0x8c, 0xf8, 0x23, 0x62}}.to_public_key(); + output_bare.target = output_to_key; + tx_1.vout.push_back(output_bare); + } + + tx_1.signatures.push_back(NLSAG_sig{{crypto::signature{}}}); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_htlc_input_refers_to_key_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + DO_CALLBACK_PARAMS(events, "check_hardfork_inactive", size_t{ZANO_HARDFORK_04_ZARCANUM}); + + { + REWIND_BLOCKS_N(events, blk, top, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + top = blk; + } + + DO_CALLBACK_PARAMS(events, "check_hardfork_active", size_t{ZANO_HARDFORK_04_ZARCANUM}); + MAKE_TX_FEE(events, tx_01, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_01); + top = blk; + } + + MAKE_TX_FEE(events, tx_02, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_02); + top = blk; + } + + // An input of the type "txin_to_key" refers by a "ref_by_id" object to an output of the type "tx_out_zarcanum". + { + DO_CALLBACK_PARAMS(events, "check_hardfork_active", size_t{ZANO_HARDFORK_04_ZARCANUM}); + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + top = blk; + } + + { + REWIND_BLOCKS(events, blk_r, top, miner); + top = blk_r; + } + + transaction tx_1{}; + + tx_1.version = 2; + + { + txin_to_key input{}; + + input.key_offsets.emplace_back(ref_by_id{get_transaction_hash(tx_0), 0}); + CHECK_AND_ASSERT_EQ(tx_0.vout.at(boost::get(input.key_offsets.front()).n).type(), typeid(tx_out_zarcanum)); + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 1); + input.k_image = crypto::point_t({0x59,0x01, 0xed, 0xcc, 0x3a, 0xe7, 0xaa, 0x83, 0x6c, 0x79, 0xfb, 0xed, 0x5d, 0x60, 0x02, 0xc5, 0xd0, 0xbf, 0x65, 0x85, 0x7b, 0x65, 0x25, 0x0e, 0x22, 0xcb, 0x63, + 0x64, 0x3b, 0x3b, 0x47, 0x30}).to_key_image(); + input.amount = MK_TEST_COINS(2); + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + tx_1.vout.push_back(tx_out_zarcanum{}); + tx_1.vout.push_back(tx_out_zarcanum{}); + CHECK_AND_ASSERT_EQ(tx_1.vout.size(), 2); + tx_1.extra.push_back(zarcanum_tx_data_v1{TESTS_DEFAULT_FEE}); + CHECK_AND_ASSERT_EQ(tx_1.extra.size(), 1); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_to_key_input_refers_zarcanum_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + // An input of the type "txin_to_key" refers by a global output index to an output of the type "tx_out_zarcanum". + { + DO_CALLBACK_PARAMS(events, "check_hardfork_active", size_t{ZANO_HARDFORK_04_ZARCANUM}); + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + top = blk; + } + + { + REWIND_BLOCKS(events, blk_r, top, miner); + top = blk_r; + } + + transaction tx_1{}; + + tx_1.version = 2; + + { + txin_to_key input{}; + + { + uint64_t global_output_index{}; + + CHECK_AND_ASSERT_EQ(tx_0.vout.front().type(), typeid(tx_out_zarcanum)); + CHECK_AND_ASSERT_EQ(find_global_index_for_output(events, get_block_hash(top), tx_0, 0, global_output_index), true); + input.key_offsets.push_back(global_output_index); + } + + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 1); + input.k_image = crypto::point_t{{0xbc, 0x2d, 0xdc, 0xc5, 0x93, 0x03, 0x9f, 0x0e, 0xce, 0x76, 0xee, 0xef, 0xd9, 0x1c, 0x2c, 0x3e, 0x8c, 0x4a, 0xca, 0x87, 0x9b, 0x6e, 0x3a, 0xda, 0xaf, 0x0c, + 0x92, 0x88, 0xda, 0x88, 0xc0, 0xf0}}.to_key_image(); + // A container is selected by an amount specified in an input. ZC outputs have .amount equals to 0. Thus, the input has .amount equals to 0. + input.amount = 0; + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + tx_1.vout.push_back(tx_out_zarcanum{}); + tx_1.vout.push_back(tx_out_zarcanum{}); + CHECK_AND_ASSERT_EQ(tx_1.vout.size(), 2); + tx_1.extra.push_back(zarcanum_tx_data_v1{TESTS_DEFAULT_FEE}); + CHECK_AND_ASSERT_EQ(tx_1.extra.size(), 1); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_to_key_input_refers_zarcanum_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + // An input of the type "txin_zc_input" refers by a "ref_by_id" object to an output with a target of the type "txout_to_key". + { + DO_CALLBACK_PARAMS(events, "check_hardfork_active", size_t{ZANO_HARDFORK_04_ZARCANUM}); + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + top = blk; + } + + { + REWIND_BLOCKS(events, blk_r, top, miner); + top = blk_r; + } + + transaction tx_1{}; + + tx_1.version = 2; + + { + txin_zc_input input{}; + + input.key_offsets.emplace_back(ref_by_id{get_transaction_hash(tx_00), 0}); + + { + const auto& output{tx_00.vout.at(boost::get(input.key_offsets.front()).n)}; + + CHECK_AND_ASSERT_EQ(output.type(), typeid(tx_out_bare)); + CHECK_AND_ASSERT_EQ(boost::get(output).target.type(), typeid(txout_to_key)); + } + + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 1); + input.key_offsets.emplace_back(ref_by_id{get_transaction_hash(tx_01), 0}); + + { + const auto& output{tx_01.vout.at(boost::get(input.key_offsets.at(1)).n)}; + + CHECK_AND_ASSERT_EQ(output.type(), typeid(tx_out_zarcanum)); + } + + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 2); + input.k_image = crypto::point_t{{0x31, 0x31, 0xd0, 0xf7, 0x13, 0x73, 0xff, 0x21, 0x14, 0xe8, 0x17, 0x4d, 0x18, 0x20, 0x12, 0x2d, 0x80, 0x31, 0xd5, 0x11, 0x82, 0xc0, 0x37, 0xad, 0xd2, 0x7b, 0x8c, + 0xf2, 0xdd, 0xd4, 0x34, 0x9a}}.to_key_image(); + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + tx_1.vout.push_back(tx_out_zarcanum{}); + tx_1.vout.push_back(tx_out_zarcanum{}); + CHECK_AND_ASSERT_EQ(tx_1.vout.size(), 2); + tx_1.extra.push_back(zarcanum_tx_data_v1{TESTS_DEFAULT_FEE}); + CHECK_AND_ASSERT_EQ(tx_1.extra.size(), 1); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_zc_input_refers_bare_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + // An input of the type "txin_htlc" refers by a "ref_by_id" object to an output of the type "tx_out_zarcanum". + { + DO_CALLBACK_PARAMS(events, "check_hardfork_active", size_t{ZANO_HARDFORK_04_ZARCANUM}); + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + top = blk; + } + + { + REWIND_BLOCKS_N_WITH_TIME(events, blk_r, top, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + top = blk_r; + } + + transaction tx_1{}; + + tx_1.version = 2; + + { + txin_htlc input{}; + + input.key_offsets.push_back(ref_by_id{get_transaction_hash(tx_0), 0}); + CHECK_AND_ASSERT_EQ(tx_0.vout.at(boost::get(input.key_offsets.front()).n).type(), typeid(tx_out_zarcanum)); + input.k_image = crypto::point_t{{0x7b, 0xf5, 0x28, 0x09, 0xe8, 0x7e, 0x9c, 0x71, 0x0b, 0xad, 0x24, 0xa1, 0x9d, 0xb4, 0xc8, 0xd7, 0x96, 0x72, 0x18, 0xe6, 0x4b, 0x8f, 0x31, 0x01, 0xb0, 0x43, 0xa0, + 0xcc, 0xce, 0x72, 0x8c, 0x7e}}.to_key_image(); + input.amount = MK_TEST_COINS(2); + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + tx_1.vout.push_back(tx_out_zarcanum{}); + tx_1.vout.push_back(tx_out_zarcanum{}); + CHECK_AND_ASSERT_EQ(tx_1.vout.size(), 2); + tx_1.extra.push_back(zarcanum_tx_data_v1{TESTS_DEFAULT_FEE}); + CHECK_AND_ASSERT_EQ(tx_1.extra.size(), 1); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_htlc_input_refers_zarcanum_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + // An input of the type "txin_htlc" refers by a global output index to an output of the type "tx_out_zarcanum". + { + MAKE_TX_FEE(events, tx_0, miner, miner, MK_TEST_COINS(2), TESTS_DEFAULT_FEE, top); + + { + MAKE_NEXT_BLOCK_TX1(events, blk, top, miner, tx_0); + top = blk; + } + + DO_CALLBACK_PARAMS(events, "check_hardfork_active", size_t{ZANO_HARDFORK_04_ZARCANUM}); + + { + REWIND_BLOCKS_N_WITH_TIME(events, blk_r, top, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + top = blk_r; + } + + transaction tx_1{}; + + tx_1.version = 2; + + { + txin_htlc input{}; + uint64_t global_output_index{}; + + CHECK_AND_ASSERT_EQ(tx_0.vout.front().type(), typeid(tx_out_zarcanum)); + CHECK_AND_ASSERT_EQ(find_global_index_for_output(events, get_block_hash(top), tx_0, 0, global_output_index), true); + input.key_offsets.push_back(global_output_index); + CHECK_AND_ASSERT_EQ(input.key_offsets.size(), 1); + input.k_image = crypto::point_t{{0xbc, 0x2d, 0xdc, 0xc5, 0x93, 0x03, 0x9f, 0x0e, 0xce, 0x76, 0xee, 0xef, 0xd9, 0x1c, 0x2c, 0x3e, 0x8c, 0x4a, 0xca, 0x87, 0x9b, 0x6e, 0x3a, 0xda, 0xaf, 0x0c, 0x92, + 0x88, 0xda, 0x88, 0xc0, 0xf0}}.to_key_image(); + // A container is selected by an amount specified in an input. ZC outputs have .amount equals to 0. Thus, the input has .amount equals to 0. + input.amount = 0; + tx_1.vin.push_back(input); + CHECK_AND_ASSERT_EQ(tx_1.vin.size(), 1); + } + + tx_1.vout.push_back(tx_out_zarcanum{}); + tx_1.vout.push_back(tx_out_zarcanum{}); + CHECK_AND_ASSERT_EQ(tx_1.vout.size(), 2); + tx_1.extra.push_back(zarcanum_tx_data_v1{TESTS_DEFAULT_FEE}); + CHECK_AND_ASSERT_EQ(tx_1.extra.size(), 1); + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + DO_CALLBACK_PARAMS_STR(events, "assert_htlc_input_refers_zarcanum_output_is_wrong", t_serializable_object_to_blob(tx_1)); + } + + return true; +} + +bool input_refers_to_incompatible_by_type_output::assert_htlc_input_refers_to_key_output_is_wrong(const currency::core& c, size_t ev_index, const std::vector& events) const +{ + transaction tx{}; + + CHECK_AND_ASSERT_EQ(t_unserializable_object_from_blob(tx, boost::get(events.at(ev_index)).callback_params), true); + CHECK_AND_ASSERT_EQ(tx.vin.front().type(), typeid(txin_htlc)); + + { + uint64_t max_related_block_height{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().check_tx_input(tx, 0, boost::get(tx.vin.front()), get_transaction_hash(tx), max_related_block_height), false); + } + + { + std::vector keys{}; + uint64_t max_related_block_height{}; + uint64_t source_max_unlock_time_for_pos_coinbase{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().get_output_keys_for_input_with_checks(tx, tx.vin.front(), keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase), false); + } + + return true; +} + +bool input_refers_to_incompatible_by_type_output::assert_to_key_input_refers_zarcanum_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const +{ + transaction tx{}; + + CHECK_AND_ASSERT_EQ(t_unserializable_object_from_blob(tx, boost::get(events.at(ev_index)).callback_params), true); + CHECK_AND_ASSERT_EQ(tx.vin.front().type(), typeid(txin_to_key)); + + { + uint64_t max_related_block_height{}; + uint64_t source_max_unlock_time_for_pos_coinbase{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().check_tx_input(tx, 0, boost::get(tx.vin.front()), get_transaction_hash(tx), max_related_block_height, + source_max_unlock_time_for_pos_coinbase), false); + } + + { + std::vector keys{}; + uint64_t max_related_block_height{}; + uint64_t source_max_unlock_time_for_pos_coinbase{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().get_output_keys_for_input_with_checks(tx, tx.vin.front(), keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase), false); + } + + return true; +} + +bool input_refers_to_incompatible_by_type_output::assert_zc_input_refers_bare_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const +{ + transaction tx{}; + + CHECK_AND_ASSERT_EQ(t_unserializable_object_from_blob(tx, boost::get(events.at(ev_index)).callback_params), true); + CHECK_AND_ASSERT_EQ(tx.vin.front().type(), typeid(txin_zc_input)); + + { + std::vector keys{}; + uint64_t max_related_block_height{}; + uint64_t source_max_unlock_time_for_pos_coinbase{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().get_output_keys_for_input_with_checks(tx, tx.vin.front(), keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase), true); + } + + { + bool all_inputs_have_explicit_native_asset_id{}; + uint64_t max_related_block_height{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().check_tx_input(tx, 0, boost::get(tx.vin.front()), get_transaction_hash(tx), max_related_block_height, + all_inputs_have_explicit_native_asset_id), false); + } + + return true; +} + +bool input_refers_to_incompatible_by_type_output::assert_htlc_input_refers_zarcanum_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const +{ + transaction tx{}; + + CHECK_AND_ASSERT_EQ(t_unserializable_object_from_blob(tx, boost::get(events.at(ev_index)).callback_params), true); + CHECK_AND_ASSERT_EQ(tx.vin.front().type(), typeid(txin_htlc)); + + { + uint64_t max_related_block_height{}; + uint64_t source_max_unlock_time_for_pos_coinbase{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().check_tx_input(tx, 0, boost::get(tx.vin.front()), get_transaction_hash(tx), max_related_block_height, + source_max_unlock_time_for_pos_coinbase), false); + } + + { + std::vector keys{}; + uint64_t max_related_block_height{}; + uint64_t source_max_unlock_time_for_pos_coinbase{}; + + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().get_output_keys_for_input_with_checks(tx, tx.vin.front(), keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase), false); + } + + return true; +} diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 9888ee3e..6e3ae56e 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -165,3 +165,13 @@ struct tx_pool_semantic_validation : public test_chain_unit_enchanced { bool generate(std::vector& events) const; }; + +struct input_refers_to_incompatible_by_type_output : public test_chain_unit_enchanced +{ + input_refers_to_incompatible_by_type_output(); + bool generate(std::vector& events) const; + bool assert_htlc_input_refers_to_key_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const; + bool assert_to_key_input_refers_zarcanum_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const; + bool assert_zc_input_refers_bare_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const; + bool assert_htlc_input_refers_zarcanum_output_is_wrong(const currency::core& c, const size_t ev_index, const std::vector& events) const; +};