mirror of
https://git.wownero.com/wownero/wownero.git
synced 2024-08-15 01:03:23 +00:00
moved all stuff to github
This commit is contained in:
parent
095fbeeb67
commit
296ae46ed8
388 changed files with 95937 additions and 469 deletions
250
tests/core_tests/block_reward.cpp
Normal file
250
tests/core_tests/block_reward.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
#include "block_reward.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool construct_miner_tx_by_size(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins,
|
||||
const account_public_address& miner_address, std::vector<size_t>& block_sizes, size_t target_tx_size,
|
||||
size_t target_block_size, uint64_t fee = 0)
|
||||
{
|
||||
if (!construct_miner_tx(height, already_generated_coins, miner_address, miner_tx, fee, block_sizes, target_block_size))
|
||||
return false;
|
||||
|
||||
size_t current_size = get_object_blobsize(miner_tx);
|
||||
size_t try_count = 0;
|
||||
while (target_tx_size != current_size)
|
||||
{
|
||||
++try_count;
|
||||
if (10 < try_count)
|
||||
return false;
|
||||
|
||||
if (target_tx_size < current_size)
|
||||
{
|
||||
size_t diff = current_size - target_tx_size;
|
||||
if (diff <= miner_tx.extra.size())
|
||||
miner_tx.extra.resize(miner_tx.extra.size() - diff);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t diff = target_tx_size - current_size;
|
||||
miner_tx.extra.resize(miner_tx.extra.size() + diff);
|
||||
}
|
||||
|
||||
current_size = get_object_blobsize(miner_tx);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool construct_max_size_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account,
|
||||
size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW)
|
||||
{
|
||||
std::vector<size_t> block_sizes;
|
||||
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_prev), median_block_count);
|
||||
|
||||
size_t median = misc_utils::median(block_sizes);
|
||||
median = std::max(median, static_cast<size_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE));
|
||||
|
||||
transaction miner_tx;
|
||||
bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev),
|
||||
miner_account.get_keys().m_account_address, block_sizes, 2 * median, 2 * median);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
return generator.construct_block_manually(blk, blk_prev, miner_account, test_generator::bf_miner_tx, 0, 0, 0,
|
||||
crypto::hash(), 0, miner_tx);
|
||||
}
|
||||
|
||||
bool rewind_blocks(std::vector<test_event_entry>& events, test_generator& generator, block& blk, const block& blk_prev,
|
||||
const account_base& miner_account, size_t block_count)
|
||||
{
|
||||
blk = blk_prev;
|
||||
for (size_t i = 0; i < block_count; ++i)
|
||||
{
|
||||
block blk_i;
|
||||
if (!construct_max_size_block(generator, blk_i, blk, miner_account))
|
||||
return false;
|
||||
|
||||
events.push_back(blk_i);
|
||||
blk = blk_i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t get_tx_out_amount(const transaction& tx)
|
||||
{
|
||||
uint64_t amount = 0;
|
||||
BOOST_FOREACH(auto& o, tx.vout)
|
||||
amount += o.amount;
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
gen_block_reward::gen_block_reward()
|
||||
: m_invalid_block_index(0)
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(gen_block_reward, mark_invalid_block);
|
||||
REGISTER_CALLBACK_METHOD(gen_block_reward, mark_checked_block);
|
||||
REGISTER_CALLBACK_METHOD(gen_block_reward, check_block_rewards);
|
||||
}
|
||||
|
||||
bool gen_block_reward::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);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
MAKE_ACCOUNT(events, bob_account);
|
||||
|
||||
// Test: miner transactions without outputs (block reward == 0)
|
||||
block blk_0r;
|
||||
if (!rewind_blocks(events, generator, blk_0r, blk_0, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW))
|
||||
return false;
|
||||
|
||||
// Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
block blk_1_bad_1;
|
||||
if (!construct_max_size_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1))
|
||||
return false;
|
||||
events.push_back(blk_1_bad_1);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
block blk_1_bad_2;
|
||||
if (!construct_max_size_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1))
|
||||
return false;
|
||||
events.push_back(blk_1_bad_2);
|
||||
|
||||
block blk_1;
|
||||
if (!construct_max_size_block(generator, blk_1, blk_0r, miner_account))
|
||||
return false;
|
||||
events.push_back(blk_1);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_account);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
|
||||
block blk_5r;
|
||||
if (!rewind_blocks(events, generator, blk_5r, blk_5, miner_account, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW))
|
||||
return false;
|
||||
|
||||
// Test: fee increases block reward
|
||||
transaction tx_0(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * TESTS_DEFAULT_FEE));
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_account, tx_0);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
|
||||
// Test: fee from all block transactions increase block reward
|
||||
std::list<transaction> txs_0;
|
||||
txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * TESTS_DEFAULT_FEE));
|
||||
txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * TESTS_DEFAULT_FEE));
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_7, blk_6, miner_account, txs_0);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
|
||||
// Test: block reward == transactions fee
|
||||
{
|
||||
transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE);
|
||||
transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE);
|
||||
size_t txs_1_size = get_object_blobsize(tx_1) + get_object_blobsize(tx_2);
|
||||
uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2);
|
||||
|
||||
std::vector<size_t> block_sizes;
|
||||
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
size_t median = misc_utils::median(block_sizes);
|
||||
|
||||
transaction miner_tx;
|
||||
bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7),
|
||||
miner_account.get_keys().m_account_address, block_sizes, 2 * median - txs_1_size, 2 * median, txs_fee);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
std::vector<crypto::hash> txs_1_hashes;
|
||||
txs_1_hashes.push_back(get_transaction_hash(tx_1));
|
||||
txs_1_hashes.push_back(get_transaction_hash(tx_2));
|
||||
|
||||
block blk_8;
|
||||
generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes,
|
||||
0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_size);
|
||||
|
||||
events.push_back(blk_8);
|
||||
DO_CALLBACK(events, "mark_checked_block");
|
||||
}
|
||||
|
||||
DO_CALLBACK(events, "check_block_rewards");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_reward::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/)
|
||||
{
|
||||
if (m_invalid_block_index == event_idx)
|
||||
{
|
||||
m_invalid_block_index = 0;
|
||||
return bvc.m_verifivation_failed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !bvc.m_verifivation_failed;
|
||||
}
|
||||
}
|
||||
|
||||
bool gen_block_reward::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_block_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_reward::mark_checked_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_checked_blocks_indices.push_back(ev_index - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_reward::check_block_rewards(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_block_reward_without_txs::check_block_rewards");
|
||||
|
||||
std::array<uint64_t, 7> blk_rewards;
|
||||
blk_rewards[0] = MONEY_SUPPLY >> 18;
|
||||
uint64_t cumulative_reward = blk_rewards[0];
|
||||
for (size_t i = 1; i < blk_rewards.size(); ++i)
|
||||
{
|
||||
blk_rewards[i] = (MONEY_SUPPLY - cumulative_reward) >> 18;
|
||||
cumulative_reward += blk_rewards[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
{
|
||||
block blk_i = boost::get<block>(events[m_checked_blocks_indices[i]]);
|
||||
CHECK_EQ(blk_rewards[i], get_tx_out_amount(blk_i.miner_tx));
|
||||
}
|
||||
|
||||
block blk_n1 = boost::get<block>(events[m_checked_blocks_indices[5]]);
|
||||
CHECK_EQ(blk_rewards[5] + 3 * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n1.miner_tx));
|
||||
|
||||
block blk_n2 = boost::get<block>(events[m_checked_blocks_indices[6]]);
|
||||
CHECK_EQ(blk_rewards[6] + (5 + 7) * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n2.miner_tx));
|
||||
|
||||
block blk_n3 = boost::get<block>(events[m_checked_blocks_indices[7]]);
|
||||
CHECK_EQ((11 + 13) * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n3.miner_tx));
|
||||
|
||||
return true;
|
||||
}
|
||||
23
tests/core_tests/block_reward.h
Normal file
23
tests/core_tests/block_reward.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
struct gen_block_reward : public test_chain_unit_base
|
||||
{
|
||||
gen_block_reward();
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& blk);
|
||||
|
||||
bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool mark_checked_block(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_block_rewards(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
size_t m_invalid_block_index;
|
||||
std::vector<size_t> m_checked_blocks_indices;
|
||||
};
|
||||
613
tests/core_tests/block_validation.cpp
Normal file
613
tests/core_tests/block_validation.cpp
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool lift_up_difficulty(std::vector<test_event_entry>& events, std::vector<uint64_t>& timestamps,
|
||||
std::vector<difficulty_type>& cummulative_difficulties, test_generator& generator,
|
||||
size_t new_block_count, const block blk_last, const account_base& miner_account)
|
||||
{
|
||||
difficulty_type commulative_diffic = cummulative_difficulties.empty() ? 0 : cummulative_difficulties.back();
|
||||
block blk_prev = blk_last;
|
||||
for (size_t i = 0; i < new_block_count; ++i)
|
||||
{
|
||||
block blk_next;
|
||||
difficulty_type diffic = next_difficulty(timestamps, cummulative_difficulties);
|
||||
if (!generator.construct_block_manually(blk_next, blk_prev, miner_account,
|
||||
test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic))
|
||||
return false;
|
||||
|
||||
commulative_diffic += diffic;
|
||||
if (timestamps.size() == DIFFICULTY_WINDOW)
|
||||
{
|
||||
timestamps.erase(timestamps.begin());
|
||||
cummulative_difficulties.erase(cummulative_difficulties.begin());
|
||||
}
|
||||
timestamps.push_back(blk_next.timestamp);
|
||||
cummulative_difficulties.push_back(commulative_diffic);
|
||||
|
||||
events.push_back(blk_next);
|
||||
blk_prev = blk_next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#define BLOCK_VALIDATION_INIT_GENERATE() \
|
||||
GENERATE_ACCOUNT(miner_account); \
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, 1338224400);
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Tests
|
||||
|
||||
bool gen_block_big_major_version::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, CURRENT_BLOCK_MAJOR_VERSION + 1);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_big_minor_version::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_minor_ver, 0, CURRENT_BLOCK_MINOR_VERSION + 1);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_accepted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_ts_not_checked::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 2);
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, blk_0.timestamp - 60 * 60);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_accepted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_ts_in_past::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 1);
|
||||
|
||||
uint64_t ts_below_median = boost::get<block>(events[BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW / 2 - 1]).timestamp;
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, ts_below_median);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_ts_in_future::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_timestamp, 0, 0, time(NULL) + 60*60 + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_invalid_prev_id::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
block blk_1;
|
||||
crypto::hash prev_id = get_block_hash(blk_0);
|
||||
reinterpret_cast<char &>(prev_id) ^= 1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_prev_id, 0, 0, 0, prev_id);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_invalid_prev_id::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/)
|
||||
{
|
||||
if (1 == event_idx)
|
||||
return bvc.m_marked_as_orphaned && !bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
|
||||
else
|
||||
return !bvc.m_marked_as_orphaned && bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
|
||||
}
|
||||
|
||||
bool gen_block_invalid_nonce::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> commulative_difficulties;
|
||||
if (!lift_up_difficulty(events, timestamps, commulative_difficulties, generator, 2, blk_0, miner_account))
|
||||
return false;
|
||||
|
||||
// Create invalid nonce
|
||||
difficulty_type diffic = next_difficulty(timestamps, commulative_difficulties);
|
||||
assert(1 < diffic);
|
||||
const block& blk_last = boost::get<block>(events.back());
|
||||
uint64_t timestamp = blk_last.timestamp;
|
||||
block blk_3;
|
||||
do
|
||||
{
|
||||
++timestamp;
|
||||
blk_3.miner_tx.set_null();
|
||||
if (!generator.construct_block_manually(blk_3, blk_last, miner_account,
|
||||
test_generator::bf_diffic | test_generator::bf_timestamp, 0, 0, timestamp, crypto::hash(), diffic))
|
||||
return false;
|
||||
}
|
||||
while (0 == blk_3.nonce);
|
||||
--blk_3.nonce;
|
||||
events.push_back(blk_3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_no_miner_tx::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
transaction miner_tx;
|
||||
miner_tx.set_null();
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_unlock_time_is_low::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
--miner_tx.unlock_time;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_unlock_time_is_high::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
++miner_tx.unlock_time;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_unlock_time_is_timestamp_in_past::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.unlock_time = blk_0.timestamp - 10 * 60;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_unlock_time_is_timestamp_in_future::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.unlock_time = blk_0.timestamp + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW * DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_height_is_low::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
boost::get<txin_gen>(miner_tx.vin[0]).height--;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_height_is_high::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
boost::get<txin_gen>(miner_tx.vin[0]).height++;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_has_2_tx_gen_in::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
|
||||
txin_gen in;
|
||||
in.height = get_block_height(blk_0) + 1;
|
||||
miner_tx.vin.push_back(in);
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
|
||||
|
||||
GENERATE_ACCOUNT(alice);
|
||||
|
||||
tx_source_entry se;
|
||||
se.amount = blk_0.miner_tx.vout[0].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key));
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
|
||||
se.real_output_in_tx_index = 0;
|
||||
std::vector<tx_source_entry> sources;
|
||||
sources.push_back(se);
|
||||
|
||||
tx_destination_entry de;
|
||||
de.addr = miner_account.get_keys().m_account_address;
|
||||
de.amount = se.amount;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(de);
|
||||
|
||||
transaction tmp_tx;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, tmp_tx, 0))
|
||||
return false;
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.vin.push_back(tmp_tx.vin[0]);
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
// This block has only one output
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_none);
|
||||
events.push_back(blk_1);
|
||||
|
||||
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
|
||||
|
||||
tx_source_entry se;
|
||||
se.amount = blk_1.miner_tx.vout[0].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key));
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
|
||||
se.real_output_in_tx_index = 0;
|
||||
std::vector<tx_source_entry> sources;
|
||||
sources.push_back(se);
|
||||
|
||||
tx_destination_entry de;
|
||||
de.addr = miner_account.get_keys().m_account_address;
|
||||
de.amount = se.amount;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(de);
|
||||
|
||||
transaction tmp_tx;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, tmp_tx, 0))
|
||||
return false;
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_1);
|
||||
miner_tx.vin[0] = tmp_tx.vin[0];
|
||||
|
||||
block blk_2;
|
||||
generator.construct_block_manually(blk_2, blk_1r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_2);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_is_small::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.vout[0].amount /= 2;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_out_is_big::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.vout[0].amount *= 2;
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_has_no_out::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
miner_tx.vout.clear();
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_miner_tx_has_out_to_alice::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
GENERATE_ACCOUNT(alice);
|
||||
|
||||
keypair txkey;
|
||||
MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey);
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::generate_key_derivation(alice.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 1, alice.get_keys().m_account_address.m_spend_public_key, out_eph_public_key);
|
||||
|
||||
tx_out out_to_alice;
|
||||
out_to_alice.amount = miner_tx.vout[0].amount / 2;
|
||||
miner_tx.vout[0].amount -= out_to_alice.amount;
|
||||
out_to_alice.target = txout_to_key(out_eph_public_key);
|
||||
miner_tx.vout.push_back(out_to_alice);
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_accepted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_has_invalid_tx::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
std::vector<crypto::hash> tx_hashes;
|
||||
tx_hashes.push_back(crypto::hash());
|
||||
|
||||
block blk_1;
|
||||
generator.construct_block_manually_tx(blk_1, blk_0, miner_account, tx_hashes, 0);
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_is_too_big::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
// Creating a huge miner_tx, it will have a lot of outs
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
static const size_t tx_out_count = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE / 2;
|
||||
uint64_t amount = get_outs_money_amount(miner_tx);
|
||||
uint64_t portion = amount / tx_out_count;
|
||||
uint64_t remainder = amount % tx_out_count;
|
||||
txout_target_v target = miner_tx.vout[0].target;
|
||||
miner_tx.vout.clear();
|
||||
for (size_t i = 0; i < tx_out_count; ++i)
|
||||
{
|
||||
tx_out o;
|
||||
o.amount = portion;
|
||||
o.target = target;
|
||||
miner_tx.vout.push_back(o);
|
||||
}
|
||||
if (0 < remainder)
|
||||
{
|
||||
tx_out o;
|
||||
o.amount = remainder;
|
||||
o.target = target;
|
||||
miner_tx.vout.push_back(o);
|
||||
}
|
||||
|
||||
// Block reward will be incorrect, as it must be reduced if cumulative block size is very big,
|
||||
// but in this test it doesn't matter
|
||||
block blk_1;
|
||||
if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx))
|
||||
return false;
|
||||
|
||||
events.push_back(blk_1);
|
||||
|
||||
DO_CALLBACK(events, "check_block_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gen_block_invalid_binary_format::gen_block_invalid_binary_format()
|
||||
: m_corrupt_blocks_begin_idx(0)
|
||||
{
|
||||
REGISTER_CALLBACK("check_all_blocks_purged", gen_block_invalid_binary_format::check_all_blocks_purged);
|
||||
REGISTER_CALLBACK("corrupt_blocks_boundary", gen_block_invalid_binary_format::corrupt_blocks_boundary);
|
||||
}
|
||||
|
||||
bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
BLOCK_VALIDATION_INIT_GENERATE();
|
||||
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> cummulative_difficulties;
|
||||
difficulty_type cummulative_diff = 1;
|
||||
|
||||
// Unlock blk_0 outputs
|
||||
block blk_last = blk_0;
|
||||
assert(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW < DIFFICULTY_WINDOW);
|
||||
for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
|
||||
{
|
||||
MAKE_NEXT_BLOCK(events, blk_curr, blk_last, miner_account);
|
||||
timestamps.push_back(blk_curr.timestamp);
|
||||
cummulative_difficulties.push_back(++cummulative_diff);
|
||||
blk_last = blk_curr;
|
||||
}
|
||||
|
||||
// Lifting up takes a while
|
||||
difficulty_type diffic;
|
||||
do
|
||||
{
|
||||
blk_last = boost::get<block>(events.back());
|
||||
diffic = next_difficulty(timestamps, cummulative_difficulties);
|
||||
if (!lift_up_difficulty(events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account))
|
||||
return false;
|
||||
std::cout << "Block #" << events.size() << ", difficulty: " << diffic << std::endl;
|
||||
}
|
||||
while (diffic < 1500);
|
||||
|
||||
blk_last = boost::get<block>(events.back());
|
||||
MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(120), boost::get<block>(events[1]));
|
||||
DO_CALLBACK(events, "corrupt_blocks_boundary");
|
||||
|
||||
block blk_test;
|
||||
std::vector<crypto::hash> tx_hashes;
|
||||
tx_hashes.push_back(get_transaction_hash(tx_0));
|
||||
size_t txs_size = get_object_blobsize(tx_0);
|
||||
diffic = next_difficulty(timestamps, cummulative_difficulties);
|
||||
if (!generator.construct_block_manually(blk_test, blk_last, miner_account,
|
||||
test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp,
|
||||
crypto::hash(), diffic, transaction(), tx_hashes, txs_size))
|
||||
return false;
|
||||
|
||||
blobdata blob = t_serializable_object_to_blob(blk_test);
|
||||
for (size_t i = 0; i < blob.size(); ++i)
|
||||
{
|
||||
for (size_t bit_idx = 0; bit_idx < sizeof(blobdata::value_type) * 8; ++bit_idx)
|
||||
{
|
||||
serialized_block sr_block(blob);
|
||||
blobdata::value_type& ch = sr_block.data[i];
|
||||
ch ^= 1 << bit_idx;
|
||||
|
||||
events.push_back(sr_block);
|
||||
}
|
||||
}
|
||||
|
||||
DO_CALLBACK(events, "check_all_blocks_purged");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_invalid_binary_format::check_block_verification_context(const cryptonote::block_verification_context& bvc,
|
||||
size_t event_idx, const cryptonote::block& blk)
|
||||
{
|
||||
if (0 == m_corrupt_blocks_begin_idx || event_idx < m_corrupt_blocks_begin_idx)
|
||||
{
|
||||
return bvc.m_added_to_main_chain;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !bvc.m_added_to_main_chain && (bvc.m_already_exists || bvc.m_marked_as_orphaned || bvc.m_verifivation_failed);
|
||||
}
|
||||
}
|
||||
|
||||
bool gen_block_invalid_binary_format::corrupt_blocks_boundary(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
m_corrupt_blocks_begin_idx = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_block_invalid_binary_format::check_all_blocks_purged");
|
||||
|
||||
CHECK_EQ(1, c.get_pool_transactions_count());
|
||||
CHECK_EQ(m_corrupt_blocks_begin_idx - 2, c.get_current_blockchain_height());
|
||||
|
||||
return true;
|
||||
}
|
||||
182
tests/core_tests/block_validation.h
Normal file
182
tests/core_tests/block_validation.h
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
template<size_t invalid_block_idx = 0>
|
||||
class gen_block_verification_base : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_block_verification_base()
|
||||
{
|
||||
REGISTER_CALLBACK("check_block_purged", gen_block_verification_base<invalid_block_idx>::check_block_purged);
|
||||
}
|
||||
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/)
|
||||
{
|
||||
if (invalid_block_idx == event_idx)
|
||||
return bvc.m_verifivation_failed;
|
||||
else
|
||||
return !bvc.m_verifivation_failed;
|
||||
}
|
||||
|
||||
bool check_block_purged(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_block_verification_base::check_block_purged");
|
||||
|
||||
CHECK_TEST_CONDITION(invalid_block_idx < ev_index);
|
||||
CHECK_EQ(0, c.get_pool_transactions_count());
|
||||
CHECK_EQ(invalid_block_idx, c.get_current_blockchain_height());
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t expected_blockchain_height>
|
||||
struct gen_block_accepted_base : public test_chain_unit_base
|
||||
{
|
||||
gen_block_accepted_base()
|
||||
{
|
||||
REGISTER_CALLBACK("check_block_accepted", gen_block_accepted_base::check_block_accepted);
|
||||
}
|
||||
|
||||
bool check_block_accepted(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_block_accepted_base::check_block_accepted");
|
||||
|
||||
CHECK_EQ(0, c.get_pool_transactions_count());
|
||||
CHECK_EQ(expected_blockchain_height, c.get_current_blockchain_height());
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct gen_block_big_major_version : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_big_minor_version : public gen_block_accepted_base<2>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_ts_not_checked : public gen_block_accepted_base<BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_ts_in_past : public gen_block_verification_base<BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_ts_in_future : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_invalid_prev_id : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/);
|
||||
};
|
||||
|
||||
struct gen_block_invalid_nonce : public gen_block_verification_base<3>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_no_miner_tx : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_unlock_time_is_low : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_unlock_time_is_high : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_unlock_time_is_timestamp_in_past : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_unlock_time_is_timestamp_in_future : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_height_is_low : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_height_is_high : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_has_2_tx_gen_in : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_has_2_in : public gen_block_verification_base<CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_with_txin_to_key : public gen_block_verification_base<CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 2>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_is_small : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_out_is_big : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_has_no_out : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_miner_tx_has_out_to_alice : public gen_block_accepted_base<2>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_has_invalid_tx : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_is_too_big : public gen_block_verification_base<1>
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_block_invalid_binary_format : public test_chain_unit_base
|
||||
{
|
||||
gen_block_invalid_binary_format();
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/);
|
||||
bool check_all_blocks_purged(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool corrupt_blocks_boundary(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
size_t m_corrupt_blocks_begin_idx;
|
||||
};
|
||||
293
tests/core_tests/chain_split_1.cpp
Normal file
293
tests/core_tests/chain_split_1.cpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
gen_simple_chain_split_1::gen_simple_chain_split_1()
|
||||
{
|
||||
REGISTER_CALLBACK("check_split_not_switched", gen_simple_chain_split_1::check_split_not_switched);
|
||||
REGISTER_CALLBACK("check_split_not_switched2", gen_simple_chain_split_1::check_split_not_switched2);
|
||||
REGISTER_CALLBACK("check_split_switched", gen_simple_chain_split_1::check_split_switched);
|
||||
REGISTER_CALLBACK("check_split_not_switched_back", gen_simple_chain_split_1::check_split_not_switched_back);
|
||||
REGISTER_CALLBACK("check_split_switched_back_1", gen_simple_chain_split_1::check_split_switched_back_1);
|
||||
REGISTER_CALLBACK("check_split_switched_back_2", gen_simple_chain_split_1::check_split_switched_back_2);
|
||||
REGISTER_CALLBACK("check_mempool_1", gen_simple_chain_split_1::check_mempool_1);
|
||||
REGISTER_CALLBACK("check_mempool_2", gen_simple_chain_split_1::check_mempool_2);
|
||||
//REGISTER_CALLBACK("check_orphaned_chain_1", gen_simple_chain_split_1::check_orphaned_chain_1);
|
||||
//REGISTER_CALLBACK("check_orphaned_switched_to_alternative", gen_simple_chain_split_1::check_orphaned_switched_to_alternative);
|
||||
//REGISTER_CALLBACK("check_orphaned_chain_2", gen_simple_chain_split_1::check_orphaned_chain_2);
|
||||
//REGISTER_CALLBACK("check_orphaned_switched_to_main", gen_simple_chain_split_1::check_orphaned_switched_to_main);
|
||||
//REGISTER_CALLBACK("check_orphaned_chain_38", gen_simple_chain_split_1::check_orphaned_chain_38);
|
||||
//REGISTER_CALLBACK("check_orphaned_chain_39", gen_simple_chain_split_1::check_orphaned_chain_39);
|
||||
//REGISTER_CALLBACK("check_orphaned_chain_40", gen_simple_chain_split_1::check_orphaned_chain_40);
|
||||
//REGISTER_CALLBACK("check_orphaned_chain_41", gen_simple_chain_split_1::check_orphaned_chain_41);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::generate(std::vector<test_event_entry> &events) const
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
/*
|
||||
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <-- main blockchain height
|
||||
(0 )-(1 )-(2 )-(3 )-(4 )-(5 ) -(6 ) -(7 ) -(8 )|-(17) -(18) -(19) -(20) -(21)|-(22)|-(23)|-(24)|
|
||||
\ -(9 ) -(10)|-(11)|-(12)|-(13) -(14) -(15) -(16)
|
||||
-(25) -(26)|
|
||||
-(27)| #check switching to alternative
|
||||
----------------------------------------------------------------------------------
|
||||
-(28) -(29) -(30) -(31)|
|
||||
-(32)| #check switching orphans to main
|
||||
----------------------------------------------------------------------------------
|
||||
-(33) -(34) -(35) -(36) -(37) -(38)|
|
||||
-(39)| #<--this part becomes alternative chain connected to main
|
||||
-(40)| #still marked as orphaned
|
||||
-(41)|
|
||||
#check orphaned with block in the middle of the orphaned chain
|
||||
*/
|
||||
|
||||
GENERATE_ACCOUNT(first_miner_account);
|
||||
// events index
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, first_miner_account, ts_start); // 0
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, first_miner_account); // 1
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, first_miner_account); // 2
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, first_miner_account); // 3
|
||||
MAKE_NEXT_BLOCK(events, blk_4, blk_3, first_miner_account); // 4
|
||||
MAKE_NEXT_BLOCK(events, blk_5, blk_4, first_miner_account); // 5
|
||||
MAKE_NEXT_BLOCK(events, blk_6, blk_5, first_miner_account); // 6
|
||||
MAKE_NEXT_BLOCK(events, blk_7, blk_6, first_miner_account); // 7
|
||||
MAKE_NEXT_BLOCK(events, blk_8, blk_7, first_miner_account); // 8
|
||||
//split
|
||||
MAKE_NEXT_BLOCK(events, blk_9, blk_5, first_miner_account); // 9
|
||||
MAKE_NEXT_BLOCK(events, blk_10, blk_9, first_miner_account); // 10
|
||||
DO_CALLBACK(events, "check_split_not_switched"); // 11
|
||||
MAKE_NEXT_BLOCK(events, blk_11, blk_10, first_miner_account); // 12
|
||||
DO_CALLBACK(events, "check_split_not_switched2"); // 13
|
||||
MAKE_NEXT_BLOCK(events, blk_12, blk_11, first_miner_account); // 14
|
||||
DO_CALLBACK(events, "check_split_switched"); // 15
|
||||
MAKE_NEXT_BLOCK(events, blk_13, blk_12, first_miner_account); // 16
|
||||
MAKE_NEXT_BLOCK(events, blk_14, blk_13, first_miner_account); // 17
|
||||
MAKE_NEXT_BLOCK(events, blk_15, blk_14, first_miner_account); // 18
|
||||
MAKE_NEXT_BLOCK(events, blk_16, blk_15, first_miner_account); // 19
|
||||
//split again and check back switching
|
||||
MAKE_NEXT_BLOCK(events, blk_17, blk_8, first_miner_account); // 20
|
||||
MAKE_NEXT_BLOCK(events, blk_18, blk_17, first_miner_account); // 21
|
||||
MAKE_NEXT_BLOCK(events, blk_19, blk_18, first_miner_account); // 22
|
||||
MAKE_NEXT_BLOCK(events, blk_20, blk_19, first_miner_account); // 23
|
||||
MAKE_NEXT_BLOCK(events, blk_21, blk_20, first_miner_account); // 24
|
||||
DO_CALLBACK(events, "check_split_not_switched_back"); // 25
|
||||
MAKE_NEXT_BLOCK(events, blk_22, blk_21, first_miner_account); // 26
|
||||
DO_CALLBACK(events, "check_split_switched_back_1"); // 27
|
||||
MAKE_NEXT_BLOCK(events, blk_23, blk_22, first_miner_account); // 28
|
||||
DO_CALLBACK(events, "check_split_switched_back_2"); // 29
|
||||
|
||||
REWIND_BLOCKS(events, blk_23r, blk_23, first_miner_account); // 30...N1
|
||||
GENERATE_ACCOUNT(alice);
|
||||
MAKE_TX(events, tx_0, first_miner_account, alice, MK_COINS(10), blk_23); // N1+1
|
||||
MAKE_TX(events, tx_1, first_miner_account, alice, MK_COINS(20), blk_23); // N1+2
|
||||
MAKE_TX(events, tx_2, first_miner_account, alice, MK_COINS(30), blk_23); // N1+3
|
||||
DO_CALLBACK(events, "check_mempool_1"); // N1+4
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_24, blk_23r, first_miner_account, tx_0); // N1+5
|
||||
DO_CALLBACK(events, "check_mempool_2"); // N1+6
|
||||
/*
|
||||
//check orphaned blocks
|
||||
MAKE_NEXT_BLOCK_NO_ADD(events, blk_orph_27, blk_16, get_test_target(), first_miner_account);
|
||||
MAKE_NEXT_BLOCK(events, blk_25, blk_orph_27, get_test_target(), first_miner_account); // 36
|
||||
MAKE_NEXT_BLOCK(events, blk_26, blk_25, get_test_target(), first_miner_account); // 37
|
||||
DO_CALLBACK(events, "check_orphaned_chain_1"); // 38
|
||||
ADD_BLOCK(events, blk_orph_27); // 39
|
||||
DO_CALLBACK(events, "check_orphaned_switched_to_alternative"); // 40
|
||||
|
||||
//check orphaned check to main chain
|
||||
MAKE_NEXT_BLOCK_NO_ADD(events, blk_orph_32, blk_16, get_test_target(), first_miner_account);
|
||||
MAKE_NEXT_BLOCK(events, blk_28, blk_orph_32, get_test_target(), first_miner_account); // 41
|
||||
MAKE_NEXT_BLOCK(events, blk_29, blk_28, get_test_target(), first_miner_account); // 42
|
||||
MAKE_NEXT_BLOCK(events, blk_30, blk_29, get_test_target(), first_miner_account); // 43
|
||||
MAKE_NEXT_BLOCK(events, blk_31, blk_30, get_test_target(), first_miner_account); // 44
|
||||
DO_CALLBACK(events, "check_orphaned_chain_2"); // 45
|
||||
ADD_BLOCK(events, blk_orph_32); // 46
|
||||
DO_CALLBACK(events, "check_orphaned_switched_to_main"); // 47
|
||||
|
||||
//check orphaned check to main chain
|
||||
MAKE_NEXT_BLOCK_NO_ADD(events, blk_orph_39, blk_16, get_test_target(), first_miner_account);
|
||||
MAKE_NEXT_BLOCK(events, blk_33, blk_orph_39, get_test_target(), first_miner_account); // 48
|
||||
MAKE_NEXT_BLOCK(events, blk_34, blk_33, get_test_target(), first_miner_account); // 49
|
||||
MAKE_NEXT_BLOCK_NO_ADD(events, blk_orph_41, blk_34, get_test_target(), first_miner_account);
|
||||
MAKE_NEXT_BLOCK(events, blk_35, blk_orph_41, get_test_target(), first_miner_account); // 50
|
||||
MAKE_NEXT_BLOCK(events, blk_36, blk_35, get_test_target(), first_miner_account); // 51
|
||||
MAKE_NEXT_BLOCK_NO_ADD(events, blk_orph_40, blk_36, get_test_target(), first_miner_account);
|
||||
MAKE_NEXT_BLOCK(events, blk_37, blk_orph_40, get_test_target(), first_miner_account); // 52
|
||||
MAKE_NEXT_BLOCK(events, blk_38, blk_37, get_test_target(), first_miner_account); // 53
|
||||
DO_CALLBACK(events, "check_orphaned_chain_38"); // 54
|
||||
ADD_BLOCK(events, blk_orph_39); // 55
|
||||
DO_CALLBACK(events, "check_orphaned_chain_39"); // 56
|
||||
ADD_BLOCK(events, blk_orph_40); // 57
|
||||
DO_CALLBACK(events, "check_orphaned_chain_40"); // 58
|
||||
ADD_BLOCK(events, blk_orph_41); // 59
|
||||
DO_CALLBACK(events, "check_orphaned_chain_41"); // 60
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_mempool_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_mempool_2");
|
||||
CHECK_TEST_CONDITION(c.get_pool_transactions_count() == 2);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_mempool_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_mempool_1");
|
||||
CHECK_TEST_CONDITION(c.get_pool_transactions_count() == 3);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched");
|
||||
//check height
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9);
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9);
|
||||
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[8])));
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 2);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_split_not_switched2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched2");
|
||||
//check height
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9);
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9);
|
||||
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[8])));
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched");
|
||||
|
||||
//check height
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 10);
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 10);
|
||||
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[14])));
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_split_not_switched_back(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched_back");
|
||||
//check height
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 14);
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 14);
|
||||
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[19])));
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_split_switched_back_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched_back_1");
|
||||
|
||||
//check height
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 15);
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 15);
|
||||
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[26])));
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8);
|
||||
|
||||
return true;
|
||||
}//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_split_switched_back_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched_back_2");
|
||||
|
||||
//check height
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 16);
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 16);
|
||||
CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get<cryptonote::block>(events[28])));
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
bool gen_simple_chain_split_1::check_orphaned_chain_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_1");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 2);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_switched_to_alternative(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_switched_to_alternative");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 0);
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 17);
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 11);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_chain_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_2");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 4);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_switched_to_main(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_switched_to_main");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 0);
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 19);
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 14);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_chain_38(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_38");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 6);
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 14);
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 19);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_chain_39(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_39");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 4);
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 17);
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 19);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_chain_40(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_40");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 5);
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 17);
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 19);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_simple_chain_split_1::check_orphaned_chain_41(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_41");
|
||||
CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 0);
|
||||
CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 19);
|
||||
CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 23);
|
||||
|
||||
return true;
|
||||
}*/
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
33
tests/core_tests/chain_split_1.h
Normal file
33
tests/core_tests/chain_split_1.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_simple_chain_split_1 : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_simple_chain_split_1();
|
||||
bool generate(std::vector<test_event_entry> &events) const;
|
||||
bool check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_split_not_switched2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_split_not_switched_back(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_split_switched_back_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_split_switched_back_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_mempool_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_mempool_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
/*bool check_orphaned_chain_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_switched_to_alternative(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_chain_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_switched_to_main(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_chain_38(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_chain_39(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_chain_40(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool check_orphaned_chain_41(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events); */
|
||||
private:
|
||||
};
|
||||
184
tests/core_tests/chain_switch_1.cpp
Normal file
184
tests/core_tests/chain_switch_1.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
gen_chain_switch_1::gen_chain_switch_1()
|
||||
{
|
||||
REGISTER_CALLBACK("check_split_not_switched", gen_chain_switch_1::check_split_not_switched);
|
||||
REGISTER_CALLBACK("check_split_switched", gen_chain_switch_1::check_split_switched);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_chain_switch_1::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
/*
|
||||
(0 )-(1 )-(2 ) -(3 )-(4 ) <- main chain, until 7 isn't connected
|
||||
\ |-(5 )-(6 )-(7 )| <- alt chain, until 7 isn't connected
|
||||
|
||||
transactions ([n] - tx amount, (m) - block):
|
||||
(1) : miner -[ 5]-> account_1 ( +5 in main chain, +5 in alt chain)
|
||||
(3) : miner -[ 7]-> account_2 ( +7 in main chain, +0 in alt chain), tx will be in tx pool after switch
|
||||
(4), (6): miner -[11]-> account_3 (+11 in main chain, +11 in alt chain)
|
||||
(5) : miner -[13]-> account_4 ( +0 in main chain, +13 in alt chain), tx will be in tx pool before switch
|
||||
|
||||
transactions orders ([n] - tx amount, (m) - block):
|
||||
miner -[1], [2]-> account_1: in main chain (3), (3), in alt chain (5), (6)
|
||||
miner -[1], [2]-> account_2: in main chain (3), (4), in alt chain (5), (5)
|
||||
miner -[1], [2]-> account_3: in main chain (3), (4), in alt chain (6), (5)
|
||||
miner -[1], [2]-> account_4: in main chain (4), (3), in alt chain (5), (6)
|
||||
*/
|
||||
|
||||
GENERATE_ACCOUNT(miner_account);
|
||||
|
||||
// events
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); // 0
|
||||
MAKE_ACCOUNT(events, recipient_account_1); // 1
|
||||
MAKE_ACCOUNT(events, recipient_account_2); // 2
|
||||
MAKE_ACCOUNT(events, recipient_account_3); // 3
|
||||
MAKE_ACCOUNT(events, recipient_account_4); // 4
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account) // <N blocks>
|
||||
MAKE_TX(events, tx_00, miner_account, recipient_account_1, MK_COINS(5), blk_0); // 5 + N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_00); // 6 + N
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 7 + N
|
||||
REWIND_BLOCKS(events, blk_2r, blk_2, miner_account) // <N blocks>
|
||||
|
||||
// Transactions to test account balances after switch
|
||||
MAKE_TX_LIST_START(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(7), blk_2); // 8 + 2N
|
||||
MAKE_TX_LIST_START(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(11), blk_2); // 9 + 2N
|
||||
MAKE_TX_LIST_START(events, txs_blk_5, miner_account, recipient_account_4, MK_COINS(13), blk_2); // 10 + 2N
|
||||
std::list<transaction> txs_blk_6;
|
||||
txs_blk_6.push_back(txs_blk_4.front());
|
||||
|
||||
// Transactions, that has different order in alt block chains
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(1), blk_2); // 11 + 2N
|
||||
txs_blk_5.push_back(txs_blk_3.back());
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(2), blk_2); // 12 + 2N
|
||||
txs_blk_6.push_back(txs_blk_3.back());
|
||||
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(1), blk_2); // 13 + 2N
|
||||
txs_blk_5.push_back(txs_blk_3.back());
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_2, MK_COINS(2), blk_2); // 14 + 2N
|
||||
txs_blk_5.push_back(txs_blk_4.back());
|
||||
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_3, MK_COINS(1), blk_2); // 15 + 2N
|
||||
txs_blk_6.push_back(txs_blk_3.back());
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(2), blk_2); // 16 + 2N
|
||||
txs_blk_5.push_back(txs_blk_4.back());
|
||||
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_4, MK_COINS(1), blk_2); // 17 + 2N
|
||||
txs_blk_5.push_back(txs_blk_4.back());
|
||||
MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_4, MK_COINS(2), blk_2); // 18 + 2N
|
||||
txs_blk_6.push_back(txs_blk_3.back());
|
||||
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_blk_3); // 19 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3, miner_account, txs_blk_4); // 20 + 2N
|
||||
//split
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_5, blk_2r, miner_account, txs_blk_5); // 22 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_6, blk_5, miner_account, txs_blk_6); // 23 + 2N
|
||||
DO_CALLBACK(events, "check_split_not_switched"); // 21 + 2N
|
||||
MAKE_NEXT_BLOCK(events, blk_7, blk_6, miner_account); // 24 + 2N
|
||||
DO_CALLBACK(events, "check_split_switched"); // 25 + 2N
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_chain_switch_1::check_split_not_switched");
|
||||
|
||||
m_recipient_account_1 = boost::get<account_base>(events[1]);
|
||||
m_recipient_account_2 = boost::get<account_base>(events[2]);
|
||||
m_recipient_account_3 = boost::get<account_base>(events[3]);
|
||||
m_recipient_account_4 = boost::get<account_base>(events[4]);
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 10000, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(5 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size());
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<block>(events[20 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_4
|
||||
|
||||
CHECK_EQ(2, c.get_alternative_blocks_count());
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(MK_COINS(8), get_balance(m_recipient_account_1, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(10), get_balance(m_recipient_account_2, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(14), get_balance(m_recipient_account_3, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(3), get_balance(m_recipient_account_4, chain, mtx));
|
||||
|
||||
std::list<transaction> tx_pool;
|
||||
r = c.get_pool_transactions(tx_pool);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(1, tx_pool.size());
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
uint64_t transfered;
|
||||
lookup_acc_outs(m_recipient_account_4.get_keys(), tx_pool.front(), get_tx_pub_key_from_extra(tx_pool.front()), tx_outs, transfered);
|
||||
CHECK_EQ(MK_COINS(13), transfered);
|
||||
|
||||
m_chain_1.swap(blocks);
|
||||
m_tx_pool.swap(tx_pool);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_chain_switch_1::check_split_switched");
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 10000, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(6 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size());
|
||||
auto it = blocks.end();
|
||||
--it; --it; --it;
|
||||
CHECK_TEST_CONDITION(std::equal(blocks.begin(), it, m_chain_1.begin()));
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<block>(events[24 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_7
|
||||
|
||||
std::list<block> alt_blocks;
|
||||
r = c.get_alternative_blocks(alt_blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(2, c.get_alternative_blocks_count());
|
||||
|
||||
// Some blocks that were in main chain are in alt chain now
|
||||
BOOST_FOREACH(block b, alt_blocks)
|
||||
{
|
||||
CHECK_TEST_CONDITION(m_chain_1.end() != std::find(m_chain_1.begin(), m_chain_1.end(), b));
|
||||
}
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(MK_COINS(8), get_balance(m_recipient_account_1, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(3), get_balance(m_recipient_account_2, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(14), get_balance(m_recipient_account_3, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(16), get_balance(m_recipient_account_4, chain, mtx));
|
||||
|
||||
std::list<transaction> tx_pool;
|
||||
r = c.get_pool_transactions(tx_pool);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(1, tx_pool.size());
|
||||
CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front()));
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
uint64_t transfered;
|
||||
lookup_acc_outs(m_recipient_account_2.get_keys(), tx_pool.front(), tx_outs, transfered);
|
||||
CHECK_EQ(MK_COINS(7), transfered);
|
||||
|
||||
return true;
|
||||
}
|
||||
30
tests/core_tests/chain_switch_1.h
Normal file
30
tests/core_tests/chain_switch_1.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_chain_switch_1 : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_chain_switch_1();
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
||||
bool check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
std::list<cryptonote::block> m_chain_1;
|
||||
|
||||
cryptonote::account_base m_recipient_account_1;
|
||||
cryptonote::account_base m_recipient_account_2;
|
||||
cryptonote::account_base m_recipient_account_3;
|
||||
cryptonote::account_base m_recipient_account_4;
|
||||
|
||||
std::list<cryptonote::transaction> m_tx_pool;
|
||||
};
|
||||
637
tests/core_tests/chaingen.cpp
Normal file
637
tests/core_tests/chaingen.cpp
Normal file
|
|
@ -0,0 +1,637 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
|
||||
#include "console_handler.h"
|
||||
|
||||
#include "p2p/net_node.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "cryptonote_core/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/miner.h"
|
||||
|
||||
#include "chaingen.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
void test_generator::get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const
|
||||
{
|
||||
crypto::hash curr = head;
|
||||
while (null_hash != curr && blockchain.size() < n)
|
||||
{
|
||||
auto it = m_blocks_info.find(curr);
|
||||
if (m_blocks_info.end() == it)
|
||||
{
|
||||
throw std::runtime_error("block hash wasn't found");
|
||||
}
|
||||
|
||||
blockchain.push_back(it->second);
|
||||
curr = it->second.prev_id;
|
||||
}
|
||||
|
||||
std::reverse(blockchain.begin(), blockchain.end());
|
||||
}
|
||||
|
||||
void test_generator::get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const
|
||||
{
|
||||
std::vector<block_info> blockchain;
|
||||
get_block_chain(blockchain, head, n);
|
||||
BOOST_FOREACH(auto& bi, blockchain)
|
||||
{
|
||||
block_sizes.push_back(bi.block_size);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t test_generator::get_already_generated_coins(const crypto::hash& blk_id) const
|
||||
{
|
||||
auto it = m_blocks_info.find(blk_id);
|
||||
if (it == m_blocks_info.end())
|
||||
throw std::runtime_error("block hash wasn't found");
|
||||
|
||||
return it->second.already_generated_coins;
|
||||
}
|
||||
|
||||
uint64_t test_generator::get_already_generated_coins(const cryptonote::block& blk) const
|
||||
{
|
||||
crypto::hash blk_hash;
|
||||
get_block_hash(blk, blk_hash);
|
||||
return get_already_generated_coins(blk_hash);
|
||||
}
|
||||
|
||||
void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins)
|
||||
{
|
||||
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx);
|
||||
bool block_too_big;
|
||||
uint64_t block_reward = get_block_reward(block_sizes, block_size, block_too_big, already_generated_coins);
|
||||
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size);
|
||||
}
|
||||
|
||||
bool test_generator::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<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list)
|
||||
{
|
||||
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
|
||||
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
||||
blk.timestamp = timestamp;
|
||||
blk.prev_id = prev_id;
|
||||
|
||||
blk.tx_hashes.reserve(tx_list.size());
|
||||
BOOST_FOREACH(const transaction &tx, tx_list)
|
||||
{
|
||||
crypto::hash tx_hash;
|
||||
get_transaction_hash(tx, tx_hash);
|
||||
blk.tx_hashes.push_back(tx_hash);
|
||||
}
|
||||
|
||||
uint64_t total_fee = 0;
|
||||
size_t txs_size = 0;
|
||||
BOOST_FOREACH(auto& tx, tx_list)
|
||||
{
|
||||
uint64_t fee = 0;
|
||||
bool r = get_tx_fee(tx, fee);
|
||||
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block");
|
||||
total_fee += fee;
|
||||
txs_size += get_object_blobsize(tx);
|
||||
}
|
||||
|
||||
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
|
||||
size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
while (true)
|
||||
{
|
||||
if (!construct_miner_tx(height, already_generated_coins, miner_acc.get_keys().m_account_address, blk.miner_tx, total_fee, block_sizes, target_block_size, 10))
|
||||
return false;
|
||||
|
||||
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
if (target_block_size < actual_block_size)
|
||||
{
|
||||
target_block_size = actual_block_size;
|
||||
}
|
||||
else if (actual_block_size < target_block_size)
|
||||
{
|
||||
size_t delta = target_block_size - actual_block_size;
|
||||
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
|
||||
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
if (actual_block_size == target_block_size)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size");
|
||||
delta = actual_block_size - target_block_size;
|
||||
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta);
|
||||
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
if (actual_block_size == target_block_size)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(actual_block_size < target_block_size, false, "Unexpected block size");
|
||||
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
|
||||
target_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//blk.tree_root_hash = get_tx_tree_hash(blk);
|
||||
|
||||
// Nonce search...
|
||||
blk.nonce = 0;
|
||||
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height))
|
||||
blk.timestamp++;
|
||||
|
||||
add_block(blk, txs_size, block_sizes, already_generated_coins);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp)
|
||||
{
|
||||
std::vector<size_t> block_sizes;
|
||||
std::list<cryptonote::transaction> tx_list;
|
||||
return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_sizes, tx_list);
|
||||
}
|
||||
|
||||
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev,
|
||||
const cryptonote::account_base& miner_acc,
|
||||
const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/)
|
||||
{
|
||||
uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin.front()).height + 1;
|
||||
crypto::hash prev_id = get_block_hash(blk_prev);
|
||||
// Keep difficulty unchanged
|
||||
uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
|
||||
uint64_t already_generated_coins = get_already_generated_coins(prev_id);
|
||||
std::vector<size_t> block_sizes;
|
||||
get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
|
||||
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list);
|
||||
}
|
||||
|
||||
bool test_generator::construct_block_manually(block& blk, const block& prev_block, const 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 difficulty_type& diffic/* = 1*/,
|
||||
const transaction& miner_tx/* = transaction()*/,
|
||||
const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/,
|
||||
size_t txs_sizes/* = 0*/)
|
||||
{
|
||||
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;
|
||||
blk.timestamp = actual_params & bf_timestamp ? timestamp : prev_block.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; // Keep difficulty unchanged
|
||||
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<crypto::hash>();
|
||||
|
||||
size_t height = get_block_height(prev_block) + 1;
|
||||
uint64_t already_generated_coins = get_already_generated_coins(prev_block);
|
||||
std::vector<size_t> block_sizes;
|
||||
get_last_n_block_sizes(block_sizes, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
if (actual_params & bf_miner_tx)
|
||||
{
|
||||
blk.miner_tx = miner_tx;
|
||||
}
|
||||
else
|
||||
{
|
||||
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, already_generated_coins, miner_acc.get_keys().m_account_address, blk.miner_tx, 0, block_sizes, current_block_size, 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
//blk.tree_root_hash = get_tx_tree_hash(blk);
|
||||
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
|
||||
const cryptonote::account_base& miner_acc,
|
||||
const std::vector<crypto::hash>& tx_hashes, size_t txs_size)
|
||||
{
|
||||
return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size);
|
||||
}
|
||||
|
||||
|
||||
struct output_index {
|
||||
const cryptonote::txout_target_v out;
|
||||
uint64_t amount;
|
||||
size_t blk_height; // block height
|
||||
size_t tx_no; // index of transaction in block
|
||||
size_t out_no; // index of out in transaction
|
||||
size_t idx;
|
||||
bool spent;
|
||||
const cryptonote::block *p_blk;
|
||||
const cryptonote::transaction *p_tx;
|
||||
|
||||
output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt)
|
||||
: out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { }
|
||||
|
||||
output_index(const output_index &other)
|
||||
: out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { }
|
||||
|
||||
const std::string toString() const {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "output_index{blk_height=" << blk_height
|
||||
<< " tx_no=" << tx_no
|
||||
<< " out_no=" << out_no
|
||||
<< " amount=" << amount
|
||||
<< " idx=" << idx
|
||||
<< " spent=" << spent
|
||||
<< "}";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
output_index& operator=(const output_index& other)
|
||||
{
|
||||
new(this) output_index(other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<uint64_t, std::vector<size_t> > map_output_t;
|
||||
typedef std::map<uint64_t, std::vector<output_index> > map_output_idx_t;
|
||||
typedef pair<uint64_t, size_t> outloc_t;
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t get_inputs_amount(const vector<tx_source_entry> &s)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
BOOST_FOREACH(const tx_source_entry &e, s)
|
||||
{
|
||||
r += e.amount;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector<size_t> >& outs_mine, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) {
|
||||
|
||||
BOOST_FOREACH (const block& blk, blockchain) {
|
||||
vector<const transaction*> vtx;
|
||||
vtx.push_back(&blk.miner_tx);
|
||||
|
||||
BOOST_FOREACH(const crypto::hash &h, blk.tx_hashes) {
|
||||
const map_hash2tx_t::const_iterator cit = mtx.find(h);
|
||||
if (mtx.end() == cit)
|
||||
throw std::runtime_error("block contains an unknown tx hash");
|
||||
|
||||
vtx.push_back(cit->second);
|
||||
}
|
||||
|
||||
//vtx.insert(vtx.end(), blk.);
|
||||
// TODO: add all other txes
|
||||
for (size_t i = 0; i < vtx.size(); i++) {
|
||||
const transaction &tx = *vtx[i];
|
||||
|
||||
for (size_t j = 0; j < tx.vout.size(); ++j) {
|
||||
const tx_out &out = tx.vout[j];
|
||||
|
||||
output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]);
|
||||
|
||||
if (2 == out.target.which()) { // out_to_key
|
||||
outs[out.amount].push_back(oi);
|
||||
size_t tx_global_idx = outs[out.amount].size() - 1;
|
||||
outs[out.amount][tx_global_idx].idx = tx_global_idx;
|
||||
// Is out to me?
|
||||
if (is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), j)) {
|
||||
outs_mine[out.amount].push_back(tx_global_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) {
|
||||
|
||||
BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) {
|
||||
for (size_t i = 0; i < o.second.size(); ++i) {
|
||||
output_index &oi = outs[o.first][o.second[i]];
|
||||
|
||||
// construct key image for this output
|
||||
crypto::key_image img;
|
||||
keypair in_ephemeral;
|
||||
generate_key_image_helper(from.get_keys(), get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img);
|
||||
|
||||
// lookup for this key image in the events vector
|
||||
BOOST_FOREACH(auto& tx_pair, mtx) {
|
||||
const transaction& tx = *tx_pair.second;
|
||||
BOOST_FOREACH(const txin_v &in, tx.vin) {
|
||||
if (typeid(txin_to_key) == in.type()) {
|
||||
const txin_to_key &itk = boost::get<txin_to_key>(in);
|
||||
if (itk.k_image == img) {
|
||||
oi.spent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_out, size_t nmix, size_t& real_entry_idx, std::vector<tx_source_entry::output_entry>& output_entries)
|
||||
{
|
||||
if (out_indices.size() <= nmix)
|
||||
return false;
|
||||
|
||||
bool sender_out_found = false;
|
||||
size_t rest = nmix;
|
||||
for (size_t i = 0; i < out_indices.size() && (0 < rest || !sender_out_found); ++i)
|
||||
{
|
||||
const output_index& oi = out_indices[i];
|
||||
if (oi.spent)
|
||||
continue;
|
||||
|
||||
bool append = false;
|
||||
if (i == sender_out)
|
||||
{
|
||||
append = true;
|
||||
sender_out_found = true;
|
||||
real_entry_idx = output_entries.size();
|
||||
}
|
||||
else if (0 < rest)
|
||||
{
|
||||
--rest;
|
||||
append = true;
|
||||
}
|
||||
|
||||
if (append)
|
||||
{
|
||||
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
|
||||
output_entries.push_back(tx_source_entry::output_entry(oi.idx, otk.key));
|
||||
}
|
||||
}
|
||||
|
||||
return 0 == rest && sender_out_found;
|
||||
}
|
||||
|
||||
bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<test_event_entry>& events,
|
||||
const block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix)
|
||||
{
|
||||
map_output_idx_t outs;
|
||||
map_output_t outs_mine;
|
||||
|
||||
std::vector<cryptonote::block> blockchain;
|
||||
map_hash2tx_t mtx;
|
||||
if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)))
|
||||
return false;
|
||||
|
||||
if (!init_output_indices(outs, outs_mine, blockchain, mtx, from))
|
||||
return false;
|
||||
|
||||
if (!init_spent_output_indices(outs, outs_mine, blockchain, mtx, from))
|
||||
return false;
|
||||
|
||||
// Iterate in reverse is more efficiency
|
||||
uint64_t sources_amount = 0;
|
||||
bool sources_found = false;
|
||||
BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine)
|
||||
{
|
||||
for (size_t i = 0; i < o.second.size() && !sources_found; ++i)
|
||||
{
|
||||
size_t sender_out = o.second[i];
|
||||
const output_index& oi = outs[o.first][sender_out];
|
||||
if (oi.spent)
|
||||
continue;
|
||||
|
||||
cryptonote::tx_source_entry ts;
|
||||
ts.amount = oi.amount;
|
||||
ts.real_output_in_tx_index = oi.out_no;
|
||||
ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key
|
||||
if (!fill_output_entries(outs[o.first], sender_out, nmix, ts.real_output, ts.outputs))
|
||||
continue;
|
||||
|
||||
sources.push_back(ts);
|
||||
|
||||
sources_amount += ts.amount;
|
||||
sources_found = amount <= sources_amount;
|
||||
}
|
||||
|
||||
if (sources_found)
|
||||
break;
|
||||
}
|
||||
|
||||
return sources_found;
|
||||
}
|
||||
|
||||
bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_base &to, uint64_t amount) {
|
||||
de.addr = to.get_keys().m_account_address;
|
||||
de.amount = amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head,
|
||||
const cryptonote::account_base& from, const cryptonote::account_base& to,
|
||||
uint64_t amount, uint64_t fee, size_t nmix, std::vector<tx_source_entry>& sources,
|
||||
std::vector<tx_destination_entry>& destinations)
|
||||
{
|
||||
sources.clear();
|
||||
destinations.clear();
|
||||
|
||||
if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix))
|
||||
throw std::runtime_error("couldn't fill transaction sources");
|
||||
|
||||
tx_destination_entry de;
|
||||
if (!fill_tx_destination(de, to, amount))
|
||||
throw std::runtime_error("couldn't fill transaction destination");
|
||||
destinations.push_back(de);
|
||||
|
||||
tx_destination_entry de_change;
|
||||
uint64_t cache_back = get_inputs_amount(sources) - (amount + fee);
|
||||
if (0 < cache_back)
|
||||
{
|
||||
if (!fill_tx_destination(de_change, from, cache_back))
|
||||
throw std::runtime_error("couldn't fill transaction cache back destination");
|
||||
destinations.push_back(de_change);
|
||||
}
|
||||
}
|
||||
|
||||
void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height)
|
||||
{
|
||||
blk.nonce = 0;
|
||||
while (!miner::find_nonce_for_given_block(blk, diffic, height))
|
||||
blk.timestamp++;
|
||||
}
|
||||
|
||||
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
||||
const account_public_address& miner_address, transaction& tx, uint64_t fee,
|
||||
keypair* p_txkey/* = 0*/)
|
||||
{
|
||||
keypair txkey;
|
||||
txkey = keypair::generate();
|
||||
add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
|
||||
if (0 != p_txkey)
|
||||
*p_txkey = txkey;
|
||||
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
// This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
||||
std::vector<size_t> block_sizes;
|
||||
bool block_too_big = false;
|
||||
uint64_t block_reward = get_block_reward(block_sizes, 0, block_too_big, already_generated_coins) + fee;
|
||||
if (block_too_big)
|
||||
{
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
|
||||
tx_out out;
|
||||
out.amount = block_reward;
|
||||
out.target = txout_to_key(out_eph_public_key);
|
||||
tx.vout.push_back(out);
|
||||
|
||||
tx.version = CURRENT_TRANSACTION_VERSION;
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const block& blk_head,
|
||||
const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount,
|
||||
uint64_t fee, size_t nmix)
|
||||
{
|
||||
vector<tx_source_entry> sources;
|
||||
vector<tx_destination_entry> destinations;
|
||||
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations);
|
||||
|
||||
return construct_tx(from.get_keys(), sources, destinations, tx, 0);
|
||||
}
|
||||
|
||||
transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head,
|
||||
const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee)
|
||||
{
|
||||
transaction tx;
|
||||
construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 0);
|
||||
events.push_back(tx);
|
||||
return tx;
|
||||
}
|
||||
|
||||
uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx) {
|
||||
uint64_t res = 0;
|
||||
std::map<uint64_t, std::vector<output_index> > outs;
|
||||
std::map<uint64_t, std::vector<size_t> > outs_mine;
|
||||
|
||||
map_hash2tx_t confirmed_txs;
|
||||
get_confirmed_txs(blockchain, mtx, confirmed_txs);
|
||||
|
||||
if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
|
||||
return false;
|
||||
|
||||
if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
|
||||
return false;
|
||||
|
||||
BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) {
|
||||
for (size_t i = 0; i < o.second.size(); ++i) {
|
||||
if (outs[o.first][o.second[i]].spent)
|
||||
continue;
|
||||
|
||||
res += outs[o.first][o.second[i]].amount;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs)
|
||||
{
|
||||
std::unordered_set<crypto::hash> confirmed_hashes;
|
||||
BOOST_FOREACH(const block& blk, blockchain)
|
||||
{
|
||||
BOOST_FOREACH(const crypto::hash& tx_hash, blk.tx_hashes)
|
||||
{
|
||||
confirmed_hashes.insert(tx_hash);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const auto& tx_pair, mtx)
|
||||
{
|
||||
if (0 != confirmed_hashes.count(tx_pair.first))
|
||||
{
|
||||
confirmed_txs.insert(tx_pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<cryptonote::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) {
|
||||
std::unordered_map<crypto::hash, const block*> block_index;
|
||||
BOOST_FOREACH(const test_event_entry& ev, events)
|
||||
{
|
||||
if (typeid(block) == ev.type())
|
||||
{
|
||||
const block* blk = &boost::get<block>(ev);
|
||||
block_index[get_block_hash(*blk)] = blk;
|
||||
}
|
||||
else if (typeid(transaction) == ev.type())
|
||||
{
|
||||
const transaction& tx = boost::get<transaction>(ev);
|
||||
mtx[get_transaction_hash(tx)] = &tx;
|
||||
}
|
||||
}
|
||||
|
||||
bool b_success = false;
|
||||
crypto::hash id = head;
|
||||
for (auto it = block_index.find(id); block_index.end() != it; it = block_index.find(id))
|
||||
{
|
||||
blockchain.push_back(*it->second);
|
||||
id = it->second->prev_id;
|
||||
if (null_hash == id)
|
||||
{
|
||||
b_success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
reverse(blockchain.begin(), blockchain.end());
|
||||
|
||||
return b_success;
|
||||
}
|
||||
|
||||
|
||||
void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb)
|
||||
{
|
||||
m_callbacks[cb_name] = cb;
|
||||
}
|
||||
bool test_chain_unit_base::verify(const std::string& cb_name, cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
auto cb_it = m_callbacks.find(cb_name);
|
||||
if(cb_it == m_callbacks.end())
|
||||
{
|
||||
LOG_ERROR("Failed to find callback " << cb_name);
|
||||
return false;
|
||||
}
|
||||
return cb_it->second(c, ev_index, events);
|
||||
}
|
||||
646
tests/core_tests/chaingen.h
Normal file
646
tests/core_tests/chaingen.h
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/variant.hpp>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "common/boost_serialization_helper.h"
|
||||
#include "common/command_line.h"
|
||||
|
||||
#include "cryptonote_core/account_boost_serialization.h"
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "cryptonote_core/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/cryptonote_boost_serialization.h"
|
||||
#include "misc_language.h"
|
||||
|
||||
|
||||
namespace concolor
|
||||
{
|
||||
inline std::basic_ostream<char, std::char_traits<char> >& bright_white(std::basic_ostream<char, std::char_traits<char> >& ostr)
|
||||
{
|
||||
epee::log_space::set_console_color(epee::log_space::console_color_white, true);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::basic_ostream<char, std::char_traits<char> >& red(std::basic_ostream<char, std::char_traits<char> >& ostr)
|
||||
{
|
||||
epee::log_space::set_console_color(epee::log_space::console_color_red, true);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::basic_ostream<char, std::char_traits<char> >& green(std::basic_ostream<char, std::char_traits<char> >& ostr)
|
||||
{
|
||||
epee::log_space::set_console_color(epee::log_space::console_color_green, true);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::basic_ostream<char, std::char_traits<char> >& magenta(std::basic_ostream<char, std::char_traits<char> >& ostr)
|
||||
{
|
||||
epee::log_space::set_console_color(epee::log_space::console_color_magenta, true);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::basic_ostream<char, std::char_traits<char> >& yellow(std::basic_ostream<char, std::char_traits<char> >& ostr)
|
||||
{
|
||||
epee::log_space::set_console_color(epee::log_space::console_color_yellow, true);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::basic_ostream<char, std::char_traits<char> >& normal(std::basic_ostream<char, std::char_traits<char> >& ostr)
|
||||
{
|
||||
epee::log_space::reset_console_color();
|
||||
return ostr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct callback_entry
|
||||
{
|
||||
std::string callback_name;
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(callback_name)
|
||||
END_SERIALIZE()
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
||||
{
|
||||
ar & callback_name;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct serialized_object
|
||||
{
|
||||
serialized_object() { }
|
||||
|
||||
serialized_object(const cryptonote::blobdata& a_data)
|
||||
: data(a_data)
|
||||
{
|
||||
}
|
||||
|
||||
cryptonote::blobdata data;
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(data)
|
||||
END_SERIALIZE()
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
||||
{
|
||||
ar & data;
|
||||
}
|
||||
};
|
||||
|
||||
typedef serialized_object<cryptonote::block> serialized_block;
|
||||
typedef serialized_object<cryptonote::transaction> serialized_transaction;
|
||||
|
||||
struct event_visitor_settings
|
||||
{
|
||||
int valid_mask;
|
||||
bool txs_keeped_by_block;
|
||||
|
||||
enum settings
|
||||
{
|
||||
set_txs_keeped_by_block = 1 << 0
|
||||
};
|
||||
|
||||
event_visitor_settings(int a_valid_mask = 0, bool a_txs_keeped_by_block = false)
|
||||
: valid_mask(a_valid_mask)
|
||||
, txs_keeped_by_block(a_txs_keeped_by_block)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
||||
{
|
||||
ar & valid_mask;
|
||||
ar & txs_keeped_by_block;
|
||||
}
|
||||
};
|
||||
|
||||
VARIANT_TAG(binary_archive, callback_entry, 0xcb);
|
||||
VARIANT_TAG(binary_archive, cryptonote::account_base, 0xcc);
|
||||
VARIANT_TAG(binary_archive, serialized_block, 0xcd);
|
||||
VARIANT_TAG(binary_archive, serialized_transaction, 0xce);
|
||||
VARIANT_TAG(binary_archive, event_visitor_settings, 0xcf);
|
||||
|
||||
typedef boost::variant<cryptonote::block, cryptonote::transaction, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings> test_event_entry;
|
||||
typedef std::unordered_map<crypto::hash, const cryptonote::transaction*> map_hash2tx_t;
|
||||
|
||||
class test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
typedef boost::function<bool (cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)> verify_callback;
|
||||
typedef std::map<std::string, verify_callback> callbacks_map;
|
||||
|
||||
void register_callback(const std::string& cb_name, verify_callback cb);
|
||||
bool verify(const std::string& cb_name, cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
private:
|
||||
callbacks_map m_callbacks;
|
||||
};
|
||||
|
||||
|
||||
class test_generator
|
||||
{
|
||||
public:
|
||||
struct block_info
|
||||
{
|
||||
block_info()
|
||||
: prev_id()
|
||||
, already_generated_coins(0)
|
||||
, block_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_size)
|
||||
: prev_id(a_prev_id)
|
||||
, already_generated_coins(an_already_generated_coins)
|
||||
, block_size(a_block_size)
|
||||
{
|
||||
}
|
||||
|
||||
crypto::hash prev_id;
|
||||
uint64_t already_generated_coins;
|
||||
size_t block_size;
|
||||
};
|
||||
|
||||
enum block_fields
|
||||
{
|
||||
bf_none = 0,
|
||||
bf_major_ver = 1 << 0,
|
||||
bf_minor_ver = 1 << 1,
|
||||
bf_timestamp = 1 << 2,
|
||||
bf_prev_id = 1 << 3,
|
||||
bf_miner_tx = 1 << 4,
|
||||
bf_tx_hashes = 1 << 5,
|
||||
bf_diffic = 1 << 6
|
||||
};
|
||||
|
||||
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
|
||||
void get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const;
|
||||
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<size_t>& block_sizes, uint64_t already_generated_coins);
|
||||
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<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list);
|
||||
bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp);
|
||||
bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc,
|
||||
const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>());
|
||||
|
||||
bool construct_block_manually(cryptonote::block& blk, const cryptonote::block& prev_block,
|
||||
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<crypto::hash>& tx_hashes = std::vector<crypto::hash>(), size_t txs_sizes = 0);
|
||||
bool construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
|
||||
const cryptonote::account_base& miner_acc, const std::vector<crypto::hash>& tx_hashes, size_t txs_size);
|
||||
|
||||
private:
|
||||
std::unordered_map<crypto::hash, block_info> m_blocks_info;
|
||||
};
|
||||
|
||||
inline cryptonote::difficulty_type get_test_difficulty() {return 1;}
|
||||
void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height);
|
||||
|
||||
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
||||
const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx,
|
||||
uint64_t fee, cryptonote::keypair* p_txkey = 0);
|
||||
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx,
|
||||
const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to,
|
||||
uint64_t amount, uint64_t fee, size_t nmix);
|
||||
cryptonote::transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const cryptonote::block& blk_head,
|
||||
const cryptonote::account_base& acc_from, const cryptonote::account_base& acc_to,
|
||||
uint64_t amount, uint64_t fee);
|
||||
|
||||
void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs);
|
||||
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<cryptonote::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head);
|
||||
void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const cryptonote::block& blk_head,
|
||||
const cryptonote::account_base& from, const cryptonote::account_base& to,
|
||||
uint64_t amount, uint64_t fee, size_t nmix,
|
||||
std::vector<cryptonote::tx_source_entry>& sources,
|
||||
std::vector<cryptonote::tx_destination_entry>& destinations);
|
||||
uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
auto do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator, int)
|
||||
-> decltype(validator.check_tx_verification_context(tvc, tx_added, event_index, tx))
|
||||
{
|
||||
return validator.check_tx_verification_context(tvc, tx_added, event_index, tx);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
bool do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t /*event_index*/, const cryptonote::transaction& /*tx*/, t_test_class&, long)
|
||||
{
|
||||
// Default block verification context check
|
||||
if (tvc.m_verifivation_failed)
|
||||
throw std::runtime_error("Transaction verification failed");
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator)
|
||||
{
|
||||
// SFINAE in action
|
||||
return do_check_tx_verification_context(tvc, tx_added, event_index, tx, validator, 0);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
auto do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator, int)
|
||||
-> decltype(validator.check_block_verification_context(bvc, event_index, blk))
|
||||
{
|
||||
return validator.check_block_verification_context(bvc, event_index, blk);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
bool do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t /*event_index*/, const cryptonote::block& /*blk*/, t_test_class&, long)
|
||||
{
|
||||
// Default block verification context check
|
||||
if (bvc.m_verifivation_failed)
|
||||
throw std::runtime_error("Block verification failed");
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator)
|
||||
{
|
||||
// SFINAE in action
|
||||
return do_check_block_verification_context(bvc, event_index, blk, validator, 0);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
template<class t_test_class>
|
||||
struct push_core_event_visitor: public boost::static_visitor<bool>
|
||||
{
|
||||
private:
|
||||
cryptonote::core& m_c;
|
||||
const std::vector<test_event_entry>& m_events;
|
||||
t_test_class& m_validator;
|
||||
size_t m_ev_index;
|
||||
|
||||
bool m_txs_keeped_by_block;
|
||||
|
||||
public:
|
||||
push_core_event_visitor(cryptonote::core& c, const std::vector<test_event_entry>& events, t_test_class& validator)
|
||||
: m_c(c)
|
||||
, m_events(events)
|
||||
, m_validator(validator)
|
||||
, m_ev_index(0)
|
||||
, m_txs_keeped_by_block(false)
|
||||
{
|
||||
}
|
||||
|
||||
void event_index(size_t ev_index)
|
||||
{
|
||||
m_ev_index = ev_index;
|
||||
}
|
||||
|
||||
bool operator()(const event_visitor_settings& settings)
|
||||
{
|
||||
log_event("event_visitor_settings");
|
||||
|
||||
if (settings.valid_mask & event_visitor_settings::set_txs_keeped_by_block)
|
||||
{
|
||||
m_txs_keeped_by_block = settings.txs_keeped_by_block;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const cryptonote::transaction& tx) const
|
||||
{
|
||||
log_event("cryptonote::transaction");
|
||||
|
||||
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
|
||||
size_t pool_size = m_c.get_pool_transactions_count();
|
||||
m_c.handle_incoming_tx(t_serializable_object_to_blob(tx), tvc, m_txs_keeped_by_block);
|
||||
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
|
||||
bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const cryptonote::block& b) const
|
||||
{
|
||||
log_event("cryptonote::block");
|
||||
|
||||
cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc);
|
||||
m_c.handle_incoming_block(t_serializable_object_to_blob(b), bvc);
|
||||
bool r = check_block_verification_context(bvc, m_ev_index, b, m_validator);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed");
|
||||
return r;
|
||||
}
|
||||
|
||||
bool operator()(const callback_entry& cb) const
|
||||
{
|
||||
log_event(std::string("callback_entry ") + cb.callback_name);
|
||||
return m_validator.verify(cb.callback_name, m_c, m_ev_index, m_events);
|
||||
}
|
||||
|
||||
bool operator()(const cryptonote::account_base& ab) const
|
||||
{
|
||||
log_event("cryptonote::account_base");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const serialized_block& sr_block) const
|
||||
{
|
||||
log_event("serialized_block");
|
||||
|
||||
cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc);
|
||||
m_c.handle_incoming_block(sr_block.data, bvc);
|
||||
|
||||
cryptonote::block blk;
|
||||
std::stringstream ss;
|
||||
ss << sr_block.data;
|
||||
binary_archive<false> ba(ss);
|
||||
::serialization::serialize(ba, blk);
|
||||
if (!ss.good())
|
||||
{
|
||||
blk = cryptonote::block();
|
||||
}
|
||||
bool r = check_block_verification_context(bvc, m_ev_index, blk, m_validator);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const serialized_transaction& sr_tx) const
|
||||
{
|
||||
log_event("serialized_transaction");
|
||||
|
||||
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
|
||||
size_t pool_size = m_c.get_pool_transactions_count();
|
||||
m_c.handle_incoming_tx(sr_tx.data, tvc, m_txs_keeped_by_block);
|
||||
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
|
||||
|
||||
cryptonote::transaction tx;
|
||||
std::stringstream ss;
|
||||
ss << sr_tx.data;
|
||||
binary_archive<false> ba(ss);
|
||||
::serialization::serialize(ba, tx);
|
||||
if (!ss.good())
|
||||
{
|
||||
tx = cryptonote::transaction();
|
||||
}
|
||||
|
||||
bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "transaction verification context check failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void log_event(const std::string& event_type) const
|
||||
{
|
||||
std::cout << concolor::yellow << "=== EVENT # " << m_ev_index << ": " << event_type << concolor::normal << std::endl;
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
inline bool replay_events_through_core(cryptonote::core& cr, const std::vector<test_event_entry>& events, t_test_class& validator)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
//init core here
|
||||
|
||||
CHECK_AND_ASSERT_MES(typeid(cryptonote::block) == events[0].type(), false, "First event must be genesis block creation");
|
||||
cr.set_genesis_block(boost::get<cryptonote::block>(events[0]));
|
||||
|
||||
bool r = true;
|
||||
push_core_event_visitor<t_test_class> visitor(cr, events, validator);
|
||||
for(size_t i = 1; i < events.size() && r; ++i)
|
||||
{
|
||||
visitor.event_index(i);
|
||||
r = boost::apply_visitor(visitor, events[i]);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
CATCH_ENTRY_L0("replay_events_through_core", false);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
inline bool do_replay_events(std::vector<test_event_entry>& events)
|
||||
{
|
||||
boost::program_options::options_description desc("Allowed options");
|
||||
cryptonote::core::init_options(desc);
|
||||
command_line::add_arg(desc, command_line::arg_data_dir);
|
||||
boost::program_options::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc, [&]()
|
||||
{
|
||||
boost::program_options::store(boost::program_options::basic_parsed_options<char>(&desc), vm);
|
||||
boost::program_options::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects
|
||||
cryptonote::core c(&pr);
|
||||
if (!c.init(vm))
|
||||
{
|
||||
std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl;
|
||||
return false;
|
||||
}
|
||||
t_test_class validator;
|
||||
return replay_events_through_core<t_test_class>(c, events, validator);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
template<class t_test_class>
|
||||
inline bool do_replay_file(const std::string& filename)
|
||||
{
|
||||
std::vector<test_event_entry> events;
|
||||
if (!tools::unserialize_obj_from_file(events, filename))
|
||||
{
|
||||
std::cout << concolor::magenta << "Failed to deserialize data from file: " << filename << concolor::normal << std::endl;
|
||||
return false;
|
||||
}
|
||||
return do_replay_events<t_test_class>(events);
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
#define GENERATE_ACCOUNT(account) \
|
||||
cryptonote::account_base account; \
|
||||
account.generate();
|
||||
|
||||
#define MAKE_ACCOUNT(VEC_EVENTS, account) \
|
||||
cryptonote::account_base account; \
|
||||
account.generate(); \
|
||||
VEC_EVENTS.push_back(account);
|
||||
|
||||
#define DO_CALLBACK(VEC_EVENTS, CB_NAME) \
|
||||
{ \
|
||||
callback_entry CALLBACK_ENTRY; \
|
||||
CALLBACK_ENTRY.callback_name = CB_NAME; \
|
||||
VEC_EVENTS.push_back(CALLBACK_ENTRY); \
|
||||
}
|
||||
|
||||
#define REGISTER_CALLBACK(CB_NAME, CLBACK) \
|
||||
register_callback(CB_NAME, boost::bind(&CLBACK, this, _1, _2, _3));
|
||||
|
||||
#define REGISTER_CALLBACK_METHOD(CLASS, METHOD) \
|
||||
register_callback(#METHOD, boost::bind(&CLASS::METHOD, this, _1, _2, _3));
|
||||
|
||||
#define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \
|
||||
test_generator generator; \
|
||||
cryptonote::block BLK_NAME; \
|
||||
generator.construct_block(BLK_NAME, MINER_ACC, TS); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
#define MAKE_NEXT_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
#define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
{ \
|
||||
std::list<cryptonote::transaction> tx_list; \
|
||||
tx_list.push_back(TX1); \
|
||||
generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, tx_list); \
|
||||
} \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
#define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \
|
||||
VEC_EVENTS.push_back(BLK_NAME);
|
||||
|
||||
#define REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT) \
|
||||
cryptonote::block BLK_NAME; \
|
||||
{ \
|
||||
cryptonote::block blk_last = PREV_BLOCK; \
|
||||
for (size_t i = 0; i < COUNT; ++i) \
|
||||
{ \
|
||||
MAKE_NEXT_BLOCK(VEC_EVENTS, blk, blk_last, MINER_ACC); \
|
||||
blk_last = blk; \
|
||||
} \
|
||||
BLK_NAME = blk_last; \
|
||||
}
|
||||
|
||||
#define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)
|
||||
|
||||
#define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
|
||||
cryptonote::transaction TX_NAME; \
|
||||
construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \
|
||||
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, 0, HEAD)
|
||||
|
||||
#define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \
|
||||
{ \
|
||||
cryptonote::transaction t; \
|
||||
construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \
|
||||
SET_NAME.push_back(t); \
|
||||
VEC_EVENTS.push_back(t); \
|
||||
}
|
||||
|
||||
#define MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, 0, HEAD)
|
||||
|
||||
#define MAKE_TX_LIST_START(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) \
|
||||
std::list<cryptonote::transaction> SET_NAME; \
|
||||
MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD);
|
||||
|
||||
#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \
|
||||
transaction TX; \
|
||||
if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \
|
||||
miner_account.get_keys().m_account_address, TX, 0, KEY)) \
|
||||
return false;
|
||||
|
||||
#define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0)
|
||||
|
||||
#define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT, VAL) VEC_EVENTS.push_back(event_visitor_settings(SETT, VAL));
|
||||
|
||||
#define GENERATE(filename, genclass) \
|
||||
{ \
|
||||
std::vector<test_event_entry> events; \
|
||||
genclass g; \
|
||||
g.generate(events); \
|
||||
if (!tools::serialize_obj_to_file(events, filename)) \
|
||||
{ \
|
||||
std::cout << concolor::magenta << "Failed to serialize data to file: " << filename << concolor::normal << std::endl; \
|
||||
throw std::runtime_error("Failed to serialize data to file"); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define PLAY(filename, genclass) \
|
||||
if(!do_replay_file<genclass>(filename)) \
|
||||
{ \
|
||||
std::cout << concolor::magenta << "Failed to pass test : " << #genclass << concolor::normal << std::endl; \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
#define GENERATE_AND_PLAY(genclass) \
|
||||
{ \
|
||||
std::vector<test_event_entry> events; \
|
||||
++tests_count; \
|
||||
bool generated = false; \
|
||||
try \
|
||||
{ \
|
||||
genclass g; \
|
||||
generated = g.generate(events);; \
|
||||
} \
|
||||
catch (const std::exception& ex) \
|
||||
{ \
|
||||
LOG_PRINT(#genclass << " generation failed: what=" << ex.what(), 0); \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
LOG_PRINT(#genclass << " generation failed: generic exception", 0); \
|
||||
} \
|
||||
if (generated && do_replay_events< genclass >(events)) \
|
||||
{ \
|
||||
std::cout << concolor::green << "#TEST# Succeeded " << #genclass << concolor::normal << '\n'; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
std::cout << concolor::magenta << "#TEST# Failed " << #genclass << concolor::normal << '\n'; \
|
||||
failed_tests.push_back(#genclass); \
|
||||
} \
|
||||
std::cout << std::endl; \
|
||||
}
|
||||
|
||||
#define CALL_TEST(test_name, function) \
|
||||
{ \
|
||||
if(!function()) \
|
||||
{ \
|
||||
std::cout << concolor::magenta << "#TEST# Failed " << test_name << concolor::normal << std::endl; \
|
||||
return 1; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
std::cout << concolor::green << "#TEST# Succeeded " << test_name << concolor::normal << std::endl; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define QUOTEME(x) #x
|
||||
#define DEFINE_TESTS_ERROR_CONTEXT(text) const char* perr_context = text;
|
||||
#define CHECK_TEST_CONDITION(cond) CHECK_AND_ASSERT_MES(cond, false, "[" << perr_context << "] failed: \"" << QUOTEME(cond) << "\"")
|
||||
#define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2)
|
||||
#define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2)
|
||||
#define MK_COINS(amount) (UINT64_C(amount) * COIN)
|
||||
#define TESTS_DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6)
|
||||
135
tests/core_tests/chaingen001.cpp
Normal file
135
tests/core_tests/chaingen001.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
|
||||
#include "console_handler.h"
|
||||
|
||||
#include "cryptonote_core/cryptonote_basic.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
////////
|
||||
// class one_block;
|
||||
|
||||
one_block::one_block()
|
||||
{
|
||||
REGISTER_CALLBACK("verify_1", one_block::verify_1);
|
||||
}
|
||||
|
||||
bool one_block::generate(std::vector<test_event_entry> &events)
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, alice, ts_start);
|
||||
MAKE_ACCOUNT(events, alice);
|
||||
DO_CALLBACK(events, "verify_1");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("one_block::verify_1");
|
||||
|
||||
alice = boost::get<cryptonote::account_base>(events[1]);
|
||||
|
||||
// check balances
|
||||
//std::vector<const cryptonote::block*> chain;
|
||||
//map_hash2tx_t mtx;
|
||||
//CHECK_TEST_CONDITION(find_block_chain(events, chain, mtx, get_block_hash(boost::get<cryptonote::block>(events[1]))));
|
||||
//CHECK_TEST_CONDITION(get_block_reward(0) == get_balance(alice, events, chain, mtx));
|
||||
|
||||
// check height
|
||||
std::list<cryptonote::block> blocks;
|
||||
std::list<crypto::public_key> outs;
|
||||
bool r = c.get_blocks(0, 100, blocks);
|
||||
//c.get_outs(100, outs);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_TEST_CONDITION(blocks.size() == 1);
|
||||
//CHECK_TEST_CONDITION(outs.size() == blocks.size());
|
||||
CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 1);
|
||||
CHECK_TEST_CONDITION(blocks.back() == boost::get<cryptonote::block>(events[0]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// class gen_simple_chain_001;
|
||||
|
||||
gen_simple_chain_001::gen_simple_chain_001()
|
||||
{
|
||||
REGISTER_CALLBACK("verify_callback_1", gen_simple_chain_001::verify_callback_1);
|
||||
REGISTER_CALLBACK("verify_callback_2", gen_simple_chain_001::verify_callback_2);
|
||||
}
|
||||
|
||||
bool gen_simple_chain_001::generate(std::vector<test_event_entry> &events)
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
|
||||
GENERATE_ACCOUNT(miner);
|
||||
GENERATE_ACCOUNT(alice);
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner, ts_start);
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner);
|
||||
MAKE_NEXT_BLOCK(events, blk_1_side, blk_0, miner);
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner);
|
||||
//MAKE_TX(events, tx_0, first_miner_account, alice, 151, blk_2);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
/*bool r = */find_block_chain(events, chain, mtx, get_block_hash(boost::get<cryptonote::block>(events[3])));
|
||||
std::cout << "BALANCE = " << get_balance(miner, chain, mtx) << std::endl;
|
||||
|
||||
REWIND_BLOCKS(events, blk_2r, blk_2, miner);
|
||||
MAKE_TX_LIST_START(events, txlist_0, miner, alice, MK_COINS(1), blk_2);
|
||||
MAKE_TX_LIST(events, txlist_0, miner, alice, MK_COINS(2), blk_2);
|
||||
MAKE_TX_LIST(events, txlist_0, miner, alice, MK_COINS(4), blk_2);
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner, txlist_0);
|
||||
REWIND_BLOCKS(events, blk_3r, blk_3, miner);
|
||||
MAKE_TX(events, tx_1, miner, alice, MK_COINS(50), blk_3);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3r, miner, tx_1);
|
||||
REWIND_BLOCKS(events, blk_4r, blk_4, miner);
|
||||
MAKE_TX(events, tx_2, miner, alice, MK_COINS(50), blk_4);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner, tx_2);
|
||||
REWIND_BLOCKS(events, blk_5r, blk_5, miner);
|
||||
MAKE_TX(events, tx_3, miner, alice, MK_COINS(50), blk_5);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner, tx_3);
|
||||
|
||||
DO_CALLBACK(events, "verify_callback_1");
|
||||
//e.t.c.
|
||||
//MAKE_BLOCK_TX1(events, blk_3, 3, get_block_hash(blk_0), get_test_target(), first_miner_account, ts_start + 10, tx_0);
|
||||
//MAKE_BLOCK_TX1(events, blk_3, 3, get_block_hash(blk_0), get_test_target(), first_miner_account, ts_start + 10, tx_0);
|
||||
//DO_CALLBACK(events, "verify_callback_2");
|
||||
|
||||
/* std::vector<const cryptonote::block*> chain;
|
||||
map_hash2tx_t mtx;
|
||||
if (!find_block_chain(events, chain, mtx, get_block_hash(blk_6)))
|
||||
throw;
|
||||
cout << "miner = " << get_balance(first_miner_account, events, chain, mtx) << endl;
|
||||
cout << "alice = " << get_balance(alice, events, chain, mtx) << endl;*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_simple_chain_001::verify_callback_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_simple_chain_001::verify_callback_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
171
tests/core_tests/chaingen_main.cpp
Normal file
171
tests/core_tests/chaingen_main.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
#include "common/command_line.h"
|
||||
#include "transaction_tests.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
namespace
|
||||
{
|
||||
const command_line::arg_descriptor<std::string> arg_test_data_path = {"test_data_path", "", ""};
|
||||
const command_line::arg_descriptor<bool> arg_generate_test_data = {"generate_test_data", ""};
|
||||
const command_line::arg_descriptor<bool> arg_play_test_data = {"play_test_data", ""};
|
||||
const command_line::arg_descriptor<bool> arg_generate_and_play_test_data = {"generate_and_play_test_data", ""};
|
||||
const command_line::arg_descriptor<bool> arg_test_transactions = {"test_transactions", ""};
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
||||
//set up logging options
|
||||
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3);
|
||||
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
|
||||
|
||||
log_space::log_singletone::add_logger(LOGGER_FILE,
|
||||
log_space::log_singletone::get_default_log_file().c_str(),
|
||||
log_space::log_singletone::get_default_log_folder().c_str());
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
command_line::add_arg(desc_options, command_line::arg_help);
|
||||
command_line::add_arg(desc_options, arg_test_data_path);
|
||||
command_line::add_arg(desc_options, arg_generate_test_data);
|
||||
command_line::add_arg(desc_options, arg_play_test_data);
|
||||
command_line::add_arg(desc_options, arg_generate_and_play_test_data);
|
||||
command_line::add_arg(desc_options, arg_test_transactions);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
po::store(po::parse_command_line(argc, argv, desc_options), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << desc_options << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t tests_count = 0;
|
||||
std::vector<std::string> failed_tests;
|
||||
std::string tests_folder = command_line::get_arg(vm, arg_test_data_path);
|
||||
if (command_line::get_arg(vm, arg_generate_test_data))
|
||||
{
|
||||
GENERATE("chain001.dat", gen_simple_chain_001);
|
||||
}
|
||||
else if (command_line::get_arg(vm, arg_play_test_data))
|
||||
{
|
||||
PLAY("chain001.dat", gen_simple_chain_001);
|
||||
}
|
||||
else if (command_line::get_arg(vm, arg_generate_and_play_test_data))
|
||||
{
|
||||
GENERATE_AND_PLAY(gen_simple_chain_001);
|
||||
GENERATE_AND_PLAY(gen_simple_chain_split_1);
|
||||
GENERATE_AND_PLAY(one_block);
|
||||
GENERATE_AND_PLAY(gen_chain_switch_1);
|
||||
GENERATE_AND_PLAY(gen_ring_signature_1);
|
||||
GENERATE_AND_PLAY(gen_ring_signature_2);
|
||||
//GENERATE_AND_PLAY(gen_ring_signature_big); // Takes up to XXX hours (if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10)
|
||||
|
||||
// Block verification tests
|
||||
GENERATE_AND_PLAY(gen_block_big_major_version);
|
||||
GENERATE_AND_PLAY(gen_block_big_minor_version);
|
||||
GENERATE_AND_PLAY(gen_block_ts_not_checked);
|
||||
GENERATE_AND_PLAY(gen_block_ts_in_past);
|
||||
GENERATE_AND_PLAY(gen_block_ts_in_future);
|
||||
GENERATE_AND_PLAY(gen_block_invalid_prev_id);
|
||||
GENERATE_AND_PLAY(gen_block_invalid_nonce);
|
||||
GENERATE_AND_PLAY(gen_block_no_miner_tx);
|
||||
GENERATE_AND_PLAY(gen_block_unlock_time_is_low);
|
||||
GENERATE_AND_PLAY(gen_block_unlock_time_is_high);
|
||||
GENERATE_AND_PLAY(gen_block_unlock_time_is_timestamp_in_past);
|
||||
GENERATE_AND_PLAY(gen_block_unlock_time_is_timestamp_in_future);
|
||||
GENERATE_AND_PLAY(gen_block_height_is_low);
|
||||
GENERATE_AND_PLAY(gen_block_height_is_high);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_2_tx_gen_in);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_2_in);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_with_txin_to_key);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_is_small);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_out_is_big);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_no_out);
|
||||
GENERATE_AND_PLAY(gen_block_miner_tx_has_out_to_alice);
|
||||
GENERATE_AND_PLAY(gen_block_has_invalid_tx);
|
||||
GENERATE_AND_PLAY(gen_block_is_too_big);
|
||||
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_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_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);
|
||||
|
||||
// Double spend
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_tx<false>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_tx<true>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_the_same_block<false>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_the_same_block<true>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_different_blocks<false>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_different_blocks<true>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_different_chains);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_alt_chain_in_the_same_block<false>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_alt_chain_in_the_same_block<true>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_alt_chain_in_different_blocks<false>);
|
||||
GENERATE_AND_PLAY(gen_double_spend_in_alt_chain_in_different_blocks<true>);
|
||||
|
||||
GENERATE_AND_PLAY(gen_uint_overflow_1);
|
||||
GENERATE_AND_PLAY(gen_uint_overflow_2);
|
||||
|
||||
GENERATE_AND_PLAY(gen_block_reward);
|
||||
|
||||
std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta);
|
||||
std::cout << "\nREPORT:\n";
|
||||
std::cout << " Test run: " << tests_count << '\n';
|
||||
std::cout << " Failures: " << failed_tests.size() << '\n';
|
||||
if (!failed_tests.empty())
|
||||
{
|
||||
std::cout << "FAILED TESTS:\n";
|
||||
BOOST_FOREACH(auto test_name, failed_tests)
|
||||
{
|
||||
std::cout << " " << test_name << '\n';
|
||||
}
|
||||
}
|
||||
std::cout << concolor::normal << std::endl;
|
||||
}
|
||||
else if (command_line::get_arg(vm, arg_test_transactions))
|
||||
{
|
||||
CALL_TEST("TRANSACTIONS TESTS", test_transactions);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << concolor::magenta << "Wrong arguments" << concolor::normal << std::endl;
|
||||
std::cout << desc_options << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
return failed_tests.empty() ? 0 : 1;
|
||||
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
35
tests/core_tests/chaingen_tests_list.h
Normal file
35
tests/core_tests/chaingen_tests_list.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "block_reward.h"
|
||||
#include "block_validation.h"
|
||||
#include "chain_split_1.h"
|
||||
#include "chain_switch_1.h"
|
||||
#include "double_spend.h"
|
||||
#include "integer_overflow.h"
|
||||
#include "ring_signature_1.h"
|
||||
#include "tx_validation.h"
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_simple_chain_001: public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_simple_chain_001();
|
||||
bool generate(std::vector<test_event_entry> &events);
|
||||
bool verify_callback_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
bool verify_callback_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
};
|
||||
|
||||
class one_block: public test_chain_unit_base
|
||||
{
|
||||
cryptonote::account_base alice;
|
||||
public:
|
||||
one_block();
|
||||
bool generate(std::vector<test_event_entry> &events);
|
||||
bool verify_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
|
||||
};
|
||||
70
tests/core_tests/double_spend.cpp
Normal file
70
tests/core_tests/double_spend.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
//======================================================================================================================
|
||||
|
||||
gen_double_spend_in_different_chains::gen_double_spend_in_different_chains()
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(gen_double_spend_in_different_chains, check_double_spend);
|
||||
}
|
||||
|
||||
bool gen_double_spend_in_different_chains::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
INIT_DOUBLE_SPEND_TEST();
|
||||
|
||||
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, true);
|
||||
MAKE_TX(events, tx_1, bob_account, alice_account, send_amount / 2 - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.pop_back();
|
||||
MAKE_TX(events, tx_2, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.pop_back();
|
||||
|
||||
// Main chain
|
||||
events.push_back(tx_1);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_account, tx_1);
|
||||
|
||||
// Alternative chain
|
||||
events.push_back(tx_2);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_1r, miner_account, tx_2);
|
||||
// Switch to alternative chain
|
||||
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account);
|
||||
CHECK_AND_NO_ASSERT_MES(expected_blockchain_height == get_block_height(blk_4) + 1, false, "expected_blockchain_height has invalid value");
|
||||
|
||||
DO_CALLBACK(events, "check_double_spend");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_double_spend_in_different_chains::check_double_spend(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_double_spend_in_different_chains::check_double_spend");
|
||||
|
||||
std::list<block> block_list;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<block> blocks(block_list.begin(), block_list.end());
|
||||
CHECK_EQ(expected_blockchain_height, blocks.size());
|
||||
|
||||
CHECK_EQ(1, c.get_pool_transactions_count());
|
||||
CHECK_EQ(1, c.get_alternative_blocks_count());
|
||||
|
||||
cryptonote::account_base bob_account = boost::get<cryptonote::account_base>(events[1]);
|
||||
cryptonote::account_base alice_account = boost::get<cryptonote::account_base>(events[2]);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(0, get_balance(bob_account, blocks, mtx));
|
||||
CHECK_EQ(send_amount - TESTS_DEFAULT_FEE, get_balance(alice_account, blocks, mtx));
|
||||
|
||||
return true;
|
||||
}
|
||||
125
tests/core_tests/double_spend.h
Normal file
125
tests/core_tests/double_spend.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
const size_t invalid_index_value = std::numeric_limits<size_t>::max();
|
||||
|
||||
|
||||
template<class concrete_test>
|
||||
class gen_double_spend_base : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
|
||||
gen_double_spend_base();
|
||||
|
||||
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& tx);
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& block);
|
||||
|
||||
bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool mark_invalid_tx(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
cryptonote::block m_last_valid_block;
|
||||
size_t m_invalid_tx_index;
|
||||
size_t m_invalid_block_index;
|
||||
};
|
||||
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
struct gen_double_spend_in_tx : public gen_double_spend_base< gen_double_spend_in_tx<txs_keeped_by_block> >
|
||||
{
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const bool has_invalid_tx = true;
|
||||
static const size_t expected_pool_txs_count = 0;
|
||||
static const uint64_t expected_bob_balance = send_amount;
|
||||
static const uint64_t expected_alice_balance = 0;
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
struct gen_double_spend_in_the_same_block : public gen_double_spend_base< gen_double_spend_in_the_same_block<txs_keeped_by_block> >
|
||||
{
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const bool has_invalid_tx = !txs_keeped_by_block;
|
||||
static const size_t expected_pool_txs_count = has_invalid_tx ? 1 : 2;
|
||||
static const uint64_t expected_bob_balance = send_amount;
|
||||
static const uint64_t expected_alice_balance = 0;
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
struct gen_double_spend_in_different_blocks : public gen_double_spend_base< gen_double_spend_in_different_blocks<txs_keeped_by_block> >
|
||||
{
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const bool has_invalid_tx = !txs_keeped_by_block;
|
||||
static const size_t expected_pool_txs_count = has_invalid_tx ? 0 : 1;
|
||||
static const uint64_t expected_bob_balance = 0;
|
||||
static const uint64_t expected_alice_balance = send_amount - TESTS_DEFAULT_FEE;
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
struct gen_double_spend_in_alt_chain_in_the_same_block : public gen_double_spend_base< gen_double_spend_in_alt_chain_in_the_same_block<txs_keeped_by_block> >
|
||||
{
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const bool has_invalid_tx = !txs_keeped_by_block;
|
||||
static const size_t expected_pool_txs_count = has_invalid_tx ? 1 : 2;
|
||||
static const uint64_t expected_bob_balance = send_amount;
|
||||
static const uint64_t expected_alice_balance = 0;
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
struct gen_double_spend_in_alt_chain_in_different_blocks : public gen_double_spend_base< gen_double_spend_in_alt_chain_in_different_blocks<txs_keeped_by_block> >
|
||||
{
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const bool has_invalid_tx = !txs_keeped_by_block;
|
||||
static const size_t expected_pool_txs_count = has_invalid_tx ? 1 : 2;
|
||||
static const uint64_t expected_bob_balance = send_amount;
|
||||
static const uint64_t expected_alice_balance = 0;
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
|
||||
class gen_double_spend_in_different_chains : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
static const uint64_t send_amount = MK_COINS(17);
|
||||
static const size_t expected_blockchain_height = 4 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
gen_double_spend_in_different_chains();
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
||||
bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
};
|
||||
|
||||
|
||||
#define INIT_DOUBLE_SPEND_TEST() \
|
||||
uint64_t ts_start = 1338224400; \
|
||||
GENERATE_ACCOUNT(miner_account); \
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); \
|
||||
MAKE_ACCOUNT(events, bob_account); \
|
||||
MAKE_ACCOUNT(events, alice_account); \
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); \
|
||||
MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); \
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_0); \
|
||||
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
|
||||
|
||||
|
||||
#include "double_spend.inl"
|
||||
254
tests/core_tests/double_spend.inl
Normal file
254
tests/core_tests/double_spend.inl
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
#pragma once
|
||||
|
||||
//======================================================================================================================
|
||||
|
||||
template<class concrete_test>
|
||||
gen_double_spend_base<concrete_test>::gen_double_spend_base()
|
||||
: m_invalid_tx_index(invalid_index_value)
|
||||
, m_invalid_block_index(invalid_index_value)
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(gen_double_spend_base<concrete_test>, mark_last_valid_block);
|
||||
REGISTER_CALLBACK_METHOD(gen_double_spend_base<concrete_test>, mark_invalid_tx);
|
||||
REGISTER_CALLBACK_METHOD(gen_double_spend_base<concrete_test>, mark_invalid_block);
|
||||
REGISTER_CALLBACK_METHOD(gen_double_spend_base<concrete_test>, check_double_spend);
|
||||
}
|
||||
|
||||
template<class concrete_test>
|
||||
bool gen_double_spend_base<concrete_test>::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;
|
||||
}
|
||||
|
||||
template<class concrete_test>
|
||||
bool gen_double_spend_base<concrete_test>::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;
|
||||
}
|
||||
|
||||
template<class concrete_test>
|
||||
bool gen_double_spend_base<concrete_test>::mark_last_valid_block(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
std::list<cryptonote::block> block_list;
|
||||
bool r = c.get_blocks(c.get_current_blockchain_height() - 1, 1, block_list);
|
||||
CHECK_AND_ASSERT_MES(r, false, "core::get_blocks failed");
|
||||
m_last_valid_block = block_list.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class concrete_test>
|
||||
bool gen_double_spend_base<concrete_test>::mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_tx_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class concrete_test>
|
||||
bool gen_double_spend_base<concrete_test>::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_block_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class concrete_test>
|
||||
bool gen_double_spend_base<concrete_test>::check_double_spend(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_double_spend_base::check_double_spend");
|
||||
|
||||
if (concrete_test::has_invalid_tx)
|
||||
{
|
||||
CHECK_NOT_EQ(invalid_index_value, m_invalid_tx_index);
|
||||
}
|
||||
CHECK_NOT_EQ(invalid_index_value, m_invalid_block_index);
|
||||
|
||||
std::list<cryptonote::block> block_list;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_TEST_CONDITION(m_last_valid_block == block_list.back());
|
||||
|
||||
CHECK_EQ(concrete_test::expected_pool_txs_count, c.get_pool_transactions_count());
|
||||
|
||||
cryptonote::account_base bob_account = boost::get<cryptonote::account_base>(events[1]);
|
||||
cryptonote::account_base alice_account = boost::get<cryptonote::account_base>(events[2]);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
std::vector<cryptonote::block> blocks(block_list.begin(), block_list.end());
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(concrete_test::expected_bob_balance, get_balance(bob_account, blocks, mtx));
|
||||
CHECK_EQ(concrete_test::expected_alice_balance, get_balance(alice_account, blocks, mtx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================================================================
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
INIT_DOUBLE_SPEND_TEST();
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
cryptonote::tx_source_entry se;
|
||||
se.amount = tx_0.vout[0].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key));
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0);
|
||||
se.real_output_in_tx_index = 0;
|
||||
sources.push_back(se);
|
||||
// Double spend!
|
||||
sources.push_back(se);
|
||||
|
||||
cryptonote::tx_destination_entry de;
|
||||
de.addr = alice_account.get_keys().m_account_address;
|
||||
de.amount = 2 * se.amount - TESTS_DEFAULT_FEE;
|
||||
std::vector<cryptonote::tx_destination_entry> destinations;
|
||||
destinations.push_back(de);
|
||||
|
||||
cryptonote::transaction tx_1;
|
||||
if (!construct_tx(bob_account.get_keys(), sources, destinations, tx_1, 0))
|
||||
return false;
|
||||
|
||||
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block);
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(tx_1);
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_account, tx_1);
|
||||
DO_CALLBACK(events, "check_double_spend");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
bool gen_double_spend_in_the_same_block<txs_keeped_by_block>::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
INIT_DOUBLE_SPEND_TEST();
|
||||
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block);
|
||||
|
||||
MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
cryptonote::transaction tx_1 = txs_1.front();
|
||||
auto tx_1_idx = events.size() - 1;
|
||||
// Remove tx_1, it is being inserted back a little later
|
||||
events.pop_back();
|
||||
|
||||
if (has_invalid_tx)
|
||||
{
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
}
|
||||
MAKE_TX_LIST(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.insert(events.begin() + tx_1_idx, tx_1);
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_1);
|
||||
DO_CALLBACK(events, "check_double_spend");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
bool gen_double_spend_in_different_blocks<txs_keeped_by_block>::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
INIT_DOUBLE_SPEND_TEST();
|
||||
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block);
|
||||
|
||||
// Create two identical transactions, but don't push it to events list
|
||||
MAKE_TX(events, tx_blk_2, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.pop_back();
|
||||
MAKE_TX(events, tx_blk_3, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.pop_back();
|
||||
|
||||
events.push_back(tx_blk_2);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_account, tx_blk_2);
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
|
||||
if (has_invalid_tx)
|
||||
{
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
}
|
||||
events.push_back(tx_blk_3);
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_account, tx_blk_3);
|
||||
|
||||
DO_CALLBACK(events, "check_double_spend");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
bool gen_double_spend_in_alt_chain_in_the_same_block<txs_keeped_by_block>::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
INIT_DOUBLE_SPEND_TEST();
|
||||
|
||||
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block);
|
||||
|
||||
// Main chain
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_account);
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
|
||||
// Alt chain
|
||||
MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
cryptonote::transaction tx_1 = txs_1.front();
|
||||
auto tx_1_idx = events.size() - 1;
|
||||
// Remove tx_1, it is being inserted back a little later
|
||||
events.pop_back();
|
||||
|
||||
if (has_invalid_tx)
|
||||
{
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
}
|
||||
MAKE_TX_LIST(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.insert(events.begin() + tx_1_idx, tx_1);
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_1r, miner_account, txs_1);
|
||||
|
||||
// Try to switch to alternative chain
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account);
|
||||
|
||||
DO_CALLBACK(events, "check_double_spend");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool txs_keeped_by_block>
|
||||
bool gen_double_spend_in_alt_chain_in_different_blocks<txs_keeped_by_block>::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
INIT_DOUBLE_SPEND_TEST();
|
||||
|
||||
SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block);
|
||||
|
||||
// Main chain
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_account);
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
|
||||
// Alternative chain
|
||||
MAKE_TX(events, tx_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.pop_back();
|
||||
MAKE_TX(events, tx_2, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1);
|
||||
events.pop_back();
|
||||
|
||||
events.push_back(tx_1);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_1r, miner_account, tx_1);
|
||||
|
||||
// Try to switch to alternative chain
|
||||
if (has_invalid_tx)
|
||||
{
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
}
|
||||
events.push_back(tx_2);
|
||||
DO_CALLBACK(events, "mark_invalid_block");
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_account, tx_2);
|
||||
|
||||
DO_CALLBACK(events, "check_double_spend");
|
||||
|
||||
return true;
|
||||
}
|
||||
184
tests/core_tests/integer_overflow.cpp
Normal file
184
tests/core_tests/integer_overflow.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
#include "integer_overflow.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
namespace
|
||||
{
|
||||
void split_miner_tx_outs(transaction& miner_tx, uint64_t amount_1)
|
||||
{
|
||||
uint64_t total_amount = get_outs_money_amount(miner_tx);
|
||||
uint64_t amount_2 = total_amount - amount_1;
|
||||
txout_target_v target = miner_tx.vout[0].target;
|
||||
|
||||
miner_tx.vout.clear();
|
||||
|
||||
tx_out out1;
|
||||
out1.amount = amount_1;
|
||||
out1.target = target;
|
||||
miner_tx.vout.push_back(out1);
|
||||
|
||||
tx_out out2;
|
||||
out2.amount = amount_2;
|
||||
out2.target = target;
|
||||
miner_tx.vout.push_back(out2);
|
||||
}
|
||||
|
||||
void append_tx_source_entry(std::vector<cryptonote::tx_source_entry>& sources, const transaction& tx, size_t out_idx)
|
||||
{
|
||||
cryptonote::tx_source_entry se;
|
||||
se.amount = tx.vout[out_idx].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key));
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(tx);
|
||||
se.real_output_in_tx_index = out_idx;
|
||||
|
||||
sources.push_back(se);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================================================
|
||||
|
||||
gen_uint_overflow_base::gen_uint_overflow_base()
|
||||
: m_last_valid_block_event_idx(static_cast<size_t>(-1))
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(gen_uint_overflow_1, mark_last_valid_block);
|
||||
}
|
||||
|
||||
bool gen_uint_overflow_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/)
|
||||
{
|
||||
return m_last_valid_block_event_idx < event_idx ? !tx_added && tvc.m_verifivation_failed : tx_added && !tvc.m_verifivation_failed;
|
||||
}
|
||||
|
||||
bool gen_uint_overflow_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/)
|
||||
{
|
||||
return m_last_valid_block_event_idx < event_idx ? bvc.m_verifivation_failed | bvc.m_marked_as_orphaned : !bvc.m_verifivation_failed;
|
||||
}
|
||||
|
||||
bool gen_uint_overflow_base::mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
m_last_valid_block_event_idx = ev_index - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================================================================
|
||||
|
||||
bool gen_uint_overflow_1::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);
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
MAKE_ACCOUNT(events, bob_account);
|
||||
MAKE_ACCOUNT(events, alice_account);
|
||||
|
||||
// Problem 1. Miner tx output overflow
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx_0, blk_0);
|
||||
split_miner_tx_outs(miner_tx_0, MONEY_SUPPLY);
|
||||
block blk_1;
|
||||
if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_0))
|
||||
return false;
|
||||
events.push_back(blk_1);
|
||||
|
||||
// Problem 1. Miner tx outputs overflow
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx_1, blk_1);
|
||||
split_miner_tx_outs(miner_tx_1, MONEY_SUPPLY);
|
||||
block blk_2;
|
||||
if (!generator.construct_block_manually(blk_2, blk_1, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_1))
|
||||
return false;
|
||||
events.push_back(blk_2);
|
||||
|
||||
REWIND_BLOCKS(events, blk_2r, blk_2, miner_account);
|
||||
MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2);
|
||||
MAKE_TX_LIST(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2);
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_0);
|
||||
REWIND_BLOCKS(events, blk_3r, blk_3, miner_account);
|
||||
|
||||
// Problem 2. total_fee overflow, block_reward overflow
|
||||
std::list<cryptonote::transaction> txs_1;
|
||||
// Create txs with huge fee
|
||||
txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), MONEY_SUPPLY - MK_COINS(1)));
|
||||
txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), MONEY_SUPPLY - MK_COINS(1)));
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================================================================
|
||||
|
||||
bool gen_uint_overflow_2::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);
|
||||
MAKE_ACCOUNT(events, bob_account);
|
||||
MAKE_ACCOUNT(events, alice_account);
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
|
||||
DO_CALLBACK(events, "mark_last_valid_block");
|
||||
|
||||
// Problem 1. Regular tx outputs overflow
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
for (size_t i = 0; i < blk_0.miner_tx.vout.size(); ++i)
|
||||
{
|
||||
if (TESTS_DEFAULT_FEE < blk_0.miner_tx.vout[i].amount)
|
||||
{
|
||||
append_tx_source_entry(sources, blk_0.miner_tx, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sources.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> destinations;
|
||||
const account_public_address& bob_addr = bob_account.get_keys().m_account_address;
|
||||
destinations.push_back(tx_destination_entry(MONEY_SUPPLY, bob_addr));
|
||||
destinations.push_back(tx_destination_entry(MONEY_SUPPLY - 1, bob_addr));
|
||||
// sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + TESTS_DEFAULT_FEE
|
||||
destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr));
|
||||
|
||||
cryptonote::transaction tx_1;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, tx_1, 0))
|
||||
return false;
|
||||
events.push_back(tx_1);
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_1);
|
||||
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
|
||||
|
||||
// Problem 2. Regular tx inputs overflow
|
||||
sources.clear();
|
||||
for (size_t i = 0; i < tx_1.vout.size(); ++i)
|
||||
{
|
||||
auto& tx_1_out = tx_1.vout[i];
|
||||
if (tx_1_out.amount < MONEY_SUPPLY - 1)
|
||||
continue;
|
||||
|
||||
append_tx_source_entry(sources, tx_1, i);
|
||||
}
|
||||
|
||||
destinations.clear();
|
||||
cryptonote::tx_destination_entry de;
|
||||
de.addr = alice_account.get_keys().m_account_address;
|
||||
de.amount = MONEY_SUPPLY - TESTS_DEFAULT_FEE;
|
||||
destinations.push_back(de);
|
||||
destinations.push_back(de);
|
||||
|
||||
cryptonote::transaction tx_2;
|
||||
if (!construct_tx(bob_account.get_keys(), sources, destinations, tx_2, 0))
|
||||
return false;
|
||||
events.push_back(tx_2);
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_account, tx_2);
|
||||
|
||||
return true;
|
||||
}
|
||||
29
tests/core_tests/integer_overflow.h
Normal file
29
tests/core_tests/integer_overflow.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
struct gen_uint_overflow_base : public test_chain_unit_base
|
||||
{
|
||||
gen_uint_overflow_base();
|
||||
|
||||
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& tx);
|
||||
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& block);
|
||||
|
||||
bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
size_t m_last_valid_block_event_idx;
|
||||
};
|
||||
|
||||
struct gen_uint_overflow_1 : public gen_uint_overflow_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_uint_overflow_2 : public gen_uint_overflow_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
318
tests/core_tests/ring_signature_1.cpp
Normal file
318
tests/core_tests/ring_signature_1.cpp
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
////////
|
||||
// class gen_ring_signature_1;
|
||||
|
||||
gen_ring_signature_1::gen_ring_signature_1()
|
||||
{
|
||||
REGISTER_CALLBACK("check_balances_1", gen_ring_signature_1::check_balances_1);
|
||||
REGISTER_CALLBACK("check_balances_2", gen_ring_signature_1::check_balances_2);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// To be sure that miner tx outputs don't match any bob_account and some_accounts inputs
|
||||
const uint64_t rnd_11 = 475921;
|
||||
const uint64_t rnd_20 = 360934;
|
||||
const uint64_t rnd_29 = 799665;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_1::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
|
||||
GENERATE_ACCOUNT(miner_account);
|
||||
|
||||
// events
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); // 0
|
||||
MAKE_ACCOUNT(events, some_account_1); // 1
|
||||
MAKE_ACCOUNT(events, some_account_2); // 2
|
||||
MAKE_ACCOUNT(events, bob_account); // 3
|
||||
MAKE_ACCOUNT(events, alice_account); // 4
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account); // 5
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 6
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account); // 7
|
||||
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account); // 8
|
||||
REWIND_BLOCKS(events, blk_5, blk_4, miner_account); // <N blocks>
|
||||
REWIND_BLOCKS(events, blk_5r, blk_5, miner_account); // <N blocks>
|
||||
MAKE_TX_LIST_START(events, txs_blk_6, miner_account, bob_account, MK_COINS(1), blk_5); // 9 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5); // 10 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5); // 11 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(20) + rnd_20, blk_5); // 12 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 13 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 14 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 15 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 16 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 17 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 18 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 19 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(20) + rnd_20, blk_5); // 20 + 2N
|
||||
MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_2, MK_COINS(20) + rnd_20, blk_5); // 21 + 2N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_6, blk_5r, miner_account, txs_blk_6); // 22 + 2N
|
||||
DO_CALLBACK(events, "check_balances_1"); // 23 + 2N
|
||||
REWIND_BLOCKS(events, blk_6r, blk_6, miner_account); // <N blocks>
|
||||
// 129 = 11 + 11 + 20 + 29 + 29 + 29
|
||||
MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - TESTS_DEFAULT_FEE, 2, blk_6); // 24 + 3N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6r, miner_account, tx_0); // 25 + 3N
|
||||
DO_CALLBACK(events, "check_balances_2"); // 26 + 3N
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_1");
|
||||
|
||||
m_bob_account = boost::get<account_base>(events[3]);
|
||||
m_alice_account = boost::get<account_base>(events[4]);
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(MK_COINS(130) + 2 * rnd_11 + rnd_20 + 3 * rnd_29, get_balance(m_bob_account, chain, mtx));
|
||||
CHECK_EQ(0, get_balance(m_alice_account, chain, mtx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_1::check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_2");
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(MK_COINS(1), get_balance(m_bob_account, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - TESTS_DEFAULT_FEE, get_balance(m_alice_account, chain, mtx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// class gen_ring_signature_2;
|
||||
|
||||
gen_ring_signature_2::gen_ring_signature_2()
|
||||
{
|
||||
REGISTER_CALLBACK("check_balances_1", gen_ring_signature_2::check_balances_1);
|
||||
REGISTER_CALLBACK("check_balances_2", gen_ring_signature_2::check_balances_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bob has 4 inputs by 61 coins. He sends 4 * 61 coins to Alice, using ring signature with nmix = 3. Each Bob's input
|
||||
* is used as mix for 3 others.
|
||||
*/
|
||||
bool gen_ring_signature_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
uint64_t ts_start = 1338224400;
|
||||
|
||||
GENERATE_ACCOUNT(miner_account);
|
||||
|
||||
// events
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); // 0
|
||||
MAKE_ACCOUNT(events, bob_account); // 1
|
||||
MAKE_ACCOUNT(events, alice_account); // 2
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account); // 3
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 4
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account); // 5
|
||||
REWIND_BLOCKS(events, blk_3r, blk_3, miner_account); // <N blocks>
|
||||
MAKE_TX_LIST_START(events, txs_blk_4, miner_account, bob_account, MK_COINS(61), blk_3); // 6 + N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(61), blk_3); // 7 + N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(61), blk_3); // 8 + N
|
||||
MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(61), blk_3); // 9 + N
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_blk_4); // 10 + N
|
||||
DO_CALLBACK(events, "check_balances_1"); // 11 + N
|
||||
REWIND_BLOCKS(events, blk_4r, blk_4, miner_account); // <N blocks>
|
||||
MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(244) - TESTS_DEFAULT_FEE, 3, blk_4); // 12 + 2N
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner_account, tx_0); // 13 + 2N
|
||||
DO_CALLBACK(events, "check_balances_2"); // 14 + 2N
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_1");
|
||||
|
||||
m_bob_account = boost::get<account_base>(events[1]);
|
||||
m_alice_account = boost::get<account_base>(events[2]);
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(MK_COINS(244), get_balance(m_bob_account, chain, mtx));
|
||||
CHECK_EQ(0, get_balance(m_alice_account, chain, mtx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_2");
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(0, get_balance(m_bob_account, chain, mtx));
|
||||
CHECK_EQ(MK_COINS(244) - TESTS_DEFAULT_FEE, get_balance(m_alice_account, chain, mtx));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// class gen_ring_signature_big;
|
||||
|
||||
gen_ring_signature_big::gen_ring_signature_big()
|
||||
: m_test_size(100)
|
||||
, m_tx_amount(MK_COINS(29))
|
||||
{
|
||||
REGISTER_CALLBACK("check_balances_1", gen_ring_signature_big::check_balances_1);
|
||||
REGISTER_CALLBACK("check_balances_2", gen_ring_signature_big::check_balances_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check ring signature with m_test_size-1 sources.
|
||||
* - Create 100 accounts.
|
||||
* - Create 100 blocks, each block contains transaction from the miner to account[i].
|
||||
* - Create transaction with ring signature from account[99] to Alice with nmix = 99.
|
||||
* - Check balances.
|
||||
*/
|
||||
bool gen_ring_signature_big::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
std::vector<account_base> accounts(m_test_size);
|
||||
std::vector<block> blocks;
|
||||
blocks.reserve(m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
|
||||
uint64_t ts_start = 1338224400;
|
||||
GENERATE_ACCOUNT(miner_account);
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
|
||||
|
||||
for (size_t i = 0; i < m_test_size; ++i)
|
||||
{
|
||||
MAKE_ACCOUNT(events, an_account);
|
||||
accounts[i] = an_account;
|
||||
}
|
||||
MAKE_ACCOUNT(events, alice_account);
|
||||
|
||||
size_t blk_0r_idx = events.size();
|
||||
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
|
||||
blocks.push_back(blk_0);
|
||||
for (size_t i = blk_0r_idx; i < events.size(); ++i)
|
||||
{
|
||||
blocks.push_back(boost::get<block>(events[i]));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_test_size; ++i)
|
||||
{
|
||||
block blk_with_unlocked_out = blocks[blocks.size() - 1 - CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW];
|
||||
MAKE_TX_LIST_START(events, txs_blk_i, miner_account, accounts[i], m_tx_amount, blk_with_unlocked_out);
|
||||
for (size_t j = 0; j <= i; ++j)
|
||||
{
|
||||
MAKE_TX_LIST(events, txs_blk_i, miner_account, accounts[i], TESTS_DEFAULT_FEE, blk_with_unlocked_out);
|
||||
}
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_i, blocks.back(), miner_account, txs_blk_i);
|
||||
blocks.push_back(blk_i);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
bool r = find_block_chain(events, chain, mtx, get_block_hash(blk_i));
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "failed to call find_block_chain");
|
||||
std::cout << i << ": " << get_balance(accounts[i], chain, mtx) << std::endl;
|
||||
}
|
||||
|
||||
DO_CALLBACK(events, "check_balances_1");
|
||||
MAKE_TX_MIX(events, tx_0, accounts[0], alice_account, m_tx_amount, m_test_size - 1, blocks.back());
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blocks.back(), miner_account, tx_0);
|
||||
DO_CALLBACK(events, "check_balances_2");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_1");
|
||||
|
||||
m_bob_account = boost::get<account_base>(events[1]);
|
||||
m_alice_account = boost::get<account_base>(events[1 + m_test_size]);
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(m_tx_amount + TESTS_DEFAULT_FEE, get_balance(m_bob_account, chain, mtx));
|
||||
CHECK_EQ(0, get_balance(m_alice_account, chain, mtx));
|
||||
|
||||
for (size_t i = 2; i < 1 + m_test_size; ++i)
|
||||
{
|
||||
const account_base& an_account = boost::get<account_base>(events[i]);
|
||||
uint64_t balance = m_tx_amount + TESTS_DEFAULT_FEE * i;
|
||||
CHECK_EQ(balance, get_balance(an_account, chain, mtx));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_2");
|
||||
|
||||
std::list<block> blocks;
|
||||
bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks);
|
||||
CHECK_TEST_CONDITION(r);
|
||||
|
||||
std::vector<cryptonote::block> chain;
|
||||
map_hash2tx_t mtx;
|
||||
r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back()));
|
||||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(0, get_balance(m_bob_account, chain, mtx));
|
||||
CHECK_EQ(m_tx_amount, get_balance(m_alice_account, chain, mtx));
|
||||
|
||||
for (size_t i = 2; i < 1 + m_test_size; ++i)
|
||||
{
|
||||
const account_base& an_account = boost::get<account_base>(events[i]);
|
||||
uint64_t balance = m_tx_amount + TESTS_DEFAULT_FEE * i;
|
||||
CHECK_EQ(balance, get_balance(an_account, chain, mtx));
|
||||
}
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
uint64_t transfered;
|
||||
lookup_acc_outs(m_alice_account.get_keys(), boost::get<transaction>(events[events.size() - 3]), get_tx_pub_key_from_extra(boost::get<transaction>(events[events.size() - 3])), tx_outs, transfered);
|
||||
CHECK_EQ(m_tx_amount, transfered);
|
||||
|
||||
return true;
|
||||
}
|
||||
65
tests/core_tests/ring_signature_1.h
Normal file
65
tests/core_tests/ring_signature_1.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_ring_signature_1 : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_ring_signature_1();
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
||||
bool check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
cryptonote::account_base m_bob_account;
|
||||
cryptonote::account_base m_alice_account;
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_ring_signature_2 : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_ring_signature_2();
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
||||
bool check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
cryptonote::account_base m_bob_account;
|
||||
cryptonote::account_base m_alice_account;
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class gen_ring_signature_big : public test_chain_unit_base
|
||||
{
|
||||
public:
|
||||
gen_ring_signature_big();
|
||||
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
|
||||
bool check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
private:
|
||||
size_t m_test_size;
|
||||
uint64_t m_tx_amount;
|
||||
|
||||
cryptonote::account_base m_bob_account;
|
||||
cryptonote::account_base m_alice_account;
|
||||
};
|
||||
146
tests/core_tests/transaction_tests.cpp
Normal file
146
tests/core_tests/transaction_tests.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "cryptonote_core/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_core/account.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
|
||||
|
||||
bool test_transaction_generation_and_ring_signature()
|
||||
{
|
||||
|
||||
account_base miner_acc1;
|
||||
miner_acc1.generate();
|
||||
account_base miner_acc2;
|
||||
miner_acc2.generate();
|
||||
account_base miner_acc3;
|
||||
miner_acc3.generate();
|
||||
account_base miner_acc4;
|
||||
miner_acc4.generate();
|
||||
account_base miner_acc5;
|
||||
miner_acc5.generate();
|
||||
account_base miner_acc6;
|
||||
miner_acc6.generate();
|
||||
|
||||
std::string add_str = miner_acc3.get_public_address_str();
|
||||
|
||||
|
||||
account_base rv_acc;
|
||||
rv_acc.generate();
|
||||
account_base rv_acc2;
|
||||
rv_acc2.generate();
|
||||
std::vector<size_t> b;
|
||||
transaction tx_mine_1;
|
||||
construct_miner_tx(0, 0, miner_acc1.get_keys().m_account_address, tx_mine_1, 0, b, 10);
|
||||
transaction tx_mine_2;
|
||||
construct_miner_tx(0, 0, miner_acc2.get_keys().m_account_address, tx_mine_2, 0, b, 0);
|
||||
transaction tx_mine_3;
|
||||
construct_miner_tx(0, 0, miner_acc3.get_keys().m_account_address, tx_mine_3, 0, b, 0);
|
||||
transaction tx_mine_4;
|
||||
construct_miner_tx(0, 0, miner_acc4.get_keys().m_account_address, tx_mine_4, 0, b, 0);
|
||||
transaction tx_mine_5;
|
||||
construct_miner_tx(0, 0, miner_acc5.get_keys().m_account_address, tx_mine_5, 0, b, 0);
|
||||
transaction tx_mine_6;
|
||||
construct_miner_tx(0, 0, miner_acc6.get_keys().m_account_address, tx_mine_6, 0, b, 0);
|
||||
|
||||
//fill inputs entry
|
||||
typedef tx_source_entry::output_entry tx_output_entry;
|
||||
std::vector<tx_source_entry> sources;
|
||||
sources.resize(sources.size()+1);
|
||||
tx_source_entry& src = sources.back();
|
||||
src.amount = 70368744177663;
|
||||
{
|
||||
tx_output_entry oe;
|
||||
oe.first = 0;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_1.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
oe.first = 1;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_2.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
oe.first = 2;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_3.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
oe.first = 3;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_4.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
oe.first = 4;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_5.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
oe.first = 5;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_6.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
crypto::public_key tx_pub_key = null_pkey;
|
||||
cryptonote::parse_and_validate_tx_extra(tx_mine_2, tx_pub_key);
|
||||
src.real_out_tx_key = tx_pub_key;
|
||||
src.real_output = 1;
|
||||
src.real_output_in_tx_index = 0;
|
||||
}
|
||||
//fill outputs entry
|
||||
tx_destination_entry td;
|
||||
td.addr = rv_acc.get_keys().m_account_address;
|
||||
td.amount = 69368744177663;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(td);
|
||||
|
||||
transaction tx_rc1;
|
||||
bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, tx_rc1, 0);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
||||
|
||||
crypto::hash pref_hash = get_transaction_prefix_hash(tx_rc1);
|
||||
std::vector<const crypto::public_key *> output_keys;
|
||||
output_keys.push_back(&boost::get<txout_to_key>(tx_mine_1.vout[0].target).key);
|
||||
output_keys.push_back(&boost::get<txout_to_key>(tx_mine_2.vout[0].target).key);
|
||||
output_keys.push_back(&boost::get<txout_to_key>(tx_mine_3.vout[0].target).key);
|
||||
output_keys.push_back(&boost::get<txout_to_key>(tx_mine_4.vout[0].target).key);
|
||||
output_keys.push_back(&boost::get<txout_to_key>(tx_mine_5.vout[0].target).key);
|
||||
output_keys.push_back(&boost::get<txout_to_key>(tx_mine_6.vout[0].target).key);
|
||||
r = crypto::check_ring_signature(pref_hash, boost::get<txin_to_key>(tx_rc1.vin[0]).k_image, output_keys, &tx_rc1.signatures[0][0]);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to check ring signature");
|
||||
|
||||
std::vector<size_t> outs;
|
||||
uint64_t money = 0;
|
||||
|
||||
r = lookup_acc_outs(rv_acc.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to lookup_acc_outs");
|
||||
CHECK_AND_ASSERT_MES(td.amount == money, false, "wrong money amount in new transaction");
|
||||
money = 0;
|
||||
r = lookup_acc_outs(rv_acc2.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to lookup_acc_outs");
|
||||
CHECK_AND_ASSERT_MES(0 == money, false, "wrong money amount in new transaction");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_block_creation()
|
||||
{
|
||||
uint64_t vszs[] = {80,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,9391,476,476,475,475,474,475,8819,8301,475,472,4302,5316,14347,16620,19583,19403,19728,19442,19852,19015,19000,19016,19795,19749,18087,19787,19704,19750,19267,19006,19050,19445,19407,19522,19546,19788,19369,19486,19329,19370,18853,19600,19110,19320,19746,19474,19474,19743,19494,19755,19715,19769,19620,19368,19839,19532,23424,28287,30707};
|
||||
std::vector<uint64_t> szs(&vszs[0], &vszs[90]);
|
||||
account_public_address adr;
|
||||
bool r = get_account_address_from_str(adr, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2");
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to import");
|
||||
block b;
|
||||
r = construct_miner_tx(90, 3553616528562147, adr, b.miner_tx, 10000000, szs, 33094, 11);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool test_transactions()
|
||||
{
|
||||
if(!test_transaction_generation_and_ring_signature())
|
||||
return false;
|
||||
if(!test_block_creation())
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
10
tests/core_tests/transaction_tests.h
Normal file
10
tests/core_tests/transaction_tests.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
bool test_transactions();
|
||||
bool test_block_creation();
|
||||
746
tests/core_tests/tx_validation.cpp
Normal file
746
tests/core_tests/tx_validation.cpp
Normal file
|
|
@ -0,0 +1,746 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chaingen.h"
|
||||
#include "chaingen_tests_list.h"
|
||||
|
||||
using namespace epee;
|
||||
using namespace crypto;
|
||||
using namespace cryptonote;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct tx_builder
|
||||
{
|
||||
void step1_init(size_t version = CURRENT_TRANSACTION_VERSION, uint64_t unlock_time = 0)
|
||||
{
|
||||
m_tx.vin.clear();
|
||||
m_tx.vout.clear();
|
||||
m_tx.signatures.clear();
|
||||
|
||||
m_tx.version = version;
|
||||
m_tx.unlock_time = unlock_time;
|
||||
|
||||
m_tx_key = keypair::generate();
|
||||
add_tx_pub_key_to_extra(m_tx, m_tx_key.pub);
|
||||
}
|
||||
|
||||
void step2_fill_inputs(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources)
|
||||
{
|
||||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
{
|
||||
m_in_contexts.push_back(keypair());
|
||||
keypair& in_ephemeral = m_in_contexts.back();
|
||||
crypto::key_image img;
|
||||
generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img);
|
||||
|
||||
// put key image into tx input
|
||||
txin_to_key input_to_key;
|
||||
input_to_key.amount = src_entr.amount;
|
||||
input_to_key.k_image = img;
|
||||
|
||||
// fill outputs array and use relative offsets
|
||||
BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs)
|
||||
input_to_key.key_offsets.push_back(out_entry.first);
|
||||
|
||||
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
|
||||
m_tx.vin.push_back(input_to_key);
|
||||
}
|
||||
}
|
||||
|
||||
void step3_fill_outputs(const std::vector<tx_destination_entry>& destinations)
|
||||
{
|
||||
size_t output_index = 0;
|
||||
BOOST_FOREACH(const tx_destination_entry& dst_entr, destinations)
|
||||
{
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, m_tx_key.sec, derivation);
|
||||
crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
|
||||
|
||||
tx_out out;
|
||||
out.amount = dst_entr.amount;
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
out.target = tk;
|
||||
m_tx.vout.push_back(out);
|
||||
output_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void step4_calc_hash()
|
||||
{
|
||||
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
|
||||
}
|
||||
|
||||
void step5_sign(const std::vector<tx_source_entry>& sources)
|
||||
{
|
||||
m_tx.signatures.clear();
|
||||
|
||||
size_t i = 0;
|
||||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
{
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
|
||||
{
|
||||
keys_ptrs.push_back(&o.second);
|
||||
}
|
||||
|
||||
m_tx.signatures.push_back(std::vector<crypto::signature>());
|
||||
std::vector<crypto::signature>& sigs = m_tx.signatures.back();
|
||||
sigs.resize(src_entr.outputs.size());
|
||||
generate_ring_signature(m_tx_prefix_hash, boost::get<txin_to_key>(m_tx.vin[i]).k_image, keys_ptrs, m_in_contexts[i].sec, src_entr.real_output, sigs.data());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
transaction m_tx;
|
||||
keypair m_tx_key;
|
||||
std::vector<keypair> m_in_contexts;
|
||||
crypto::hash m_tx_prefix_hash;
|
||||
};
|
||||
|
||||
transaction make_simple_tx_with_unlock_time(const std::vector<test_event_entry>& events,
|
||||
const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to,
|
||||
uint64_t amount, uint64_t unlock_time)
|
||||
{
|
||||
std::vector<tx_source_entry> sources;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations);
|
||||
|
||||
tx_builder builder;
|
||||
builder.step1_init(CURRENT_TRANSACTION_VERSION, unlock_time);
|
||||
builder.step2_fill_inputs(from.get_keys(), sources);
|
||||
builder.step3_fill_outputs(destinations);
|
||||
builder.step4_calc_hash();
|
||||
builder.step5_sign(sources);
|
||||
return builder.m_tx;
|
||||
};
|
||||
|
||||
crypto::public_key generate_invalid_pub_key()
|
||||
{
|
||||
for (int i = 0; i <= 0xFF; ++i)
|
||||
{
|
||||
crypto::public_key key;
|
||||
memset(&key, i, sizeof(crypto::public_key));
|
||||
if (!crypto::check_key(key))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("invalid public key wasn't found");
|
||||
return crypto::public_key();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Tests
|
||||
|
||||
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);
|
||||
|
||||
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(CURRENT_TRANSACTION_VERSION + 1, 0);
|
||||
builder.step2_fill_inputs(miner_account.get_keys(), sources);
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
std::list<transaction> txs_0;
|
||||
|
||||
txs_0.push_back(make_tx_with_unlock_time(0));
|
||||
events.push_back(txs_0.back());
|
||||
|
||||
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r) - 1));
|
||||
events.push_back(txs_0.back());
|
||||
|
||||
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r)));
|
||||
events.push_back(txs_0.back());
|
||||
|
||||
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r) + 1));
|
||||
events.push_back(txs_0.back());
|
||||
|
||||
txs_0.push_back(make_tx_with_unlock_time(get_block_height(blk_1r) + 2));
|
||||
events.push_back(txs_0.back());
|
||||
|
||||
txs_0.push_back(make_tx_with_unlock_time(ts_start - 1));
|
||||
events.push_back(txs_0.back());
|
||||
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_input_is_not_txin_to_key::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_NEXT_BLOCK(events, blk_tmp, blk_0r, 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");
|
||||
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()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_no_inputs_no_outputs::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);
|
||||
|
||||
tx_builder builder;
|
||||
builder.step1_init();
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_tx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_no_inputs_has_outputs::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);
|
||||
|
||||
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);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_tx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_has_inputs_no_outputs::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);
|
||||
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_invalid_input_amount::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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_tx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_input_wo_key_offsets::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);
|
||||
|
||||
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);
|
||||
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();
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_tx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_key_offest_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_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(60) + 1, blk_1);
|
||||
MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(60) + 1, blk_1);
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0);
|
||||
|
||||
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(60) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, 0, sources_bob, destinations_bob);
|
||||
|
||||
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(60) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, 0, 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);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_tx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_sender_key_offest_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);
|
||||
|
||||
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.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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_mixed_key_offest_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);
|
||||
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);
|
||||
|
||||
std::vector<tx_source_entry> sources;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
fill_tx_sources_and_destinations(events, blk_2, bob_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 1, sources, destinations);
|
||||
|
||||
sources.front().outputs[(sources.front().real_output + 1) % 2].first = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
tx_builder builder;
|
||||
builder.step1_init();
|
||||
builder.step2_fill_inputs(bob_account.get_keys(), sources);
|
||||
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);
|
||||
|
||||
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_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());
|
||||
keypair kp = keypair::generate();
|
||||
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();
|
||||
|
||||
// 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>();
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_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_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());
|
||||
crypto::public_key pub = generate_invalid_pub_key();
|
||||
memcpy(&in_to_key.k_image, &pub, sizeof(crypto::ec_point));
|
||||
|
||||
builder.step3_fill_outputs(destinations);
|
||||
builder.step4_calc_hash();
|
||||
|
||||
// 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>();
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_tx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gen_tx_check_input_unlock_time::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
static const size_t tests_count = 6;
|
||||
|
||||
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, 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)
|
||||
{
|
||||
MAKE_ACCOUNT(events, acc);
|
||||
accounts[i] = acc;
|
||||
}
|
||||
|
||||
std::list<transaction> txs_0;
|
||||
auto make_tx_to_acc = [&](size_t acc_idx, uint64_t unlock_time)
|
||||
{
|
||||
txs_0.push_back(make_simple_tx_with_unlock_time(events, blk_1, miner_account, accounts[acc_idx],
|
||||
MK_COINS(1) + TESTS_DEFAULT_FEE, unlock_time));
|
||||
events.push_back(txs_0.back());
|
||||
};
|
||||
|
||||
uint64_t blk_3_height = get_block_height(blk_1r) + 2;
|
||||
make_tx_to_acc(0, 0);
|
||||
make_tx_to_acc(1, blk_3_height - 1);
|
||||
make_tx_to_acc(2, blk_3_height);
|
||||
make_tx_to_acc(3, blk_3_height + 1);
|
||||
make_tx_to_acc(4, time(0) - 1);
|
||||
make_tx_to_acc(5, time(0) + 60 * 60);
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0);
|
||||
|
||||
std::list<transaction> txs_1;
|
||||
auto make_tx_from_acc = [&](size_t acc_idx, bool invalid)
|
||||
{
|
||||
transaction tx = make_simple_tx_with_unlock_time(events, blk_2, accounts[acc_idx], miner_account, MK_COINS(1), 0);
|
||||
if (invalid)
|
||||
{
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
}
|
||||
else
|
||||
{
|
||||
txs_1.push_back(tx);
|
||||
}
|
||||
events.push_back(tx);
|
||||
};
|
||||
|
||||
make_tx_from_acc(0, false);
|
||||
make_tx_from_acc(1, false);
|
||||
make_tx_from_acc(2, false);
|
||||
make_tx_from_acc(3, true);
|
||||
make_tx_from_acc(4, false);
|
||||
make_tx_from_acc(5, true);
|
||||
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2, miner_account, txs_1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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_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);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_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_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);
|
||||
|
||||
builder.m_tx.vout.front().amount = 0;
|
||||
|
||||
builder.step4_calc_hash();
|
||||
builder.step5_sign(sources);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_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_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);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_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);
|
||||
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
events.push_back(builder.m_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_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_TX(events, tx_0, miner_account, miner_account, MK_COINS(60), blk_2);
|
||||
events.pop_back();
|
||||
|
||||
MAKE_TX_MIX(events, tx_1, bob_account, miner_account, MK_COINS(1), 1, blk_2);
|
||||
events.pop_back();
|
||||
|
||||
// Tx with nmix = 0 without signatures
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
blobdata sr_tx = t_serializable_object_to_blob(static_cast<transaction_prefix>(tx_0));
|
||||
events.push_back(serialized_transaction(sr_tx));
|
||||
|
||||
// Tx with nmix = 0 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.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
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
sr_tx = t_serializable_object_to_blob(tx_0);
|
||||
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
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
sr_tx = t_serializable_object_to_blob(static_cast<transaction_prefix>(tx_1));
|
||||
events.push_back(serialized_transaction(sr_tx));
|
||||
|
||||
// Tx with nmix = 1 have not enough signatures
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
sr_tx = t_serializable_object_to_blob(tx_1);
|
||||
sr_tx.resize(sr_tx.size() - sizeof(crypto::signature));
|
||||
events.push_back(serialized_transaction(sr_tx));
|
||||
|
||||
// Tx with nmix = 1 have too many signatures
|
||||
DO_CALLBACK(events, "mark_invalid_tx");
|
||||
sr_tx = t_serializable_object_to_blob(tx_1);
|
||||
sr_tx.insert(sr_tx.end(), sr_tx.end() - sizeof(crypto::signature), sr_tx.end());
|
||||
events.push_back(serialized_transaction(sr_tx));
|
||||
|
||||
return true;
|
||||
}
|
||||
139
tests/core_tests/tx_validation.h
Normal file
139
tests/core_tests/tx_validation.h
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) 2012-2013 The Cryptonote developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
|
||||
struct get_tx_validation_base : public test_chain_unit_base
|
||||
{
|
||||
get_tx_validation_base()
|
||||
: m_invalid_tx_index(0)
|
||||
, m_invalid_block_index(0)
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(get_tx_validation_base, mark_invalid_tx);
|
||||
REGISTER_CALLBACK_METHOD(get_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<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_block_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
|
||||
{
|
||||
m_invalid_tx_index = ev_index + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_invalid_tx_index;
|
||||
size_t m_invalid_block_index;
|
||||
};
|
||||
|
||||
struct gen_tx_big_version : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_unlock_time : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_input_is_not_txin_to_key : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_no_inputs_no_outputs : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_no_inputs_has_outputs : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_has_inputs_no_outputs : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_invalid_input_amount : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_sender_key_offest_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
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_key_image_not_derive_from_tx_key : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_key_image_is_invalid : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_check_input_unlock_time : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_txout_to_key_has_invalid_key : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_output_with_zero_amount : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_output_is_not_txout_to_key : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct gen_tx_signatures_are_invalid : public get_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue