From 106e3dc3d4151ff2b9836ead3177892de92df2ce Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 27 Jun 2016 18:56:19 +0100 Subject: [PATCH] Add rct core tests --- tests/core_tests/CMakeLists.txt | 6 +- tests/core_tests/chaingen.cpp | 11 +- tests/core_tests/chaingen.h | 9 +- tests/core_tests/chaingen_main.cpp | 27 ++ tests/core_tests/chaingen_tests_list.h | 1 + tests/core_tests/rct.cpp | 469 +++++++++++++++++++++++++ tests/core_tests/rct.h | 251 +++++++++++++ 7 files changed, 764 insertions(+), 10 deletions(-) create mode 100644 tests/core_tests/rct.cpp create mode 100644 tests/core_tests/rct.h diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index c34039c17..6f07fbd25 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -39,7 +39,8 @@ set(core_tests_sources ring_signature_1.cpp transaction_tests.cpp tx_validation.cpp - v2_tests.cpp) + v2_tests.cpp + rct.cpp) set(core_tests_headers block_reward.h @@ -54,7 +55,8 @@ set(core_tests_headers ring_signature_1.h transaction_tests.h tx_validation.h - v2_tests.h) + v2_tests.h + rct.h) add_executable(coretests ${core_tests_sources} diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index a181c1d16..b7a1c3dec 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -94,11 +94,11 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl return get_already_generated_coins(blk_hash); } -void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_sizes, uint64_t already_generated_coins) +void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_sizes, uint64_t already_generated_coins, uint8_t hf_version) { const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); uint64_t block_reward; - get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, 1); + get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version); m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); } @@ -215,7 +215,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/, const transaction& miner_tx/* = transaction()*/, const std::vector& tx_hashes/* = std::vector()*/, - size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/) + size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/) { blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION; blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION; @@ -223,6 +223,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc blk.prev_id = actual_params & bf_prev_id ? prev_id : get_block_hash(prev_block); blk.tx_hashes = actual_params & bf_tx_hashes ? tx_hashes : std::vector(); max_outs = actual_params & bf_max_outs ? max_outs : 9999; + hf_version = actual_params & bf_hf_version ? hf_version : 1; size_t height = get_block_height(prev_block) + 1; uint64_t already_generated_coins = get_already_generated_coins(prev_block); @@ -236,7 +237,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc { size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx); // TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs)) + if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version)) return false; } @@ -245,7 +246,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(); fill_nonce(blk, a_diffic, height); - add_block(blk, txs_sizes, block_sizes, already_generated_coins); + add_block(blk, txs_sizes, block_sizes, already_generated_coins, hf_version); return true; } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 0e5dbb0e4..81cbc2b6a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -221,7 +221,8 @@ public: bf_miner_tx = 1 << 4, bf_tx_hashes = 1 << 5, bf_diffic = 1 << 6, - bf_max_outs = 1 << 7 + bf_max_outs = 1 << 7, + bf_hf_version= 1 << 8 }; void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; @@ -229,7 +230,8 @@ public: uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; uint64_t get_already_generated_coins(const cryptonote::block& blk) const; - void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_sizes, uint64_t already_generated_coins); + void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_sizes, uint64_t already_generated_coins, + uint8_t hf_version = 1); bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, std::vector& block_sizes, const std::list& tx_list); @@ -241,7 +243,8 @@ public: const cryptonote::account_base& miner_acc, int actual_params = bf_none, uint8_t major_ver = 0, uint8_t minor_ver = 0, uint64_t timestamp = 0, const crypto::hash& prev_id = crypto::hash(), const cryptonote::difficulty_type& diffic = 1, const cryptonote::transaction& miner_tx = cryptonote::transaction(), - const std::vector& tx_hashes = std::vector(), size_t txs_sizes = 0, size_t max_outs = 999); + const std::vector& tx_hashes = std::vector(), size_t txs_sizes = 0, size_t max_outs = 999, + uint8_t hf_version = 1); bool construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block, const cryptonote::account_base& miner_acc, const std::vector& tx_hashes, size_t txs_size); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index e20f7a152..fc93b2e74 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -173,6 +173,33 @@ int main(int argc, char* argv[]) // GENERATE_AND_PLAY(gen_v2_tx_unmixable_two); GENERATE_AND_PLAY(gen_v2_tx_dust); + GENERATE_AND_PLAY(gen_rct_tx_valid_from_pre_rct); + GENERATE_AND_PLAY(gen_rct_tx_valid_from_rct); + GENERATE_AND_PLAY(gen_rct_tx_valid_from_mixed); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_real_dest); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_real_mask); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_fake_dest); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_fake_mask); + GENERATE_AND_PLAY(gen_rct_tx_rct_bad_real_dest); + GENERATE_AND_PLAY(gen_rct_tx_rct_bad_real_mask); + GENERATE_AND_PLAY(gen_rct_tx_rct_bad_fake_dest); + GENERATE_AND_PLAY(gen_rct_tx_rct_bad_fake_mask); + GENERATE_AND_PLAY(gen_rct_tx_rct_spend_with_zero_commit); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_zero_vin_amount); + GENERATE_AND_PLAY(gen_rct_tx_rct_non_zero_vin_amount); + GENERATE_AND_PLAY(gen_rct_tx_non_zero_vout_amount); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_duplicate_key_image); + GENERATE_AND_PLAY(gen_rct_tx_rct_duplicate_key_image); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_wrong_key_image); + GENERATE_AND_PLAY(gen_rct_tx_rct_wrong_key_image); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_wrong_fee); + GENERATE_AND_PLAY(gen_rct_tx_rct_wrong_fee); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_remove_vin); + GENERATE_AND_PLAY(gen_rct_tx_rct_remove_vin); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_add_vout); + GENERATE_AND_PLAY(gen_rct_tx_rct_add_vout); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee); + std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta); std::cout << "\nREPORT:\n"; std::cout << " Test run: " << tests_count << '\n'; diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index 4da87a973..1b9ebd756 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -40,6 +40,7 @@ #include "ring_signature_1.h" #include "tx_validation.h" #include "v2_tests.h" +#include "rct.h" /************************************************************************/ /* */ /************************************************************************/ diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp new file mode 100644 index 000000000..465d2036f --- /dev/null +++ b/tests/core_tests/rct.cpp @@ -0,0 +1,469 @@ +// Copyright (c) 2014-2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "ringct/rctSigs.h" +#include "chaingen.h" +#include "chaingen_tests_list.h" + +using namespace epee; +using namespace crypto; +using namespace cryptonote; + +//---------------------------------------------------------------------------------------------------------------------- +// Tests + +bool gen_rct_tx_validation_base::generate_with(std::vector& events, + const int *out_idx, int mixin, uint64_t amount_paid, bool valid, + const std::function &sources, std::vector &destinations)> &pre_tx, + const std::function &post_tx) const +{ + uint64_t ts_start = 1338224400; + + GENERATE_ACCOUNT(miner_account); + MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); + + // create 4 miner accounts, and have them mine the next 4 blocks + cryptonote::account_base miner_accounts[4]; + const cryptonote::block *prev_block = &blk_0; + cryptonote::block blocks[4]; + for (size_t n = 0; n < 4; ++n) { + miner_accounts[n].generate(); + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, + 3, 3, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), std::vector(), 0, 0, 3), + false, "Failed to generate block"); + events.push_back(blocks[n]); + prev_block = blocks + n; + LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx)); + } + + // rewind + cryptonote::block blk_r, blk_last; + { + blk_last = blocks[3]; + for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + { + cryptonote::block blk; + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, + 3, 3, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), std::vector(), 0, 0, 3), + false, "Failed to generate block"); + events.push_back(blk); + blk_last = blk; + } + blk_r = blk_last; + } + + // create 4 txes from these miners in another block, to generate some rct outputs + transaction rct_txes[4]; + rct::key rct_tx_masks[16]; + cryptonote::block blk_txes[4]; + for (size_t n = 0; n < 4; ++n) + { + std::vector starting_rct_tx_hashes; + std::vector sources; + + sources.resize(1); + tx_source_entry& src = sources.back(); + + const size_t index_in_tx = 5; + src.amount = 30000000000000; + for (int m = 0; m < 4; ++m) { + src.push_output(m, boost::get(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount); + } + src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx); + src.real_output = n; + src.real_output_in_tx_index = index_in_tx; + src.mask = rct::identity(); + + //fill outputs entry + tx_destination_entry td; + td.addr = miner_accounts[n].get_keys().m_account_address; + td.amount = 7390000000000; + std::vector destinations; + destinations.push_back(td); + destinations.push_back(td); + destinations.push_back(td); + destinations.push_back(td); // 30 -> 7.39 * 4 + + crypto::secret_key tx_key; + bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), sources, destinations, std::vector(), rct_txes[n], 0, tx_key, true); + CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); + events.push_back(rct_txes[n]); + starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes[n])); + + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(rct_txes[n]); + for (size_t o = 0; o < 4; ++o) + { + cryptonote::keypair in_ephemeral; + crypto::key_image ki; + cryptonote::generate_key_image_helper(miner_accounts[n].get_keys(), tx_pub_key, o, in_ephemeral, ki); + rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(in_ephemeral.sec), o, rct_tx_masks[o+n*4]); + } + + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version, + 3, 3, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 0, 3), + false, "Failed to generate block"); + events.push_back(blk_txes[n]); + blk_last = blk_txes[n]; + } + + // rewind + { + for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + { + cryptonote::block blk; + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, + 3, 3, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), std::vector(), 0, 0, 3), + false, "Failed to generate block"); + events.push_back(blk); + blk_last = blk; + } + blk_r = blk_last; + } + + // create a tx from the requested ouputs + std::vector sources; + size_t rct_idx = 0, pre_rct_idx = 0; + for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) { + sources.resize(sources.size()+1); + tx_source_entry& src = sources.back(); + + src.real_output = 0; + if (out_idx[out_idx_idx]) { + // rct + src.amount = 7390000000000; + src.real_out_tx_key = get_tx_pub_key_from_extra(rct_txes[rct_idx/4]); + src.real_output_in_tx_index = rct_idx&3; + src.mask = rct_tx_masks[rct_idx]; + for (int m = 0; m <= mixin; ++m) { + rct::ctkey ctkey; + ctkey.dest = rct::pk2rct(boost::get(rct_txes[rct_idx/4].vout[rct_idx&3].target).key); + ctkey.mask = rct_txes[rct_idx/4].rct_signatures.outPk[rct_idx&3].mask; + src.outputs.push_back(std::make_pair(rct_idx, ctkey)); + ++rct_idx; + } + } + else + { + // pre rct + src.amount = 5000000000000; + src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[pre_rct_idx].miner_tx); + src.real_output_in_tx_index = 4; + src.mask = rct::identity(); + for (int m = 0; m <= mixin; ++m) { + src.push_output(m, boost::get(blocks[pre_rct_idx].miner_tx.vout[4].target).key, src.amount); + ++pre_rct_idx; + } + } + } + + //fill outputs entry + tx_destination_entry td; + td.addr = miner_account.get_keys().m_account_address; + td.amount = amount_paid; + std::vector destinations; + destinations.push_back(td); + + if (pre_tx) + pre_tx(sources, destinations); + + transaction tx; + crypto::secret_key tx_key; + bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), sources, destinations, std::vector(), tx, 0, tx_key, true); + CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); + + if (post_tx) + post_tx(tx); + + if (!valid) + DO_CALLBACK(events, "mark_invalid_tx"); + events.push_back(tx); + LOG_PRINT_L0("Test tx: " << obj_to_json_str(tx)); + + return true; +} + +bool gen_rct_tx_valid_from_pre_rct::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, true, NULL, NULL); +} + +bool gen_rct_tx_valid_from_rct::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, true, NULL, NULL); +} + +bool gen_rct_tx_valid_from_mixed::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, 0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, true, NULL, NULL); +} + +bool gen_rct_tx_pre_rct_bad_real_dest::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + bool tx_creation_succeeded = false; + // in the case, the tx will fail to create, due to mismatched sk/pk + bool ret = generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[0].second.dest);}, + [&tx_creation_succeeded](const transaction &tx){tx_creation_succeeded=true;}); + return !ret && !tx_creation_succeeded; +} + +bool gen_rct_tx_pre_rct_bad_real_mask::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(99999);}, + NULL); +} + +bool gen_rct_tx_pre_rct_bad_fake_dest::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[1].second.dest);}, + NULL); +} + +bool gen_rct_tx_pre_rct_bad_fake_mask::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommit(99999);}, + NULL); +} + +bool gen_rct_tx_rct_bad_real_dest::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + bool tx_creation_succeeded = false; + // in the case, the tx will fail to create, due to mismatched sk/pk + bool ret = generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[0].second.dest);}, + [&tx_creation_succeeded](const transaction &tx){tx_creation_succeeded=true;}); + return !ret && !tx_creation_succeeded; +} + +bool gen_rct_tx_rct_bad_real_mask::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(99999);}, + NULL); +} + +bool gen_rct_tx_rct_bad_fake_dest::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[1].second.dest);}, + NULL); +} + +bool gen_rct_tx_rct_bad_fake_mask::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommit(99999);}, + NULL); +} + +bool gen_rct_tx_rct_spend_with_zero_commit::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + [](std::vector &sources, std::vector &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(sources[0].amount); sources[0].mask = rct::identity();}, + [](transaction &tx){boost::get(tx.vin[0]).amount = 0;}); +} + +bool gen_rct_tx_pre_rct_zero_vin_amount::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {boost::get(tx.vin[0]).amount = 0;}); +} + +bool gen_rct_tx_rct_non_zero_vin_amount::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {boost::get(tx.vin[0]).amount = 5000000000000;}); // one that we know exists +} + +bool gen_rct_tx_non_zero_vout_amount::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.vout[0].amount = 5000000000000;}); // one that we know exists +} + +bool gen_rct_tx_pre_rct_duplicate_key_image::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [&events](transaction &tx) {boost::get(tx.vin[0]).k_image = boost::get(boost::get(events[67]).vin[0]).k_image;}); +} + +bool gen_rct_tx_rct_duplicate_key_image::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [&events](transaction &tx) {boost::get(tx.vin[0]).k_image = boost::get(boost::get(events[67]).vin[0]).k_image;}); +} + +bool gen_rct_tx_pre_rct_wrong_key_image::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + // some random key image from the monero blockchain, so we get something that is a valid key image + static const uint8_t k_image[33] = "\x49\x3b\x56\x16\x54\x76\xa8\x75\xb7\xf4\xa8\x51\xf5\x55\xd3\x44\xe7\x3e\xea\x73\xee\xc1\x06\x7c\x7d\xb6\x57\x28\x46\x85\xe1\x07"; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {memcpy(&boost::get(tx.vin[0]).k_image, k_image, 32);}); +} + +bool gen_rct_tx_rct_wrong_key_image::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + // some random key image from the monero blockchain, so we get something that is a valid key image + static const uint8_t k_image[33] = "\x49\x3b\x56\x16\x54\x76\xa8\x75\xb7\xf4\xa8\x51\xf5\x55\xd3\x44\xe7\x3e\xea\x73\xee\xc1\x06\x7c\x7d\xb6\x57\x28\x46\x85\xe1\x07"; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {memcpy(&boost::get(tx.vin[0]).k_image, k_image, 32);}); +} + +bool gen_rct_tx_pre_rct_wrong_fee::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.rct_signatures.txnFee++;}); +} + +bool gen_rct_tx_rct_wrong_fee::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.rct_signatures.txnFee++;}); +} + +bool gen_rct_tx_pre_rct_increase_vin_and_fee::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {boost::get(tx.vin[0]).amount++;tx.rct_signatures.txnFee++;}); +} + +bool gen_rct_tx_pre_rct_remove_vin::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.vin.pop_back();}); +} + +bool gen_rct_tx_rct_remove_vin::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.vin.pop_back();}); +} + +bool gen_rct_tx_pre_rct_add_vout::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.vout.push_back(tx.vout.back());}); +} + +bool gen_rct_tx_rct_add_vout::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {tx.vout.push_back(tx.vout.back());}); +} + diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h new file mode 100644 index 000000000..1b79cbd8f --- /dev/null +++ b/tests/core_tests/rct.h @@ -0,0 +1,251 @@ +// Copyright (c) 2014-2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once +#include "chaingen.h" + +struct gen_rct_tx_validation_base : public test_chain_unit_base +{ + gen_rct_tx_validation_base() + : m_invalid_tx_index(0) + , m_invalid_block_index(0) + { + REGISTER_CALLBACK_METHOD(gen_rct_tx_validation_base, mark_invalid_tx); + REGISTER_CALLBACK_METHOD(gen_rct_tx_validation_base, mark_invalid_block); + } + + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/) + { + if (m_invalid_tx_index == event_idx) + return tvc.m_verifivation_failed; + else + return !tvc.m_verifivation_failed && tx_added; + } + + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/) + { + if (m_invalid_block_index == event_idx) + return bvc.m_verifivation_failed; + else + return !bvc.m_verifivation_failed; + } + + bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + { + m_invalid_block_index = ev_index + 1; + return true; + } + + bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + { + m_invalid_tx_index = ev_index + 1; + return true; + } + + bool generate_with(std::vector& events, const int *out_idx, int mixin, + uint64_t amount_paid, bool valid, + const std::function &sources, std::vector &destinations)> &pre_tx, + const std::function &post_tx) const; + +private: + size_t m_invalid_tx_index; + size_t m_invalid_block_index; +}; + +template<> +struct get_test_options { + const std::pair hard_forks[2] = {std::make_pair(1, 0), std::make_pair(3, 1)}; + const cryptonote::test_options test_options = { + hard_forks + }; +}; + +// valid +struct gen_rct_tx_valid_from_pre_rct : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_valid_from_rct : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_valid_from_mixed : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +// altered commitment/dest +struct gen_rct_tx_pre_rct_bad_real_dest : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_bad_real_mask : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_bad_fake_dest : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_bad_fake_mask : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_bad_real_dest : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_bad_real_mask : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_bad_fake_dest : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_bad_fake_mask : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_spend_with_zero_commit : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +// altered amounts +struct gen_rct_tx_pre_rct_zero_vin_amount : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_non_zero_vin_amount : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_non_zero_vout_amount : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +// key image +struct gen_rct_tx_pre_rct_duplicate_key_image : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_duplicate_key_image : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_wrong_key_image : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_wrong_key_image : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +// fee +struct gen_rct_tx_pre_rct_wrong_fee : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_wrong_fee : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_increase_vin_and_fee : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +// modify vin/vout +struct gen_rct_tx_pre_rct_remove_vin : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_remove_vin : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_add_vout : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_add_vout : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; +