Core Tests TX Validation (#337)

* Fix gen_tx_unlock_time, gen_tx_big_version

* Fix gen_tx_input_is_not_txin_to_key

* Change convention for block naming in working tests

* Fix gen_tx_has_inputs_no_outputs, invalid_input_amount

* Fix gen_tx_input_wo_key_offsets

* Add test note about gen_tx_mixed_key_offset_not_exist

* Uncomment tests

* Unbreak broken gx_ten tests

* Fix gen_tx_key_image_not_derive_from_tx_key

* Fix gen_tx_key_image_is_invalid

* Fix gen_tx_txout_to_key_has_invalid_key

* Add note for gen_tx_output_with_zero_amount

* Fix gen_tx_signatures_are_invalid
This commit is contained in:
Doyle 2018-11-22 13:00:15 +11:00 committed by GitHub
parent 3adec6d661
commit 713adfe92c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 304 additions and 324 deletions

View File

@ -430,7 +430,7 @@ class TxBuilder {
/// required fields
const std::vector<test_event_entry>& m_events;
const uint8_t m_hf_version;
const size_t m_hf_version;
cryptonote::transaction& m_tx;
const cryptonote::block& m_head;
const cryptonote::account_base& m_from;
@ -951,7 +951,7 @@ cryptonote::transaction make_deregistration_tx(const std::vector<test_event_entr
// NOTE(loki): These macros assume hardfork version 7 and are from the old Monero testing code
#define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
cryptonote::transaction TX_NAME; \
TxBuilder(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, cryptonote::network_version_7).build(); \
TxBuilder(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, cryptonote::network_version_7).build(); \
VEC_EVENTS.push_back(TX_NAME);
#define MAKE_TX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, 9, HEAD)

View File

@ -157,8 +157,25 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_block_has_invalid_tx);
GENERATE_AND_PLAY(gen_block_is_too_big);
// TODO(loki): We also want to run these tx tests on deregistration tx's
// as well because they special case and run under very different code
// paths from the regular tx path
// Transaction verification tests
GENERATE_AND_PLAY(gen_tx_big_version);
GENERATE_AND_PLAY(gen_tx_unlock_time);
GENERATE_AND_PLAY(gen_tx_input_is_not_txin_to_key);
GENERATE_AND_PLAY(gen_tx_no_inputs_no_outputs);
GENERATE_AND_PLAY(gen_tx_no_inputs_has_outputs);
GENERATE_AND_PLAY(gen_tx_has_inputs_no_outputs);
GENERATE_AND_PLAY(gen_tx_invalid_input_amount);
GENERATE_AND_PLAY(gen_tx_input_wo_key_offsets);
GENERATE_AND_PLAY(gen_tx_key_offset_points_to_foreign_key);
GENERATE_AND_PLAY(gen_tx_sender_key_offset_not_exist); // TODO(loki): Revisit this test
GENERATE_AND_PLAY(gen_tx_key_image_not_derive_from_tx_key);
GENERATE_AND_PLAY(gen_tx_key_image_is_invalid);
GENERATE_AND_PLAY(gen_tx_txout_to_key_has_invalid_key);
GENERATE_AND_PLAY(gen_tx_output_is_not_txout_to_key);
GENERATE_AND_PLAY(gen_tx_signatures_are_invalid);
GENERATE_AND_PLAY(gen_multisig_tx_invalid_23_1__no_threshold);
GENERATE_AND_PLAY(gen_multisig_tx_invalid_45_5_23_no_threshold);
@ -187,6 +204,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount);
GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type);
}
// TODO(loki): Tests we need to fix
#if 0
@ -194,23 +212,8 @@ int main(int argc, char* argv[])
//GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10
// Transaction verification tests
GENERATE_AND_PLAY(gen_tx_big_version);
GENERATE_AND_PLAY(gen_tx_unlock_time);
GENERATE_AND_PLAY(gen_tx_input_is_not_txin_to_key);
GENERATE_AND_PLAY(gen_tx_no_inputs_has_outputs);
GENERATE_AND_PLAY(gen_tx_has_inputs_no_outputs);
GENERATE_AND_PLAY(gen_tx_invalid_input_amount);
GENERATE_AND_PLAY(gen_tx_input_wo_key_offsets);
GENERATE_AND_PLAY(gen_tx_sender_key_offest_not_exist);
GENERATE_AND_PLAY(gen_tx_key_offest_points_to_foreign_key);
GENERATE_AND_PLAY(gen_tx_mixed_key_offest_not_exist);
GENERATE_AND_PLAY(gen_tx_key_image_not_derive_from_tx_key);
GENERATE_AND_PLAY(gen_tx_key_image_is_invalid);
GENERATE_AND_PLAY(gen_tx_check_input_unlock_time);
GENERATE_AND_PLAY(gen_tx_txout_to_key_has_invalid_key);
GENERATE_AND_PLAY(gen_tx_output_with_zero_amount);
GENERATE_AND_PLAY(gen_tx_output_is_not_txout_to_key);
GENERATE_AND_PLAY(gen_tx_signatures_are_invalid);
GENERATE_AND_PLAY(gen_tx_mixed_key_offset_not_exist); // TODO(loki): See comment in the function
GENERATE_AND_PLAY(gen_tx_output_with_zero_amount); // TODO(loki): See comment in the function
// Double spend
GENERATE_AND_PLAY(gen_double_spend_in_tx<false>);
@ -285,7 +288,6 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234);
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234_many_inputs);
#endif
}
el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error);
MLOG(level, "\nREPORT:");

View File

@ -40,7 +40,7 @@ namespace
{
struct tx_builder
{
void step1_init(size_t version = 1, uint64_t unlock_time = 0)
void step1_init(size_t version = 7, uint64_t unlock_time = 0)
{
m_tx.vin.clear();
m_tx.vout.clear();
@ -144,7 +144,7 @@ namespace
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init(1, unlock_time);
builder.step1_init(cryptonote::network_version_7, unlock_time);
builder.step2_fill_inputs(from.get_keys(), sources);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
@ -186,22 +186,18 @@ bool gen_tx_big_version::generate(std::vector<test_event_entry>& events) const
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N(events, blk_money_unlocked, blk_tail, miner_account, 30);
REWIND_BLOCKS(events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init(1 + 1, 0);
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources);
fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), -1).build();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
@ -210,41 +206,56 @@ bool gen_tx_unlock_time::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS_N(events, blk_1, blk_0, miner_account, 10);
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
auto make_tx_with_unlock_time = [&](uint64_t unlock_time) -> transaction
{
return make_simple_tx_with_unlock_time(events, blk_1, miner_account, miner_account, MK_COINS(1), unlock_time);
};
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::list<transaction> txs_0;
txs_0.push_back(make_tx_with_unlock_time(0));
events.push_back(txs_0.back());
transaction tx = {};
uint64_t unlock_time = 0;
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r) - 1));
events.push_back(txs_0.back());
tx = {};
unlock_time = get_block_height(blk_money_unlocked) - 1;
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r)));
events.push_back(txs_0.back());
tx = {};
unlock_time = get_block_height(blk_money_unlocked);
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r) + 1));
events.push_back(txs_0.back());
tx = {};
unlock_time = get_block_height(blk_money_unlocked) + 1;
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r) + 2));
events.push_back(txs_0.back());
tx = {};
unlock_time = get_block_height(blk_money_unlocked) + 2;
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
txs_0.push_back(make_tx_with_unlock_time(ts_start - 1));
events.push_back(txs_0.back());
tx = {};
unlock_time = ts_start - 1;
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
txs_0.push_back(make_tx_with_unlock_time(time(0) + 60 * 60));
events.push_back(txs_0.back());
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0);
tx = {};
unlock_time = time(0) + 60 * 60;
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build();
events.push_back(tx);
txs_0.push_back(tx);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_tmp, blk_money_unlocked, miner_account, txs_0);
return true;
}
@ -253,33 +264,27 @@ bool gen_tx_input_is_not_txin_to_key::generate(std::vector<test_event_entry>& ev
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
MAKE_NEXT_BLOCK(events, blk_tmp, blk_0r, miner_account);
MAKE_NEXT_BLOCK(events, blk_tmp, blk_head, miner_account);
events.pop_back();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(blk_tmp.miner_tx);
auto make_tx_with_input = [&](const txin_v& tx_input) -> transaction
{
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.m_tx.vin.push_back(tx_input);
builder.step3_fill_outputs(destinations);
return builder.m_tx;
};
DO_CALLBACK(events, "mark_invalid_tx");
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vin.push_back(txin_to_script());
events.push_back(tx);
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(make_tx_with_input(txin_to_script()));
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(make_tx_with_input(txin_to_scripthash()));
tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vin.push_back(txin_to_scripthash());
events.push_back(tx);
return true;
}
@ -291,12 +296,12 @@ bool gen_tx_no_inputs_no_outputs::generate(std::vector<test_event_entry>& events
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
tx_builder builder;
builder.step1_init();
transaction tx = {};
tx.version = cryptonote::network_version_7;
add_tx_pub_key_to_extra(tx, keypair::generate(hw::get_device("default")).pub);
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
@ -304,20 +309,17 @@ bool gen_tx_no_inputs_has_outputs::generate(std::vector<test_event_entry>& event
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step3_fill_outputs(destinations);
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vin.clear();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
@ -325,25 +327,17 @@ bool gen_tx_has_inputs_no_outputs::generate(std::vector<test_event_entry>& event
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
destinations.clear();
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources);
events.push_back(builder.m_tx);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, builder.m_tx);
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vout.clear();
DO_CALLBACK(events, "mark_invalid_tx"); // NOTE(loki): This used to be valid in Monero pre RCT, but not anymore with our transactions because we start with RCT type TXs
events.push_back(tx);
return true;
}
@ -352,24 +346,22 @@ bool gen_tx_invalid_input_amount::generate(std::vector<test_event_entry>& events
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
uint64_t change_amount;
fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_TX_DEFAULT_MIX, sources, destinations, &change_amount);
sources.front().amount++;
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources);
transaction tx = {};
cryptonote::tx_destination_entry change_addr{ change_amount, miner_account.get_keys().m_account_address, false /*is_subaddress*/ };
assert(cryptonote::construct_tx(miner_account.get_keys(), sources, destinations, change_addr, {} /*tx_extra*/, tx, 0 /*unlock_time*/, cryptonote::network_version_7, false /*is_staking*/));
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
@ -378,100 +370,94 @@ bool gen_tx_input_wo_key_offsets::generate(std::vector<test_event_entry>& events
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_TX_DEFAULT_MIX, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
txin_to_key& in_to_key = boost::get<txin_to_key>(builder.m_tx.vin.front());
uint64_t key_offset = in_to_key.key_offsets.front();
in_to_key.key_offsets.pop_back();
CHECK_AND_ASSERT_MES(in_to_key.key_offsets.empty(), false, "txin contained more than one key_offset");
builder.step4_calc_hash();
in_to_key.key_offsets.push_back(key_offset);
builder.step5_sign(sources);
in_to_key.key_offsets.pop_back();
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin.front());
while (!in_to_key.key_offsets.empty())
in_to_key.key_offsets.pop_back();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
bool gen_tx_key_offest_points_to_foreign_key::generate(std::vector<test_event_entry>& events) const
bool gen_tx_key_offset_points_to_foreign_key::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_1, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_2, blk_1, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account);
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
MAKE_ACCOUNT(events, alice_account);
MAKE_ACCOUNT(events, bob_account);
MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(15) + 1, blk_1);
MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(15) + 1, blk_1);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0);
MAKE_ACCOUNT (events, alice_account);
MAKE_ACCOUNT (events, bob_account);
std::vector<tx_source_entry> sources_bob;
std::vector<tx_destination_entry> destinations_bob;
fill_tx_sources_and_destinations(events, blk_2, bob_account, miner_account, MK_COINS(15) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, 0, sources_bob, destinations_bob);
MAKE_TX_LIST_START (events, txs_0, miner_account, bob_account, MK_COINS(15) + 1, blk_2);
MAKE_TX_LIST (events, txs_0, miner_account, alice_account, MK_COINS(15) + 1, blk_2);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_money_unlocked, blk_2, miner_account, txs_0);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources_alice;
transaction bob_tx = {};
TxBuilder(events, bob_tx, blk_money_unlocked, bob_account, miner_account, MK_COINS(15) + 1 - TESTS_DEFAULT_FEE, cryptonote::network_version_7).with_fee(TESTS_DEFAULT_FEE).build();
std::vector<tx_source_entry> sources_alice;
std::vector<tx_destination_entry> destinations_alice;
fill_tx_sources_and_destinations(events, blk_2, alice_account, miner_account, MK_COINS(15) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, 0, sources_alice, destinations_alice);
fill_tx_sources_and_destinations(events, blk_money_unlocked, alice_account, miner_account, MK_COINS(15) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, CRYPTONOTE_TX_DEFAULT_MIX, sources_alice, destinations_alice);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(bob_account.get_keys(), sources_bob);
txin_to_key& in_to_key = boost::get<txin_to_key>(builder.m_tx.vin.front());
in_to_key.key_offsets.front() = sources_alice.front().outputs.front().first;
builder.step3_fill_outputs(destinations_bob);
builder.step4_calc_hash();
builder.step5_sign(sources_bob);
txin_to_key& bob_in_to_key = boost::get<txin_to_key>(bob_tx.vin.front());
bob_in_to_key.key_offsets.front() = sources_alice.front().outputs.back().first;
// TODO(loki): This used to be first(), but in the debugger bob's front() is
// 0 and alice's front() is 0 .. sooo ?? Reassigning the first offset
// wouldn't change the test. Now this test returns the same error as
// gen_tx_sender_key_offset_not_exist so I don't think this test is correct
// using back().
// bob_in_to_key.key_offsets.front() = sources_alice.front().outputs.first().first;
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(bob_tx);
return true;
}
bool gen_tx_sender_key_offest_not_exist::generate(std::vector<test_event_entry>& events) const
bool gen_tx_sender_key_offset_not_exist::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
txin_to_key& in_to_key = boost::get<txin_to_key>(builder.m_tx.vin.front());
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin.front());
in_to_key.key_offsets.front() = std::numeric_limits<uint64_t>::max();
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources);
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
bool gen_tx_mixed_key_offest_not_exist::generate(std::vector<test_event_entry>& events) const
bool gen_tx_mixed_key_offset_not_exist::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
// TODO(loki): This test looks broken. step2_fill_inputs calls
// generate_key_image_helper() which returns false and doesn't write to the TX
// if it fails. This test fails and early outs before the the key image is
// even made so, we can't really test putting this onto the chain? This would
// be more like a unit test.
// Monero version
#if 0
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account);
@ -497,76 +483,87 @@ bool gen_tx_mixed_key_offest_not_exist::generate(std::vector<test_event_entry>&
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
#endif
// Loki version
#if 0
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_1, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_2, blk_1, miner_account);
MAKE_ACCOUNT (events, alice_account);
MAKE_ACCOUNT (events, bob_account);
MAKE_TX_LIST_START (events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_2);
MAKE_TX_LIST (events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_2);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_money_unlocked, blk_2, miner_account, txs_0);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
uint64_t change_amount;
fill_tx_sources_and_destinations(events, blk_money_unlocked, bob_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_TX_DEFAULT_MIX, sources, destinations, &change_amount);
sources.front().outputs[(sources.front().real_output + 1) % 2].first = std::numeric_limits<uint64_t>::max();
transaction tx = {};
cryptonote::tx_destination_entry change_addr{ change_amount, miner_account.get_keys().m_account_address, false /*is_subaddress*/ };
assert(cryptonote::construct_tx(miner_account.get_keys(), sources, destinations, change_addr, {} /*tx_extra*/, tx, 0 /*unlock_time*/, cryptonote::network_version_7, false /*is_staking*/));
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(tx);
#endif
return true;
}
bool gen_tx_key_image_not_derive_from_tx_key::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin.front());
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
txin_to_key& in_to_key = boost::get<txin_to_key>(builder.m_tx.vin.front());
keypair kp = keypair::generate(hw::get_device("default"));
key_image another_ki;
crypto::generate_key_image(kp.pub, kp.sec, another_ki);
in_to_key.k_image = another_ki;
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
// Use fake key image
keypair keys = keypair::generate(hw::get_device("default"));
key_image fake_key_image;
crypto::generate_key_image(keys.pub, keys.sec, fake_key_image);
in_to_key.k_image = fake_key_image;
// Tx with invalid key image can't be subscribed, so create empty signature
builder.m_tx.signatures.resize(1);
builder.m_tx.signatures[0].resize(1);
builder.m_tx.signatures[0][0] = boost::value_initialized<crypto::signature>();
tx.signatures.resize(1);
tx.signatures[0].resize(1);
tx.signatures[0][0] = boost::value_initialized<crypto::signature>();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
bool gen_tx_key_image_is_invalid::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
txin_to_key& in_to_key = boost::get<txin_to_key>(builder.m_tx.vin.front());
in_to_key.k_image = generate_invalid_key_image();
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin.front());
in_to_key.k_image = generate_invalid_key_image();
// Tx with invalid key image can't be subscribed, so create empty signature
builder.m_tx.signatures.resize(1);
builder.m_tx.signatures[0].resize(1);
builder.m_tx.signatures[0][0] = boost::value_initialized<crypto::signature>();
tx.signatures.resize(1);
tx.signatures[0].resize(1);
tx.signatures[0][0] = boost::value_initialized<crypto::signature>();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
@ -576,10 +573,10 @@ bool gen_tx_check_input_unlock_time::generate(std::vector<test_event_entry>& eve
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(miner_account);
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS_N(events, blk_1, blk_0, miner_account, tests_count - 1);
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
REWIND_BLOCKS_N (events, blk_1, blk_0, miner_account, tests_count - 1);
REWIND_BLOCKS (events, blk_1r, blk_1, miner_account);
std::array<account_base, tests_count> accounts;
for (size_t i = 0; i < tests_count; ++i)
@ -634,155 +631,136 @@ bool gen_tx_check_input_unlock_time::generate(std::vector<test_event_entry>& eve
bool gen_tx_txout_to_key_has_invalid_key::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
txout_to_key& out_to_key = boost::get<txout_to_key>(builder.m_tx.vout.front().target);
out_to_key.key = generate_invalid_pub_key();
builder.step4_calc_hash();
builder.step5_sign(sources);
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
txout_to_key& out_to_key = boost::get<txout_to_key>(tx.vout.front().target);
out_to_key.key = generate_invalid_pub_key();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
bool gen_tx_output_with_zero_amount::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
std::vector<tx_source_entry> sources;
// TODO(loki): Hmm. Can't use TxBuilder approach because RCT masks amounts
// after it's constructed, so vout amounts is already zero. It seems to be
// valid to be able to send a transaction whos output is zero, so this test
// might not be valid anymore post RCT.
#if 1
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
uint64_t change_amount;
fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_TX_DEFAULT_MIX, sources, destinations, &change_amount);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
for (tx_destination_entry &entry : destinations)
entry.amount = 0;
builder.m_tx.vout.front().amount = 0;
transaction tx = {};
cryptonote::tx_destination_entry change_addr{ change_amount, miner_account.get_keys().m_account_address, false /*is_subaddress*/ };
assert(cryptonote::construct_tx(miner_account.get_keys(), sources, destinations, change_addr, {} /*tx_extra*/, tx, 0 /*unlock_time*/, cryptonote::network_version_7, false /*is_staking*/));
builder.step4_calc_hash();
builder.step5_sign(sources);
#else
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vout.front().amount = 0;
#endif
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
bool gen_tx_output_is_not_txout_to_key::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_money_unlocked, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.m_tx.vout.push_back(tx_out());
builder.m_tx.vout.back().amount = 1;
builder.m_tx.vout.back().target = txout_to_script();
builder.step4_calc_hash();
builder.step5_sign(sources);
transaction tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vout.back().target = txout_to_script();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
builder.step1_init();
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.m_tx.vout.push_back(tx_out());
builder.m_tx.vout.back().amount = 1;
builder.m_tx.vout.back().target = txout_to_scripthash();
builder.step4_calc_hash();
builder.step5_sign(sources);
tx = {};
TxBuilder(events, tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(1), cryptonote::network_version_7).build();
tx.vout.back().target = txout_to_scripthash();
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(builder.m_tx);
events.push_back(tx);
return true;
}
bool gen_tx_signatures_are_invalid::generate(std::vector<test_event_entry>& events) const
{
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT (miner_account);
MAKE_GENESIS_BLOCK(events, blk_tail, miner_account, ts_start);
REWIND_BLOCKS_N (events, blk_1, blk_tail, miner_account, 40);
REWIND_BLOCKS (events, blk_2, blk_1, miner_account);
GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account);
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
MAKE_ACCOUNT(events, alice_account);
MAKE_ACCOUNT(events, bob_account);
MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1);
MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0);
MAKE_ACCOUNT (events, alice_account);
MAKE_ACCOUNT (events, bob_account);
MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(60), blk_2);
events.pop_back();
MAKE_TX_LIST_START (events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_2);
MAKE_TX_LIST (events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_2);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_money_unlocked, blk_2, miner_account, txs_0);
REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account);
MAKE_TX_MIX(events, tx_1, bob_account, miner_account, MK_COINS(1), 1, blk_2);
events.pop_back();
transaction miner_tx = {};
TxBuilder(events, miner_tx, blk_money_unlocked, miner_account, miner_account, MK_COINS(60), cryptonote::network_version_7).with_fee(TESTS_DEFAULT_FEE).build();
// Tx with nmix = 0 without signatures
// TX without signatures
DO_CALLBACK(events, "mark_invalid_tx");
blobdata sr_tx = t_serializable_object_to_blob(static_cast<transaction_prefix>(tx_0));
blobdata sr_tx = t_serializable_object_to_blob(static_cast<transaction_prefix>(miner_tx));
events.push_back(serialized_transaction(sr_tx));
// Tx with nmix = 0 have a few inputs, and not enough signatures
// TX have a few inputs, and not enough signatures
DO_CALLBACK(events, "mark_invalid_tx");
sr_tx = t_serializable_object_to_blob(tx_0);
sr_tx = t_serializable_object_to_blob(miner_tx);
sr_tx.resize(sr_tx.size() - sizeof(crypto::signature));
events.push_back(serialized_transaction(sr_tx));
// Tx with nmix = 0 have a few inputs, and too many signatures
// TX have a few inputs, and too many signatures
DO_CALLBACK(events, "mark_invalid_tx");
sr_tx = t_serializable_object_to_blob(tx_0);
sr_tx = t_serializable_object_to_blob(miner_tx);
sr_tx.insert(sr_tx.end(), sr_tx.end() - sizeof(crypto::signature), sr_tx.end());
events.push_back(serialized_transaction(sr_tx));
// Tx with nmix = 1 without signatures
transaction bob_tx = {};
TxBuilder(events, bob_tx, blk_money_unlocked, bob_account, miner_account, MK_COINS(1), cryptonote::network_version_7).with_fee(TESTS_DEFAULT_FEE).build();
// TX without signatures
DO_CALLBACK(events, "mark_invalid_tx");
sr_tx = t_serializable_object_to_blob(static_cast<transaction_prefix>(tx_1));
sr_tx = t_serializable_object_to_blob(static_cast<transaction_prefix>(bob_tx));
events.push_back(serialized_transaction(sr_tx));
// Tx with nmix = 1 have not enough signatures
// TX have not enough signatures
DO_CALLBACK(events, "mark_invalid_tx");
sr_tx = t_serializable_object_to_blob(tx_1);
sr_tx = t_serializable_object_to_blob(bob_tx);
sr_tx.resize(sr_tx.size() - sizeof(crypto::signature));
events.push_back(serialized_transaction(sr_tx));
// Tx with nmix = 1 have too many signatures
// TX have too many signatures
DO_CALLBACK(events, "mark_invalid_tx");
sr_tx = t_serializable_object_to_blob(tx_1);
sr_tx = t_serializable_object_to_blob(bob_tx);
sr_tx.insert(sr_tx.end(), sr_tx.end() - sizeof(crypto::signature), sr_tx.end());
events.push_back(serialized_transaction(sr_tx));
return true;
}

View File

@ -114,17 +114,17 @@ struct gen_tx_input_wo_key_offsets : public get_tx_validation_base
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_tx_key_offest_points_to_foreign_key : public get_tx_validation_base
struct gen_tx_key_offset_points_to_foreign_key : public get_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_tx_sender_key_offest_not_exist : public get_tx_validation_base
struct gen_tx_sender_key_offset_not_exist : public get_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct gen_tx_mixed_key_offest_not_exist : public get_tx_validation_base
struct gen_tx_mixed_key_offset_not_exist : public get_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};