diff options
Diffstat (limited to 'tests')
172 files changed, 4654 insertions, 545 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 20afd4203..bbb0bc051 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -85,12 +85,17 @@ add_subdirectory(performance_tests) add_subdirectory(core_proxy) add_subdirectory(unit_tests) add_subdirectory(difficulty) +add_subdirectory(block_weight) add_subdirectory(hash) add_subdirectory(net_load_tests) if (BUILD_GUI_DEPS) add_subdirectory(libwallet_api_tests) endif() +if (TREZOR_DEBUG) + add_subdirectory(trezor) +endif() + # add_subdirectory(daemon_tests) set(hash_targets_sources @@ -115,6 +120,7 @@ add_test( set(enabled_tests core_tests difficulty + block_weight hash performance_tests core_proxy diff --git a/tests/block_weight/CMakeLists.txt b/tests/block_weight/CMakeLists.txt new file mode 100644 index 000000000..b0d716ea0 --- /dev/null +++ b/tests/block_weight/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(block_weight_sources + block_weight.cpp) + +set(block_weight_headers) + +add_executable(block_weight + ${block_weight_sources} + ${block_weight_headers}) +target_link_libraries(block_weight + PRIVATE + cryptonote_core + blockchain_db + ${EXTRA_LIBRARIES}) + +add_test( + NAME block_weight + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/compare.py ${CMAKE_CURRENT_SOURCE_DIR}/block_weight.py ${CMAKE_CURRENT_BINARY_DIR}/block_weight) diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp new file mode 100644 index 000000000..57fcb497e --- /dev/null +++ b/tests/block_weight/block_weight.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#define IN_UNIT_TESTS + +#include <stdio.h> +#include <math.h> +#include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "blockchain_db/testdb.h" + +#define LONG_TERM_BLOCK_WEIGHT_WINDOW 5000 + +enum test_t +{ + test_max = 0, + test_lcg = 1, + test_min = 2, +}; + +namespace +{ + +class TestDB: public cryptonote::BaseTestDB +{ +private: + struct block_t + { + size_t weight; + uint64_t long_term_weight; + }; + +public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override { + blocks.push_back({block_weight, long_term_block_weight}); + } + virtual uint64_t height() const override { return blocks.size(); } + virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } + virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { + uint64_t h = height(); + crypto::hash top = crypto::null_hash; + if (h) + *(uint64_t*)&top = h - 1; + if (block_height) + *block_height = h - 1; + return top; + } + virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) override { if (height >= hf.size()) hf.resize(height + 1); hf[height] = version; } + virtual uint8_t get_hard_fork_version(uint64_t height) const override { if (height >= hf.size()) return 255; return hf[height]; } + +private: + std::vector<block_t> blocks; + std::vector<uint8_t> hf; +}; + +} + +#define PREFIX_WINDOW(hf_version,window) \ + std::unique_ptr<cryptonote::Blockchain> bc; \ + cryptonote::tx_memory_pool txpool(*bc); \ + bc.reset(new cryptonote::Blockchain(txpool)); \ + struct get_test_options { \ + const std::pair<uint8_t, uint64_t> hard_forks[3]; \ + const cryptonote::test_options test_options = { \ + hard_forks, \ + window, \ + }; \ + get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)LONG_TERM_BLOCK_WEIGHT_WINDOW), std::make_pair((uint8_t)0, (uint64_t)0)} {} \ + } opts; \ + cryptonote::Blockchain *blockchain = bc.get(); \ + bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \ + if (!r) \ + { \ + fprintf(stderr, "Failed to init blockchain\n"); \ + exit(1); \ + } + +#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, LONG_TERM_BLOCK_WEIGHT_WINDOW) + +static uint32_t lcg_seed = 0; + +static uint32_t lcg() +{ + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff; + return lcg_seed; +} + +static void test(test_t t, uint64_t blocks) +{ + PREFIX(10); + + for (uint64_t h = 0; h < LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + cryptonote::block b; + b.major_version = 1; + b.minor_version = 1; + bc->get_db().add_block(std::make_pair(b, ""), 300000, 300000, bc->get_db().height(), bc->get_db().height(), {}); + if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit 1\n"); + exit(1); + } + } + + for (uint64_t h = 0; h < blocks; ++h) + { + uint64_t w; + uint64_t effective_block_weight_median = bc->get_current_cumulative_block_weight_median(); + switch (t) + { + case test_lcg: + { + uint32_t r = lcg(); + int64_t wi = 90 + r % 500000 + 250000 + sin(h / 200.) * 350000; + w = wi < 90 ? 90 : wi; + break; + } + case test_max: + w = bc->get_current_cumulative_block_weight_limit(); + break; + case test_min: + w = 90; + break; + default: + exit(1); + } + uint64_t ltw = bc->get_next_long_term_block_weight(w); + cryptonote::block b; + b.major_version = 10; + b.minor_version = 10; + bc->get_db().add_block(std::make_pair(std::move(b), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + + if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit\n"); + exit(1); + } + std::cout << "H " << h << ", BW " << w << ", EMBW " << effective_block_weight_median << ", LTBW " << ltw << std::endl; + } +} + +int main() +{ + test(test_max, 2 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + test(test_lcg, 9 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + test(test_min, 1 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + return 0; +} diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py new file mode 100755 index 000000000..06aaabb02 --- /dev/null +++ b/tests/block_weight/block_weight.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# Simulate a maximal block attack on the Monero network +# This uses the scheme proposed by ArticMine +# Written by Sarang Nother +# Copyright (c) 2019 The Monero Project +import sys +import math + +MEDIAN_WINDOW_SMALL = 100 # number of recent blocks for median computation +MEDIAN_WINDOW_BIG = 5000 +MULTIPLIER_SMALL = 1.4 # multipliers for determining weights +MULTIPLIER_BIG = 50.0 +MEDIAN_THRESHOLD = 300*1000 # initial value for median (scaled kB -> B) +lcg_seed = 0 +embw = MEDIAN_THRESHOLD +ltembw = MEDIAN_THRESHOLD + +weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_SMALL # weights of recent blocks (B), with index -1 most recent +lt_weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_BIG # long-term weights + +# Compute the median of a list +def get_median(vec): + #temp = vec + temp = sorted(vec) + if len(temp) % 2 == 1: + return temp[len(temp)/2] + else: + return int((temp[len(temp)/2]+temp[len(temp)/2-1])/2) + +def LCG(): + global lcg_seed + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff + return lcg_seed + +def run(t, blocks): + global embw + global ltembw + + weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_SMALL # weights of recent blocks (B), with index -1 most recent + lt_weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_BIG # long-term weights + + for block in range(blocks): + # determine the long-term effective weight + ltmedian = get_median(lt_weights[-MEDIAN_WINDOW_BIG:]) + ltembw = max(MEDIAN_THRESHOLD,ltmedian) + + # determine the effective weight + stmedian = get_median(weights[-MEDIAN_WINDOW_SMALL:]) + embw = min(max(MEDIAN_THRESHOLD,stmedian),int(MULTIPLIER_BIG*ltembw)) + + # drop the lowest values + weights = weights[1:] + lt_weights = lt_weights[1:] + + # add a block of max weight + if t == 0: + max_weight = 2 * embw + elif t == 1: + r = LCG() + max_weight = int(90 + r % 500000 + 250000 + math.sin(block / 200.) * 350000) + if max_weight < 90: max_weight = 90 + elif t == 2: + max_weight = 90 + else: + sys.exit(1) + weights.append(max_weight) + lt_weights.append(min(max_weight,int(ltembw + int(ltembw * 2 / 5)))) + + #print "H %u, r %u, BW %u, EMBW %u, LTBW %u, LTEMBW %u, ltmedian %u" % (block, r, max_weight, embw, lt_weights[-1], ltembw, ltmedian) + print "H %u, BW %u, EMBW %u, LTBW %u" % (block, max_weight, embw, lt_weights[-1]) + +run(0, 2 * MEDIAN_WINDOW_BIG) +run(1, 9 * MEDIAN_WINDOW_BIG) +run(2, 1 * MEDIAN_WINDOW_BIG) diff --git a/tests/block_weight/compare.py b/tests/block_weight/compare.py new file mode 100755 index 000000000..c6be05206 --- /dev/null +++ b/tests/block_weight/compare.py @@ -0,0 +1,13 @@ +#!/usr/bin/python + +import sys +import subprocess + +print 'running: ', sys.argv[1] +S0 = subprocess.check_output(sys.argv[1], stderr=subprocess.STDOUT) +print 'running: ', sys.argv[2] +S1 = subprocess.check_output(sys.argv[2], stderr=subprocess.STDOUT) +print 'comparing' +if S0 != S1: + sys.exit(1) +sys.exit(0) diff --git a/tests/core_proxy/CMakeLists.txt b/tests/core_proxy/CMakeLists.txt index 105c20d22..9818d35d7 100644 --- a/tests/core_proxy/CMakeLists.txt +++ b/tests/core_proxy/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 17e552714..388808269 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -197,7 +197,7 @@ bool tests::proxy_core::handle_incoming_txs(const std::vector<blobdata>& tx_blob return true; } -bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate) { +bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block_, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate) { block b = AUTO_VAL_INIT(b); if(!parse_and_validate_block_from_blob(block_blob, b)) { diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 7888540cc..6c8b3ccb6 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -77,7 +77,7 @@ namespace tests void get_blockchain_top(uint64_t& height, crypto::hash& top_id); bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_txs(const std::vector<cryptonote::blobdata>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); + bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); void pause_mine(){} void resume_mine(){} bool on_idle(){return true;} @@ -86,7 +86,7 @@ namespace tests cryptonote::Blockchain &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); } bool get_test_drop_download() {return true;} bool get_test_drop_download_height() {return true;} - bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks) { return true; } + bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks_entry, std::vector<cryptonote::block> &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index 1ac0e7864..f93cbf3ad 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -42,7 +42,8 @@ set(core_tests_sources tx_validation.cpp v2_tests.cpp rct.cpp - bulletproofs.cpp) + bulletproofs.cpp + wallet_tools.cpp) set(core_tests_headers block_reward.h @@ -60,7 +61,8 @@ set(core_tests_headers tx_validation.h v2_tests.h rct.h - bulletproofs.h) + bulletproofs.h + wallet_tools.h) add_executable(core_tests ${core_tests_sources} @@ -73,6 +75,7 @@ target_link_libraries(core_tests version epee device + wallet ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) enable_stack_trace(core_tests) diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index e55378439..17fc762ec 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index b7eb7c98e..6234bb9c5 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 760cc4328..566ec1440 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index d229e4530..4a65b029e 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 3e2db2e29..fd3f5114b 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index b0289f355..efc751df7 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index d78e793bb..c51accb67 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h index 75602d1b0..503c3da19 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 18a813b19..0554fd676 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 989b6df11..c084de262 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index d3cb52246..09bc10ea8 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -31,6 +31,11 @@ #include <vector> #include <iostream> #include <sstream> +#include <algorithm> +#include <array> +#include <random> +#include <sstream> +#include <fstream> #include "include_base_utils.h" @@ -105,10 +110,11 @@ void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, 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_weights, const std::list<cryptonote::transaction>& tx_list) + std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list, + const boost::optional<uint8_t>& hf_ver) { - blk.major_version = CURRENT_BLOCK_MAJOR_VERSION; - blk.minor_version = CURRENT_BLOCK_MINOR_VERSION; + blk.major_version = hf_ver ? hf_ver.get() : CURRENT_BLOCK_MAJOR_VERSION; + blk.minor_version = hf_ver ? hf_ver.get() : CURRENT_BLOCK_MINOR_VERSION; blk.timestamp = timestamp; blk.prev_id = prev_id; @@ -135,7 +141,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); while (true) { - if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10)) + if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10, hf_ver ? hf_ver.get() : 1)) return false; size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); @@ -180,10 +186,10 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co // Nonce search... blk.nonce = 0; - while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height)) + while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(hf_ver), height)) blk.timestamp++; - add_block(blk, txs_weight, block_weights, already_generated_coins); + add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1); return true; } @@ -197,17 +203,18 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::a 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>()*/) + const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/, + const boost::optional<uint8_t>& hf_ver) { 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 timestamp = blk_prev.timestamp + current_difficulty_window(hf_ver); // DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; uint64_t already_generated_coins = get_already_generated_coins(prev_id); std::vector<size_t> block_weights; get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list); + return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list, hf_ver); } bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc, @@ -244,7 +251,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc //blk.tree_root_hash = get_tx_tree_hash(blk); - difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(); + difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(hf_version); fill_nonce(blk, a_diffic, height); add_block(blk, txs_weight, block_weights, already_generated_coins, hf_version); @@ -259,49 +266,6 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight); } - -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) @@ -339,6 +303,9 @@ bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector< 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]); + oi.set_rct(tx.version == 2); + oi.unlock_time = tx.unlock_time; + oi.is_coin_base = i == 0; if (2 == out.target.which()) { // out_to_key outs[out.amount].push_back(oi); @@ -416,8 +383,9 @@ bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_o if (append) { + rct::key comm = oi.commitment(); const txout_to_key& otk = boost::get<txout_to_key>(oi.out); - output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::identity()}))); + output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), comm}))); } } @@ -452,6 +420,8 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te const output_index& oi = outs[o.first][sender_out]; if (oi.spent) continue; + if (oi.rct) + continue; cryptonote::tx_source_entry ts; ts.amount = oi.amount; @@ -463,6 +433,11 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te ts.real_output = realOutput; ts.rct = false; + ts.mask = rct::identity(); // non-rct has identity mask by definition + + rct::key comm = rct::zeroCommit(ts.amount); + for(auto & ot : ts.outputs) + ot.second.mask = comm; sources.push_back(ts); @@ -477,38 +452,347 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te 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; +bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_public_address &to, uint64_t amount) { + de.addr = to; 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) +map_txid_output_t::iterator block_tracker::find_out(const crypto::hash &txid, size_t out) +{ + return find_out(std::make_pair(txid, out)); +} + +map_txid_output_t::iterator block_tracker::find_out(const output_hasher &id) +{ + return m_map_outs.find(id); +} + +void block_tracker::process(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx) +{ + std::vector<const cryptonote::block*> blks; + blks.reserve(blockchain.size()); + + BOOST_FOREACH (const block& blk, blockchain) { + auto hsh = get_block_hash(blk); + auto it = m_blocks.find(hsh); + if (it == m_blocks.end()){ + m_blocks[hsh] = blk; + } + + blks.push_back(&m_blocks[hsh]); + } + + process(blks, mtx); +} + +void block_tracker::process(const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t& mtx) +{ + 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); + CHECK_AND_ASSERT_THROW_MES(mtx.end() != cit, "block contains an unknown tx hash"); + vtx.push_back(cit->second); + } + + for (size_t i = 0; i < vtx.size(); i++) { + process(blk, vtx[i], i); + } + } +} + +void block_tracker::process(const block* blk, const transaction * tx, size_t i) +{ + for (size_t j = 0; j < tx->vout.size(); ++j) { + const tx_out &out = tx->vout[j]; + + if (typeid(cryptonote::txout_to_key) != out.target.type()) { // out_to_key + continue; + } + + const uint64_t rct_amount = tx->version == 2 ? 0 : out.amount; + const output_hasher hid = std::make_pair(tx->hash, j); + auto it = find_out(hid); + if (it != m_map_outs.end()){ + continue; + } + + output_index oi(out.target, out.amount, boost::get<txin_gen>(blk->miner_tx.vin.front()).height, i, j, blk, tx); + oi.set_rct(tx->version == 2); + oi.idx = m_outs[rct_amount].size(); + oi.unlock_time = tx->unlock_time; + oi.is_coin_base = tx->vin.size() == 1 && tx->vin.back().type() == typeid(cryptonote::txin_gen); + + m_outs[rct_amount].push_back(oi); + m_map_outs.insert({hid, oi}); + } +} + +void block_tracker::global_indices(const cryptonote::transaction *tx, std::vector<uint64_t> &indices) +{ + indices.clear(); + + for(size_t j=0; j < tx->vout.size(); ++j){ + auto it = find_out(tx->hash, j); + if (it != m_map_outs.end()){ + indices.push_back(it->second.idx); + } + } +} + +void block_tracker::get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector<get_outs_entry> &outs){ + auto & vct = m_outs[amount]; + const size_t n_outs = vct.size(); + + std::set<size_t> used; + std::vector<size_t> choices; + choices.resize(n_outs); + for(size_t i=0; i < n_outs; ++i) choices[i] = i; + shuffle(choices.begin(), choices.end(), std::default_random_engine(crypto::rand<unsigned>())); + + size_t n_iters = 0; + ssize_t idx = -1; + outs.reserve(num_outs); + while(outs.size() < num_outs){ + n_iters += 1; + idx = (idx + 1) % n_outs; + size_t oi_idx = choices[(size_t)idx]; + CHECK_AND_ASSERT_THROW_MES((n_iters / n_outs) <= outs.size(), "Fake out pick selection problem"); + + auto & oi = vct[oi_idx]; + if (oi.idx == global_index) + continue; + if (oi.out.type() != typeid(cryptonote::txout_to_key)) + continue; + if (oi.unlock_time > cur_height) + continue; + if (used.find(oi_idx) != used.end()) + continue; + + rct::key comm = oi.commitment(); + auto out = boost::get<txout_to_key>(oi.out); + auto item = std::make_tuple(oi.idx, out.key, comm); + outs.push_back(item); + used.insert(oi_idx); + } +} + +std::string block_tracker::dump_data() +{ + ostringstream ss; + for (auto &m_out : m_outs) + { + auto & vct = m_out.second; + ss << m_out.first << " => |vector| = " << vct.size() << '\n'; + + for (const auto & oi : vct) + { + auto out = boost::get<txout_to_key>(oi.out); + + ss << " idx: " << oi.idx + << ", rct: " << oi.rct + << ", xmr: " << oi.amount + << ", key: " << dump_keys(out.key.data) + << ", msk: " << dump_keys(oi.comm.bytes) + << ", txid: " << dump_keys(oi.p_tx->hash.data) + << '\n'; + } + } + + return ss.str(); +} + +void block_tracker::dump_data(const std::string & fname) +{ + ofstream myfile; + myfile.open (fname); + myfile << dump_data(); + myfile.close(); +} + +std::string dump_data(const cryptonote::transaction &tx) +{ + ostringstream ss; + ss << "msg: " << dump_keys(tx.rct_signatures.message.bytes) + << ", vin: "; + + for(auto & in : tx.vin){ + if (typeid(txin_to_key) == in.type()){ + auto tk = boost::get<txin_to_key>(in); + std::vector<uint64_t> full_off; + int64_t last = -1; + + ss << " i: " << tk.amount << " ["; + for(auto ix : tk.key_offsets){ + ss << ix << ", "; + if (last == -1){ + last = ix; + full_off.push_back(ix); + } else { + last += ix; + full_off.push_back((uint64_t)last); + } + } + + ss << "], full: ["; + for(auto ix : full_off){ + ss << ix << ", "; + } + ss << "]; "; + + } else if (typeid(txin_gen) == in.type()){ + ss << " h: " << boost::get<txin_gen>(in).height << ", "; + } else { + ss << " ?, "; + } + } + + ss << ", mixring: \n"; + for (const auto & row : tx.rct_signatures.mixRing){ + for(auto cur : row){ + ss << " (" << dump_keys(cur.dest.bytes) << ", " << dump_keys(cur.mask.bytes) << ")\n "; + } + ss << "; "; + } + + return ss.str(); +} + +cryptonote::account_public_address get_address(const var_addr_t& inp) +{ + if (typeid(cryptonote::account_public_address) == inp.type()){ + return boost::get<cryptonote::account_public_address>(inp); + } else if(typeid(cryptonote::account_keys) == inp.type()){ + return boost::get<cryptonote::account_keys>(inp).m_account_address; + } else if (typeid(cryptonote::account_base) == inp.type()){ + return boost::get<cryptonote::account_base>(inp).get_keys().m_account_address; + } else if (typeid(cryptonote::tx_destination_entry) == inp.type()){ + return boost::get<cryptonote::tx_destination_entry>(inp).addr; + } else { + throw std::runtime_error("Unexpected type"); + } +} + +cryptonote::account_public_address get_address(const cryptonote::account_public_address& inp) +{ + return inp; +} + +cryptonote::account_public_address get_address(const cryptonote::account_keys& inp) +{ + return inp.m_account_address; +} + +cryptonote::account_public_address get_address(const cryptonote::account_base& inp) +{ + return inp.get_keys().m_account_address; +} + +cryptonote::account_public_address get_address(const cryptonote::tx_destination_entry& inp) +{ + return inp.addr; +} + +uint64_t sum_amount(const std::vector<tx_destination_entry>& destinations) +{ + uint64_t amount = 0; + for(auto & cur : destinations){ + amount += cur.amount; + } + + return amount; +} + +uint64_t sum_amount(const std::vector<cryptonote::tx_source_entry>& sources) +{ + uint64_t amount = 0; + for(auto & cur : sources){ + amount += cur.amount; + } + + return amount; +} + +void fill_tx_destinations(const var_addr_t& from, const std::vector<tx_destination_entry>& dests, + uint64_t fee, + const std::vector<tx_source_entry> &sources, + std::vector<tx_destination_entry>& destinations, + bool always_change) + { - sources.clear(); destinations.clear(); + uint64_t amount = sum_amount(dests); + std::copy(dests.begin(), dests.end(), std::back_inserter(destinations)); - 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_change; + uint64_t cache_back = get_inputs_amount(sources) - (amount + fee); + + if (cache_back > 0 || always_change) { + if (!fill_tx_destination(de_change, get_address(from), cache_back <= 0 ? 0 : cache_back)) + throw std::runtime_error("couldn't fill transaction cache back destination"); + destinations.push_back(de_change); + } +} + +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<tx_source_entry> &sources, + std::vector<tx_destination_entry>& destinations, + std::vector<tx_destination_entry>& destinations_pure, + bool always_change) +{ + destinations.clear(); tx_destination_entry de; if (!fill_tx_destination(de, to, amount)) throw std::runtime_error("couldn't fill transaction destination"); destinations.push_back(de); + destinations_pure.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)) + + if (cache_back > 0 || always_change) { + if (!fill_tx_destination(de_change, get_address(from), cache_back <= 0 ? 0 : cache_back)) throw std::runtime_error("couldn't fill transaction cache back destination"); destinations.push_back(de_change); } } +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<tx_source_entry> &sources, + std::vector<tx_destination_entry>& destinations, bool always_change) +{ + std::vector<tx_destination_entry> destinations_pure; + fill_tx_destinations(from, to, amount, fee, sources, destinations, destinations_pure, always_change); +} + +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_public_address& 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"); + + fill_tx_destinations(from, to, amount, fee, sources, destinations, false); +} + +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) +{ + fill_tx_sources_and_destinations(events, blk_head, from, to.get_keys().m_account_address, amount, fee, nmix, sources, destinations); +} + void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) { blk.nonce = 0; @@ -516,6 +800,32 @@ void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t blk.timestamp++; } +cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr, uint64_t amount) +{ + tx_destination_entry de; + de.amount = amount; + de.addr = get_address(to); + de.is_subaddress = is_subaddr; + return de; +} + +std::vector<cryptonote::tx_destination_entry> build_dsts(const var_addr_t& to1, bool sub1, uint64_t am1) +{ + std::vector<cryptonote::tx_destination_entry> res; + res.push_back(build_dst(to1, sub1, am1)); + return res; +} + +std::vector<cryptonote::tx_destination_entry> build_dsts(std::initializer_list<dest_wrapper_t> inps) +{ + std::vector<cryptonote::tx_destination_entry> res; + res.reserve(inps.size()); + for(auto & c : inps){ + res.push_back(build_dst(c.addr, c.is_subaddr, c.amount)); + } + return res; +} + 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*/) @@ -556,22 +866,70 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins 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) +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 var_addr_t& to, uint64_t amount, + uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_source_entry> sources; + vector<tx_destination_entry> destinations; + fill_tx_sources_and_destinations(events, blk_head, from, get_address(to), amount, fee, nmix, sources, destinations); + + return construct_tx_rct(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +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, std::vector<cryptonote::tx_destination_entry> destinations, + uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version) { vector<tx_source_entry> sources; + vector<tx_destination_entry> destinations_all; + uint64_t amount = sum_amount(destinations); + + if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix)) + throw std::runtime_error("couldn't fill transaction sources"); + + fill_tx_destinations(from, destinations, fee, sources, destinations_all, false); + + return construct_tx_rct(from.get_keys(), sources, destinations_all, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ vector<tx_destination_entry> destinations; - fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations); + fill_tx_destinations(from, get_address(to), amount, fee, sources, destinations, rct); + return construct_tx_rct(from.get_keys(), sources, destinations, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + const cryptonote::account_base& from, + const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_destination_entry> all_destinations; + fill_tx_destinations(from, destinations, fee, sources, all_destinations, rct); + return construct_tx_rct(from.get_keys(), sources, all_destinations, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} - return construct_tx(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, 0); +bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; + subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0, 0}; + crypto::secret_key tx_key; + std::vector<crypto::secret_key> additional_tx_keys; + std::vector<tx_destination_entry> destinations_copy = destinations; + rct::RCTConfig rct_config = {range_proof_type, bp_version}; + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); } 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) + const account_base& acc_from, const var_addr_t& to, uint64_t amount, uint64_t fee) { transaction tx; - construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 0); + construct_tx_to_key(events, tx, blk_head, acc_from, to, amount, fee, 0); events.push_back(tx); return tx; } @@ -602,6 +960,24 @@ uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cry return res; } +bool extract_hard_forks(const std::vector<test_event_entry>& events, v_hardforks_t& hard_forks) +{ + for(auto & ev : events) + { + if (typeid(event_replay_settings) == ev.type()) + { + const auto & rep_settings = boost::get<event_replay_settings>(ev); + if (rep_settings.hard_forks) + { + const auto & hf = rep_settings.hard_forks.get(); + std::copy(hf.begin(), hf.end(), std::back_inserter(hard_forks)); + } + } + } + + return !hard_forks.empty(); +} + 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; @@ -622,6 +998,74 @@ void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const m } } +bool trim_block_chain(std::vector<cryptonote::block>& blockchain, const crypto::hash& tail){ + size_t cut = 0; + bool found = true; + + for(size_t i = 0; i < blockchain.size(); ++i){ + crypto::hash chash = get_block_hash(blockchain[i]); + if (chash == tail){ + cut = i; + found = true; + break; + } + } + + if (found && cut > 0){ + blockchain.erase(blockchain.begin(), blockchain.begin() + cut); + } + + return found; +} + +bool trim_block_chain(std::vector<const cryptonote::block*>& blockchain, const crypto::hash& tail){ + size_t cut = 0; + bool found = true; + + for(size_t i = 0; i < blockchain.size(); ++i){ + crypto::hash chash = get_block_hash(*blockchain[i]); + if (chash == tail){ + cut = i; + found = true; + break; + } + } + + if (found && cut > 0){ + blockchain.erase(blockchain.begin(), blockchain.begin() + cut); + } + + return found; +} + +uint64_t num_blocks(const std::vector<test_event_entry>& events) +{ + uint64_t res = 0; + BOOST_FOREACH(const test_event_entry& ev, events) + { + if (typeid(block) == ev.type()) + { + res += 1; + } + } + + return res; +} + +cryptonote::block get_head_block(const std::vector<test_event_entry>& events) +{ + for(auto it = events.rbegin(); it != events.rend(); ++it) + { + auto &ev = *it; + if (typeid(block) == ev.type()) + { + return boost::get<block>(ev); + } + } + + throw std::runtime_error("No block event"); +} + 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) @@ -655,6 +1099,38 @@ bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<c return b_success; } +bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<const 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) { diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 907b32bcd..8bf5a9b08 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -37,8 +37,12 @@ #include <boost/archive/binary_oarchive.hpp> #include <boost/archive/binary_iarchive.hpp> #include <boost/program_options.hpp> +#include <boost/optional.hpp> #include <boost/serialization/vector.hpp> #include <boost/serialization/variant.hpp> +#include <boost/serialization/optional.hpp> +#include <boost/serialization/unordered_map.hpp> +#include <boost/functional/hash.hpp> #include "include_base_utils.h" #include "common/boost_serialization_helper.h" @@ -129,13 +133,32 @@ private: } }; +typedef std::vector<std::pair<uint8_t, uint64_t>> v_hardforks_t; +struct event_replay_settings +{ + boost::optional<v_hardforks_t> hard_forks; + + event_replay_settings() = default; + +private: + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & hard_forks; + } +}; + + 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); +VARIANT_TAG(binary_archive, event_replay_settings, 0xda); -typedef boost::variant<cryptonote::block, cryptonote::transaction, std::vector<cryptonote::transaction>, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings> test_event_entry; +typedef boost::variant<cryptonote::block, cryptonote::transaction, std::vector<cryptonote::transaction>, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings, event_replay_settings> test_event_entry; typedef std::unordered_map<crypto::hash, const cryptonote::transaction*> map_hash2tx_t; class test_chain_unit_base @@ -173,6 +196,17 @@ public: crypto::hash prev_id; uint64_t already_generated_coins; size_t block_weight; + + private: + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & prev_id; + ar & already_generated_coins; + ar & block_weight; + } }; enum block_fields @@ -189,6 +223,8 @@ public: bf_hf_version= 1 << 8 }; + test_generator() {} + test_generator(const test_generator &other): m_blocks_info(other.m_blocks_info) {} void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const; void get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; @@ -198,10 +234,12 @@ public: uint8_t hf_version = 1); bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, - std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list); + std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list, + const boost::optional<uint8_t>& hf_ver = boost::none); 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>()); + const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>(), + const boost::optional<uint8_t>& hf_ver = boost::none); 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, @@ -214,30 +252,241 @@ public: private: std::unordered_map<crypto::hash, block_info> m_blocks_info; + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & m_blocks_info; + } }; -inline cryptonote::difficulty_type get_test_difficulty() {return 1;} +template<typename T> +std::string dump_keys(T * buff32) +{ + std::ostringstream ss; + char buff[10]; + + ss << "["; + for(int i = 0; i < 32; i++) + { + snprintf(buff, 10, "0x%02x", ((uint8_t)buff32[i] & 0xff)); + ss << buff; + if (i < 31) + ss << ","; + } + ss << "]"; + return ss.str(); +} + +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; + uint64_t unlock_time; + bool is_coin_base; + bool spent; + bool rct; + rct::key comm; + 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), unlock_time(0), + is_coin_base(false), spent(false), rct(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), rct(other.rct), + out_no(other.out_no), idx(other.idx), unlock_time(other.unlock_time), is_coin_base(other.is_coin_base), + spent(other.spent), comm(other.comm), p_blk(other.p_blk), p_tx(other.p_tx) { } + + void set_rct(bool arct) { + rct = arct; + if (rct && p_tx->rct_signatures.outPk.size() > out_no) + comm = p_tx->rct_signatures.outPk[out_no].mask; + else + comm = rct::commit(amount, rct::identity()); + } + + rct::key commitment() const { + return comm; + } + + 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 + << " unlock_time=" << unlock_time + << " spent=" << spent + << " is_coin_base=" << is_coin_base + << " rct=" << rct + << " comm=" << dump_keys(comm.bytes) + << "}"; + + return ss.str(); + } + + output_index& operator=(const output_index& other) + { + new(this) output_index(other); + return *this; + } +}; + +typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry; +typedef std::pair<crypto::hash, size_t> output_hasher; +typedef boost::hash<output_hasher> output_hasher_hasher; +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 std::unordered_map<crypto::hash, cryptonote::block> map_block_t; +typedef std::unordered_map<output_hasher, output_index, output_hasher_hasher> map_txid_output_t; +typedef std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses_t; +typedef std::pair<uint64_t, size_t> outloc_t; + +typedef boost::variant<cryptonote::account_public_address, cryptonote::account_keys, cryptonote::account_base, cryptonote::tx_destination_entry> var_addr_t; +typedef struct { + const var_addr_t addr; + bool is_subaddr; + uint64_t amount; +} dest_wrapper_t; + +// Daemon functionality +class block_tracker +{ +public: + map_output_idx_t m_outs; + map_txid_output_t m_map_outs; // mapping (txid, out) -> output_index + map_block_t m_blocks; + + block_tracker() = default; + block_tracker(const block_tracker &bt): m_outs(bt.m_outs), m_map_outs(bt.m_map_outs), m_blocks(bt.m_blocks) {}; + map_txid_output_t::iterator find_out(const crypto::hash &txid, size_t out); + map_txid_output_t::iterator find_out(const output_hasher &id); + void process(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx); + void process(const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t& mtx); + void process(const cryptonote::block* blk, const cryptonote::transaction * tx, size_t i); + void global_indices(const cryptonote::transaction *tx, std::vector<uint64_t> &indices); + void get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector<get_outs_entry> &outs); + + std::string dump_data(); + void dump_data(const std::string & fname); + +private: + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & m_outs; + ar & m_map_outs; + ar & m_blocks; + } +}; + +std::string dump_data(const cryptonote::transaction &tx); +cryptonote::account_public_address get_address(const var_addr_t& inp); +cryptonote::account_public_address get_address(const cryptonote::account_public_address& inp); +cryptonote::account_public_address get_address(const cryptonote::account_keys& inp); +cryptonote::account_public_address get_address(const cryptonote::account_base& inp); +cryptonote::account_public_address get_address(const cryptonote::tx_destination_entry& inp); + +inline cryptonote::difficulty_type get_test_difficulty(const boost::optional<uint8_t>& hf_ver=boost::none) {return !hf_ver || hf_ver.get() <= 1 ? 1 : 2;} +inline uint64_t current_difficulty_window(const boost::optional<uint8_t>& hf_ver=boost::none){ return !hf_ver || hf_ver.get() <= 1 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; } void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height); +cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr=false, uint64_t amount=0); +std::vector<cryptonote::tx_destination_entry> build_dsts(const var_addr_t& to1, bool sub1=false, uint64_t am1=0); +std::vector<cryptonote::tx_destination_entry> build_dsts(std::initializer_list<dest_wrapper_t> inps); +uint64_t sum_amount(const std::vector<cryptonote::tx_destination_entry>& destinations); +uint64_t sum_amount(const std::vector<cryptonote::tx_source_entry>& sources); + 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); + uint64_t fee, cryptonote::keypair* p_txkey = nullptr); + 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); + const cryptonote::block& blk_head, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 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, std::vector<cryptonote::tx_destination_entry> destinations, + uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); + 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, + const cryptonote::account_base& acc_from, const var_addr_t& to, uint64_t amount, uint64_t fee); +bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, + std::vector<cryptonote::tx_source_entry>& sources, + const std::vector<cryptonote::tx_destination_entry>& destinations, + const boost::optional<cryptonote::account_public_address>& change_addr, + std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, + bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + + +uint64_t num_blocks(const std::vector<test_event_entry>& events); +cryptonote::block get_head_block(const std::vector<test_event_entry>& events); + void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); +bool trim_block_chain(std::vector<cryptonote::block>& blockchain, const crypto::hash& tail); +bool trim_block_chain(std::vector<const cryptonote::block*>& blockchain, const crypto::hash& tail); bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<cryptonote::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); +bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<const cryptonote::block*>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); + +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<cryptonote::tx_source_entry> &sources, + std::vector<cryptonote::tx_destination_entry>& destinations, bool always_change=false); + +void fill_tx_destinations(const var_addr_t& from, const std::vector<cryptonote::tx_destination_entry>& dests, + uint64_t fee, + const std::vector<cryptonote::tx_source_entry> &sources, + std::vector<cryptonote::tx_destination_entry>& destinations, + bool always_change); + +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<cryptonote::tx_source_entry> &sources, + std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_destination_entry>& destinations_pure, + bool always_change=false); + + +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_public_address& to, + uint64_t amount, uint64_t fee, size_t nmix, + std::vector<cryptonote::tx_source_entry>& sources, + std::vector<cryptonote::tx_destination_entry>& destinations); + 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); +bool extract_hard_forks(const std::vector<test_event_entry>& events, v_hardforks_t& hard_forks); + //-------------------------------------------------------------------------- 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) @@ -338,6 +587,12 @@ public: m_ev_index = ev_index; } + bool operator()(const event_replay_settings& settings) + { + log_event("event_replay_settings"); + return true; + } + bool operator()(const event_visitor_settings& settings) { log_event("event_visitor_settings"); @@ -388,7 +643,7 @@ public: 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); + m_c.handle_incoming_block(t_serializable_object_to_blob(b), &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; @@ -411,7 +666,7 @@ public: log_event("serialized_block"); cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_c.handle_incoming_block(sr_block.data, bvc); + m_c.handle_incoming_block(sr_block.data, NULL, bvc); cryptonote::block blk; std::stringstream ss; @@ -461,12 +716,20 @@ private: 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) { + return replay_events_through_core_plain(cr, events, validator, true); +} +//-------------------------------------------------------------------------- +template<class t_test_class> +inline bool replay_events_through_core_plain(cryptonote::core& cr, const std::vector<test_event_entry>& events, t_test_class& validator, bool reinit=true) +{ 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])); + if (reinit) { + 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); @@ -489,10 +752,9 @@ struct get_test_options { }; get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{} }; - //-------------------------------------------------------------------------- template<class t_test_class> -inline bool do_replay_events(std::vector<test_event_entry>& events) +inline bool do_replay_events_get_core(std::vector<test_event_entry>& events, cryptonote::core **core) { boost::program_options::options_description desc("Allowed options"); cryptonote::core::init_options(desc); @@ -506,12 +768,24 @@ inline bool do_replay_events(std::vector<test_event_entry>& events) 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); + *core = new cryptonote::core(nullptr); + auto & c = **core; + // FIXME: make sure that vm has arg_testnet_on set to true or false if // this test needs for it to be so. get_test_options<t_test_class> gto; - if (!c.init(vm, >o.test_options)) + + // Hardforks can be specified in events. + v_hardforks_t hardforks; + cryptonote::test_options test_options_tmp{}; + const cryptonote::test_options * test_options_ = >o.test_options; + if (extract_hard_forks(events, hardforks)){ + hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator + test_options_tmp.hard_forks = hardforks.data(); + test_options_ = &test_options_tmp; + } + + if (!c.init(vm, test_options_)) { MERROR("Failed to init core"); return false; @@ -529,7 +803,31 @@ inline bool do_replay_events(std::vector<test_event_entry>& events) t_test_class validator; bool ret = replay_events_through_core<t_test_class>(c, events, validator); - c.deinit(); +// c.deinit(); + return ret; +} +//-------------------------------------------------------------------------- +template<class t_test_class> +inline bool replay_events_through_core_validate(std::vector<test_event_entry>& events, cryptonote::core & c) +{ + std::vector<crypto::hash> pool_txs; + if (!c.get_pool_transaction_hashes(pool_txs)) + { + MERROR("Failed to flush txpool"); + return false; + } + c.get_blockchain_storage().flush_txes_from_pool(pool_txs); + + t_test_class validator; + return replay_events_through_core_plain<t_test_class>(c, events, validator, false); +} +//-------------------------------------------------------------------------- +template<class t_test_class> +inline bool do_replay_events(std::vector<test_event_entry>& events) +{ + cryptonote::core * core; + bool ret = do_replay_events_get_core<t_test_class>(events, &core); + core->deinit(); return ret; } //-------------------------------------------------------------------------- @@ -546,6 +844,12 @@ inline bool do_replay_file(const std::string& filename) } //-------------------------------------------------------------------------- +#define DEFAULT_HARDFORKS(HARDFORKS) do { \ + HARDFORKS.push_back(std::make_pair((uint8_t)1, (uint64_t)0)); \ +} while(0) + +#define ADD_HARDFORK(HARDFORKS, FORK, HEIGHT) HARDFORKS.push_back(std::make_pair((uint8_t)FORK, (uint64_t)HEIGHT)) + #define GENERATE_ACCOUNT(account) \ cryptonote::account_base account; \ account.generate(); @@ -589,6 +893,11 @@ inline bool do_replay_file(const std::string& filename) generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC); \ VEC_EVENTS.push_back(BLK_NAME); +#define MAKE_NEXT_BLOCK_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, HF) \ + cryptonote::block BLK_NAME; \ + generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, std::list<cryptonote::transaction>(), HF); \ + VEC_EVENTS.push_back(BLK_NAME); + #define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \ cryptonote::block BLK_NAME; \ { \ @@ -598,46 +907,91 @@ inline bool do_replay_file(const std::string& filename) } \ VEC_EVENTS.push_back(BLK_NAME); +#define MAKE_NEXT_BLOCK_TX1_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1, HF) \ + 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, HF); \ + } \ + 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) \ +#define MAKE_NEXT_BLOCK_TX_LIST_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST, HF) \ cryptonote::block BLK_NAME; \ + generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST, HF); \ + VEC_EVENTS.push_back(BLK_NAME); + +#define REWIND_BLOCKS_N_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT, HF) \ + cryptonote::block BLK_NAME; \ { \ - cryptonote::block blk_last = PREV_BLOCK; \ + cryptonote::block blk_last = PREV_BLOCK; \ for (size_t i = 0; i < COUNT; ++i) \ { \ - MAKE_NEXT_BLOCK(VEC_EVENTS, blk, blk_last, MINER_ACC); \ + MAKE_NEXT_BLOCK_HF(VEC_EVENTS, blk, blk_last, MINER_ACC, HF); \ blk_last = blk; \ } \ BLK_NAME = blk_last; \ } +#define REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT) REWIND_BLOCKS_N_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT, boost::none) #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 REWIND_BLOCKS_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, HF) REWIND_BLOCKS_N_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, HF) #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_MIX_RCT(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, true, rct::RangeProofPaddedBulletproof); \ + 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; \ + 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_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, rct::RangeProofPaddedBulletproof, 1) +#define MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, RCT_TYPE, BP_VER) \ + { \ + cryptonote::transaction t; \ + construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, true, RCT_TYPE, BP_VER); \ + SET_NAME.push_back(t); \ + VEC_EVENTS.push_back(t); \ + } + +#define MAKE_TX_MIX_DEST_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD) \ + MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, rct::RangeProofPaddedBulletproof, 1) +#define MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, RCT_TYPE, BP_VER) \ + { \ + cryptonote::transaction t; \ + construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, TESTS_DEFAULT_FEE, NMIX, true, RCT_TYPE, BP_VER); \ + 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_TX_LIST_START_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + std::list<cryptonote::transaction> SET_NAME; \ + MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, 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), \ @@ -668,19 +1022,7 @@ inline bool do_replay_file(const std::string& filename) return 1; \ } -#define GENERATE_AND_PLAY(genclass) \ - if (list_tests) \ - std::cout << #genclass << std::endl; \ - else if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ - { \ - std::vector<test_event_entry> events; \ - ++tests_count; \ - bool generated = false; \ - try \ - { \ - genclass g; \ - generated = g.generate(events);; \ - } \ +#define CATCH_REPLAY(genclass) \ catch (const std::exception& ex) \ { \ MERROR(#genclass << " generation failed: what=" << ex.what()); \ @@ -688,7 +1030,9 @@ inline bool do_replay_file(const std::string& filename) catch (...) \ { \ MERROR(#genclass << " generation failed: generic exception"); \ - } \ + } + +#define REPLAY_CORE(genclass) \ if (generated && do_replay_events< genclass >(events)) \ { \ MGINFO_GREEN("#TEST# Succeeded " << #genclass); \ @@ -697,7 +1041,54 @@ inline bool do_replay_file(const std::string& filename) { \ MERROR("#TEST# Failed " << #genclass); \ failed_tests.push_back(#genclass); \ + } + +#define REPLAY_WITH_CORE(genclass, CORE) \ + if (generated && replay_events_through_core_validate< genclass >(events, CORE)) \ + { \ + MGINFO_GREEN("#TEST# Succeeded " << #genclass); \ + } \ + else \ + { \ + MERROR("#TEST# Failed " << #genclass); \ + failed_tests.push_back(#genclass); \ + } + +#define CATCH_GENERATE_REPLAY(genclass) \ + CATCH_REPLAY(genclass); \ + REPLAY_CORE(genclass); + +#define CATCH_GENERATE_REPLAY_CORE(genclass, CORE) \ + CATCH_REPLAY(genclass); \ + REPLAY_WITH_CORE(genclass, CORE); + +#define GENERATE_AND_PLAY(genclass) \ + if (list_tests) \ + std::cout << #genclass << std::endl; \ + else if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ + { \ + std::vector<test_event_entry> events; \ + ++tests_count; \ + bool generated = false; \ + try \ + { \ + genclass g; \ + generated = g.generate(events); \ + } \ + CATCH_GENERATE_REPLAY(genclass); \ + } + +#define GENERATE_AND_PLAY_INSTANCE(genclass, ins, CORE) \ + if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ + { \ + std::vector<test_event_entry> events; \ + ++tests_count; \ + bool generated = false; \ + try \ + { \ + generated = ins.generate(events); \ } \ + CATCH_GENERATE_REPLAY_CORE(genclass, CORE); \ } #define CALL_TEST(test_name, function) \ diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index a76cf1592..b313f4821 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 71b8c4463..cb35cfde2 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index c12e97f95..cf20c725c 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index c60ea885e..afd212b27 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 276e74853..70d9f84b9 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index d02147065..600899b6f 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index e7e1ac9ca..42df4aa3b 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index eaca268e4..75a60eb15 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 37fda6643..e0c90423d 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index e0d227840..1e8226d26 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index bb7dd013b..248354177 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index 641f3dd5e..72460d98e 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index 8b2b943cc..eb5ee48ec 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h index 7ff6cfd96..256f5732b 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 810dec6fc..14028b5be 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index 9500c66f6..eac2c3479 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index f0a385ee5..232c86482 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 01a258a20..8d457cdb5 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index b96c744b3..8c2b51acf 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index ecb23818c..71b0f0e83 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -81,7 +81,7 @@ template<> struct get_test_options<gen_v2_tx_validation_base> { const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp new file mode 100644 index 000000000..ff7ce3a34 --- /dev/null +++ b/tests/core_tests/wallet_tools.cpp @@ -0,0 +1,287 @@ +// +// Created by Dusan Klinec on 2019-02-28. +// + +#include "wallet_tools.h" +#include <random> + +using namespace std; +using namespace epee; +using namespace crypto; +using namespace cryptonote; + +// Shared random generator +static std::default_random_engine RND(crypto::rand<unsigned>()); + +void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::account_base& account) +{ + wallet->clear(); + wallet->m_account = account; + wallet->m_nettype = MAINNET; + + wallet->m_key_device_type = account.get_device().get_type(); + wallet->m_account_public_address = account.get_keys().m_account_address; + wallet->m_watch_only = false; + wallet->m_multisig = false; + wallet->m_multisig_threshold = 0; + wallet->m_multisig_signers.clear(); + wallet->m_device_name = account.get_device().get_name(); + + wallet->m_subaddress_lookahead_major = 5; + wallet->m_subaddress_lookahead_minor = 20; + + wallet->setup_new_blockchain(); // generates also subadress register +} + +void wallet_accessor_test::process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<tools::wallet2::parsed_block> &parsed_blocks, uint64_t& blocks_added) +{ + wallet->process_parsed_blocks(start_height, blocks, parsed_blocks, blocks_added); +} + +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional<crypto::hash>& blk_tail) +{ + map_hash2tx_t mtx; + std::vector<const cryptonote::block*> blockchain; + find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)); + + if (blk_tail){ + trim_block_chain(blockchain, blk_tail.get()); + } + + process_transactions(wallet, blockchain, mtx, bt); +} + +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) +{ + uint64_t start_height=0, blocks_added=0; + std::vector<cryptonote::block_complete_entry> v_bche; + std::vector<tools::wallet2::parsed_block> v_parsed_block; + + v_bche.reserve(blockchain.size()); + v_parsed_block.reserve(blockchain.size()); + + size_t idx = 0; + for(auto bl : blockchain) + { + idx += 1; + uint64_t height; + v_bche.emplace_back(); + v_parsed_block.emplace_back(); + + wallet_tools::gen_block_data(bt, bl, mtx, v_bche.back(), v_parsed_block.back(), idx == 1 ? start_height : height); + } + + if (wallet) + wallet_accessor_test::process_parsed_blocks(wallet, start_height, v_bche, v_parsed_block, blocks_added); +} + +bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector<cryptonote::tx_source_entry>& sources, size_t mixin, const boost::optional<size_t>& num_utxo, const boost::optional<uint64_t>& min_amount, block_tracker &bt, std::vector<size_t> &selected, uint64_t cur_height, ssize_t offset, int step, const boost::optional<fnc_accept_tx_source_t>& fnc_accept) +{ + CHECK_AND_ASSERT_THROW_MES(step != 0, "Step is zero"); + sources.clear(); + + auto & transfers = wallet_accessor_test::get_transfers(wallet); + std::unordered_set<size_t> selected_idx; + std::unordered_set<crypto::key_image> selected_kis; + const size_t ntrans = wallet->get_num_transfer_details(); + size_t roffset = offset >= 0 ? offset : ntrans - offset - 1; + size_t iters = 0; + uint64_t sum = 0; + size_t cur_utxo = 0; + bool abort = false; + unsigned brk_cond = 0; + unsigned brk_thresh = num_utxo && min_amount ? 2 : (num_utxo || min_amount ? 1 : 0); + +#define EVAL_BRK_COND() do { \ + brk_cond = 0; \ + if (num_utxo && num_utxo.get() <= cur_utxo) \ + brk_cond += 1; \ + if (min_amount && min_amount.get() <= sum) \ + brk_cond += 1; \ + } while(0) + + for(ssize_t i = roffset; iters < ntrans && !abort; i += step, ++iters) + { + EVAL_BRK_COND(); + if (brk_cond >= brk_thresh) + break; + + i = i < 0 ? (i + ntrans) : i % ntrans; + auto & td = transfers[i]; + if (td.m_spent) + continue; + if (td.m_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW > cur_height) + continue; + if (selected_idx.find((size_t)i) != selected_idx.end()){ + MERROR("Should not happen (selected_idx not found): " << i); + continue; + } + if (selected_kis.find(td.m_key_image) != selected_kis.end()){ + MERROR("Should not happen (selected KI): " << i << "ki: " << dump_keys(td.m_key_image.data)); + continue; + } + + try { + cryptonote::tx_source_entry src; + wallet_tools::gen_tx_src(mixin, cur_height, td, src, bt); + + // Acceptor function + if (fnc_accept){ + tx_source_info_crate_t c_info{.td=&td, .src=&src, .selected_idx=&selected_idx, .selected_kis=&selected_kis, + .ntrans=ntrans, .iters=iters, .sum=sum, .cur_utxo=cur_utxo}; + + bool take_it = (fnc_accept.get())(c_info, abort); + if (!take_it){ + continue; + } + } + + MINFO("Selected " << i << " from tx: " << dump_keys(td.m_txid.data) + << " ki: " << dump_keys(td.m_key_image.data) + << " amnt: " << td.amount() + << " rct: " << td.is_rct() + << " glob: " << td.m_global_output_index); + + sum += td.amount(); + cur_utxo += 1; + + sources.emplace_back(src); + selected.push_back((size_t)i); + selected_idx.insert((size_t)i); + selected_kis.insert(td.m_key_image); + + } catch(const std::exception &e){ + MTRACE("Output " << i << ", from: " << dump_keys(td.m_txid.data) + << ", amnt: " << td.amount() << ", rct: " << td.is_rct() + << ", glob: " << td.m_global_output_index << " is not applicable: " << e.what()); + } + } + + EVAL_BRK_COND(); + return brk_cond >= brk_thresh; +#undef EVAL_BRK_COND +} + +void wallet_tools::gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt) +{ + src.amount = td.amount(); + src.rct = td.is_rct(); + + std::vector<tools::wallet2::get_outs_entry> outs; + bt.get_fake_outs(mixin, td.is_rct() ? 0 : td.amount(), td.m_global_output_index, cur_height, outs); + + for (size_t n = 0; n < mixin; ++n) + { + cryptonote::tx_source_entry::output_entry oe; + oe.first = std::get<0>(outs[n]); + oe.second.dest = rct::pk2rct(std::get<1>(outs[n])); + oe.second.mask = std::get<2>(outs[n]); + src.outputs.push_back(oe); + } + + size_t real_idx = crypto::rand<size_t>() % mixin; + + cryptonote::tx_source_entry::output_entry &real_oe = src.outputs[real_idx]; + real_oe.first = td.m_global_output_index; + real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key); + real_oe.second.mask = rct::commit(td.amount(), td.m_mask); + + std::sort(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry i0, const cryptonote::tx_source_entry::output_entry i1) { + return i0.first < i1.first; + }); + + for (size_t i = 0; i < src.outputs.size(); ++i){ + if (src.outputs[i].first == td.m_global_output_index){ + src.real_output = i; + break; + } + } + + src.mask = td.m_mask; + src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index); + src.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + src.real_output_in_tx_index = td.m_internal_output_index; + src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); +} + +void wallet_tools::gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t &mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height) +{ + vector<const transaction*> vtx; + vtx.push_back(&(bl->miner_tx)); + height = boost::get<txin_gen>(*bl->miner_tx.vin.begin()).height; + + BOOST_FOREACH(const crypto::hash &h, bl->tx_hashes) { + const map_hash2tx_t::const_iterator cit = mtx.find(h); + CHECK_AND_ASSERT_THROW_MES(mtx.end() != cit, "block contains an unknown tx hash @ " << height << ", " << h); + vtx.push_back(cit->second); + } + + bche.block = "NA"; + bche.txs.resize(bl->tx_hashes.size()); + + parsed_block.error = false; + parsed_block.hash = get_block_hash(*bl); + parsed_block.block = *bl; + parsed_block.txes.reserve(bl->tx_hashes.size()); + + auto & o_indices = parsed_block.o_indices.indices; + o_indices.reserve(bl->tx_hashes.size() + 1); + + size_t cur = 0; + BOOST_FOREACH(const transaction *tx, vtx){ + cur += 1; + o_indices.emplace_back(); + bt.process(bl, tx, cur - 1); + bt.global_indices(tx, o_indices.back().indices); + + if (cur > 1) // miner not included + parsed_block.txes.push_back(*tx); + } +} + +void wallet_tools::compute_subaddresses(std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors) +{ + auto &hwdev = hw::get_device("default"); + const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(creds.get_keys(), account, 0, minors); + + for(uint32_t c = 0; c < pkeys.size(); ++c){ + cryptonote::subaddress_index sidx{(uint32_t)account, c}; + subaddresses[pkeys[c]] = sidx; + } +} + +cryptonote::account_public_address get_address(const tools::wallet2* inp) +{ + return (inp)->get_account().get_keys().m_account_address; +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + tools::wallet2 * sender_wallet, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_destination_entry> destinations; + fill_tx_destinations(sender_wallet->get_account(), get_address(to), amount, fee, sources, destinations, rct); + return construct_tx_rct(sender_wallet, sources, destinations, get_address(sender_wallet), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + tools::wallet2 * sender_wallet, + const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_destination_entry> all_destinations; + fill_tx_destinations(sender_wallet->get_account(), destinations, fee, sources, all_destinations, rct); + return construct_tx_rct(sender_wallet, sources, all_destinations, get_address(sender_wallet), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_rct(tools::wallet2 * sender_wallet, std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + subaddresses_t & subaddresses = wallet_accessor_test::get_subaddresses(sender_wallet); + crypto::secret_key tx_key; + std::vector<crypto::secret_key> additional_tx_keys; + std::vector<tx_destination_entry> destinations_copy = destinations; + rct::RCTConfig rct_config = {range_proof_type, bp_version}; + return construct_tx_and_get_tx_key(sender_wallet->get_account().get_keys(), subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); +} diff --git a/tests/core_tests/wallet_tools.h b/tests/core_tests/wallet_tools.h new file mode 100644 index 000000000..03db04c99 --- /dev/null +++ b/tests/core_tests/wallet_tools.h @@ -0,0 +1,86 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "chaingen.h" +#include "wallet/wallet2.h" + +typedef struct { + tools::wallet2::transfer_details * td; + cryptonote::tx_source_entry * src; + + std::unordered_set<size_t> * selected_idx; + std::unordered_set<crypto::key_image> * selected_kis; + size_t ntrans; + size_t iters; + uint64_t sum; + size_t cur_utxo; +} tx_source_info_crate_t; + +typedef std::function<bool(const tx_source_info_crate_t &info, bool &abort)> fnc_accept_tx_source_t; + +// Wallet friend, direct access to required fields and private methods +class wallet_accessor_test +{ +public: + static void set_account(tools::wallet2 * wallet, cryptonote::account_base& account); + static tools::wallet2::transfer_container & get_transfers(tools::wallet2 * wallet) { return wallet->m_transfers; } + static subaddresses_t & get_subaddresses(tools::wallet2 * wallet) { return wallet->m_subaddresses; } + static void process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<tools::wallet2::parsed_block> &parsed_blocks, uint64_t& blocks_added); +}; + +class wallet_tools +{ +public: + static void gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt); + static void gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t & mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height); + static void compute_subaddresses(std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors); + static void process_transactions(tools::wallet2 * wallet, const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional<crypto::hash>& blk_tail=boost::none); + static void process_transactions(tools::wallet2 * wallet, const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t & mtx, block_tracker &bt); + static bool fill_tx_sources(tools::wallet2 * wallet, std::vector<cryptonote::tx_source_entry>& sources, size_t mixin, const boost::optional<size_t>& num_utxo, const boost::optional<uint64_t>& min_amount, block_tracker &bt, std::vector<size_t> &selected, uint64_t cur_height, ssize_t offset=0, int step=1, const boost::optional<fnc_accept_tx_source_t>& fnc_accept=boost::none); +}; + +cryptonote::account_public_address get_address(const tools::wallet2*); + +bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * from_wallet, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * sender_wallet, const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); + +bool construct_tx_rct(tools::wallet2 * sender_wallet, + std::vector<cryptonote::tx_source_entry>& sources, + const std::vector<cryptonote::tx_destination_entry>& destinations, + const boost::optional<cryptonote::account_public_address>& change_addr, + std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, + bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt index df96c57cc..883efb3b0 100644 --- a/tests/crypto/CMakeLists.txt +++ b/tests/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -52,3 +52,16 @@ set_property(TARGET cncrypto-tests add_test( NAME cncrypto COMMAND cncrypto-tests "${CMAKE_CURRENT_SOURCE_DIR}/tests.txt") + +add_executable(cnv4-jit-tests cnv4-jit.c) +target_link_libraries(cnv4-jit-tests + PRIVATE + common + ${EXTRA_LIBRARIES}) +set_property(TARGET cnv4-jit-tests + PROPERTY + FOLDER "tests") + +add_test( + NAME cnv4-jit + COMMAND cnv4-jit-tests 1788000 1789000) diff --git a/tests/crypto/cnv4-jit.c b/tests/crypto/cnv4-jit.c new file mode 100644 index 000000000..0f11e4393 --- /dev/null +++ b/tests/crypto/cnv4-jit.c @@ -0,0 +1,119 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "crypto/hash-ops.h" + +extern volatile int use_v4_jit_flag; + +static int test(const uint8_t *data, size_t len, uint64_t height) +{ + char hash0[32], hash1[32]; + use_v4_jit_flag = 0; + cn_slow_hash(data, len, hash0, 4, 0, height); + use_v4_jit_flag = 1; + cn_slow_hash(data, len, hash1, 4, 0, height); + return memcmp(hash0, hash1, 32); +} + +int main(int argc, char **argv) +{ + uint8_t data[64]; + uint64_t start_height = 1788000; + uint64_t end_height = 1788001; + + if (argc != 1 && argc != 2 && argc != 3) + { + fprintf(stderr, "usage: %s [<start_height> [<end_height>]]\n", argv[0]); + return 1; + } + if (argc > 1) + { + errno = 0; + start_height = strtoull(argv[1], NULL, 10); + if ((start_height == 0 && errno) || start_height == ULLONG_MAX) + { + fprintf(stderr, "invalid start_height\n"); + return 1; + } + end_height = start_height; + if (argc > 2) + { + errno = 0; + end_height = strtoull(argv[2], NULL, 10); + if ((end_height == 0 && errno) || end_height == ULLONG_MAX) + { + fprintf(stderr, "invalid end_height\n"); + return 1; + } + } + } + + if (start_height == end_height) + { + uint64_t counter = 0; + while (1) + { + printf("\r%llu", (unsigned long long)counter); + fflush(stdout); + size_t offset = 0; + while (offset + 8 < sizeof(data)) + { + memcpy(data + offset, &counter, sizeof(counter)); + offset += 8; + } + if (test(data, sizeof(data), start_height)) + { + fprintf(stderr, "\nFailure at height %llu, counter %llu\n", (unsigned long long)start_height, (unsigned long long)counter); + return 0; + } + ++counter; + } + } + + memset(data, 0x42, sizeof(data)); + for (uint64_t h = start_height; h < end_height; ++h) + { + printf("\r%llu/%llu", (unsigned long long)(h-start_height), (unsigned long long)(end_height-start_height)); + fflush(stdout); + if (test(data, sizeof(data), h)) + { + fprintf(stderr, "\nFailure at height %llu\n", (unsigned long long)h); + return 0; + } + } + + printf("\r"); + + return 0; +} diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 9bdd6b88f..63fedff07 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index 476de786a..9b1ccfcee 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index d910a845c..be8d5a2e1 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index 6a1dd1b29..160e0a23f 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index 6b865abd9..d65f13f11 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index cc3b53b83..3cdef4ce7 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/random.c b/tests/crypto/random.c index e9464d457..7a17cbccc 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/cryptolib.pl b/tests/cryptolib.pl index 3d4cb638b..d26ce601c 100644 --- a/tests/cryptolib.pl +++ b/tests/cryptolib.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/cryptotest.pl b/tests/cryptotest.pl index 13f8db09e..57f6e2c5a 100644 --- a/tests/cryptotest.pl +++ b/tests/cryptotest.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/CMakeLists.txt b/tests/daemon_tests/CMakeLists.txt index bc1dce2cc..a32f26584 100644 --- a/tests/daemon_tests/CMakeLists.txt +++ b/tests/daemon_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp index 726f11dfb..e20e6b280 100644 --- a/tests/daemon_tests/transfers.cpp +++ b/tests/daemon_tests/transfers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt index 3cb8af8d5..2ed495806 100644 --- a/tests/difficulty/CMakeLists.txt +++ b/tests/difficulty/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 996913a19..ee20e27e4 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/difficulty/generate-data b/tests/difficulty/generate-data index c41ce025d..3a9976e05 100755 --- a/tests/difficulty/generate-data +++ b/tests/difficulty/generate-data @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 7a2a7fbd1..2e3519994 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index b9a686dbc..c6869f755 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 7e5b73415..32b601d7a 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h index 78135b9c0..530400df8 100644 --- a/tests/functional_tests/transactions_flow_test.h +++ b/tests/functional_tests/transactions_flow_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp index 92dc00f2d..b9c43e0e9 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h index b684f7c7a..cb5f94f80 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.h +++ b/tests/functional_tests/transactions_generation_from_blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index fdb745699..a6ef139f5 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/fuzz/base58.cpp b/tests/fuzz/base58.cpp index a4857bdd1..5f909a5d9 100644 --- a/tests/fuzz/base58.cpp +++ b/tests/fuzz/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp index eed3b94b2..850c58890 100644 --- a/tests/fuzz/block.cpp +++ b/tests/fuzz/block.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp index 2f3a2f8d1..e9a6ded7d 100644 --- a/tests/fuzz/bulletproof.cpp +++ b/tests/fuzz/bulletproof.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index 29b3ed267..f3f35e140 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index fa3041ba3..83493f8a0 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index ab14e2b79..24db5ee05 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/fuzzer.h b/tests/fuzz/fuzzer.h index 2bf01914a..5cbd1abc2 100644 --- a/tests/fuzz/fuzzer.h +++ b/tests/fuzz/fuzzer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp index 909325832..1750838ae 100644 --- a/tests/fuzz/http-client.cpp +++ b/tests/fuzz/http-client.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index 573abd1d3..fe9ef418e 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/load_from_binary.cpp b/tests/fuzz/load_from_binary.cpp index 89f122902..85b7361e5 100644 --- a/tests/fuzz/load_from_binary.cpp +++ b/tests/fuzz/load_from_binary.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/load_from_json.cpp b/tests/fuzz/load_from_json.cpp index 083555f7e..3ba98050b 100644 --- a/tests/fuzz/load_from_json.cpp +++ b/tests/fuzz/load_from_json.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/parse_url.cpp b/tests/fuzz/parse_url.cpp index fb5754a70..3db78f9d9 100644 --- a/tests/fuzz/parse_url.cpp +++ b/tests/fuzz/parse_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index f82ada8b4..ab8faa29f 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp index 934bd4685..0f62888a1 100644 --- a/tests/fuzz/transaction.cpp +++ b/tests/fuzz/transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index 2737ae30e..70368ce24 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 433cf94e9..a0c78bfdc 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -43,7 +43,7 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow slow-1 slow-2 tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow slow-1 slow-2 slow-4 tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 7767d0d3b..adf1bd9c4 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -44,6 +44,13 @@ using namespace std; using namespace crypto; typedef crypto::hash chash; +struct V4_Data +{ + const void* data; + size_t length; + uint64_t height; +}; + PUSH_WARNINGS DISABLE_VS_WARNINGS(4297) extern "C" { @@ -54,13 +61,17 @@ extern "C" { tree_hash((const char (*)[crypto::HASH_SIZE]) data, length >> 5, hash); } static void cn_slow_hash_0(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_1(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_2(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/, 0/*height*/); + } + static void cn_slow_hash_4(const void *data, size_t, char *hash) { + const V4_Data* p = reinterpret_cast<const V4_Data*>(data); + return cn_slow_hash(p->data, p->length, hash, 4/*variant*/, 0/*prehashed*/, p->height); } } POP_WARNINGS @@ -72,7 +83,7 @@ struct hash_func { } hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash_0}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}, - {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}}; + {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}, {"slow-4", cn_slow_hash_4}}; int test_variant2_int_sqrt(); int test_variant2_int_sqrt_ref(); @@ -140,7 +151,15 @@ int main(int argc, char *argv[]) { input.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); input.clear(input.rdstate()); get(input, data); - f(data.data(), data.size(), (char *) &actual); + if (f == cn_slow_hash_4) { + V4_Data d; + d.data = data.data(); + d.length = data.size(); + get(input, d.height); + f(&d, 0, (char *) &actual); + } else { + f(data.data(), data.size(), (char *) &actual); + } if (expected != actual) { size_t i; cerr << "Hash mismatch on test " << test << endl << "Input: "; diff --git a/tests/hash/tests-slow-4.txt b/tests/hash/tests-slow-4.txt new file mode 100644 index 000000000..06ab52cff --- /dev/null +++ b/tests/hash/tests-slow-4.txt @@ -0,0 +1,10 @@ +f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 1806260 +5bb833deca2bdd7252a9ccd7b4ce0b6a4854515794b56c207262f7a5b9bdb566 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 1806261 +1ee6728da60fbd8d7d55b2b1ade487a3cf52a2c3ac6f520db12c27d8921f6cab 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 1806262 +6969fe2ddfb758438d48049f302fc2108a4fcc93e37669170e6db4b0b9b4c4cb 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c 1806263 +7f3048b4e90d0cbe7a57c0394f37338a01fae3adfdc0e5126d863a895eb04e02 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 1806264 +1d290443a4b542af04a82f6b2494a6ee7f20f2754c58e0849032483a56e8e2ef 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 1806265 +c43cc6567436a86afbd6aa9eaa7c276e9806830334b614b2bee23cc76634f6fd 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 1806266 +87be2479c0c4e8edfdfaa5603e93f4265b3f8224c1c5946feb424819d18990a4 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e 1806267 +dd9d6a6d8e47465cceac0877ef889b93e7eba979557e3935d7f86dce11b070f3 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c 1806268 +75c6f2ae49a20521de97285b431e717125847fb8935ed84a61e7f8d36a2c3d8e 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e 1806269 diff --git a/tests/io.h b/tests/io.h index ec5a9ee60..0f65ed297 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt index 1a9cbc5a6..cb1d563b0 100644 --- a/tests/libwallet_api_tests/CMakeLists.txt +++ b/tests/libwallet_api_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index fe31541df..02faae50d 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/CMakeLists.txt b/tests/net_load_tests/CMakeLists.txt index 1407bbf70..40b5561a7 100644 --- a/tests/net_load_tests/CMakeLists.txt +++ b/tests/net_load_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 77cce2be7..fc2280f23 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 6c4f7cb76..cdc2d267a 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 092c6955c..fe32ec5cb 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 5cd054d86..aa424da80 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h index 7bb702c3d..7136fbbfe 100644 --- a/tests/performance_tests/bulletproof.h +++ b/tests/performance_tests/bulletproof.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index 755d8f941..329714c56 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/cn_fast_hash.h b/tests/performance_tests/cn_fast_hash.h index 2ddaef269..63b2c5bee 100644 --- a/tests/performance_tests/cn_fast_hash.h +++ b/tests/performance_tests/cn_fast_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index b484afa41..4f410d62d 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -34,6 +34,7 @@ #include "crypto/crypto.h" #include "cryptonote_basic/cryptonote_basic.h" +template<unsigned int variant> class test_cn_slow_hash { public: @@ -42,18 +43,15 @@ public: #pragma pack(push, 1) struct data_t { - char data[13]; + char data[43]; }; #pragma pack(pop) - static_assert(13 == sizeof(data_t), "Invalid structure size"); + static_assert(43 == sizeof(data_t), "Invalid structure size"); bool init() { - if (!epee::string_tools::hex_to_pod("63617665617420656d70746f72", m_data)) - return false; - - if (!epee::string_tools::hex_to_pod("bbec2cacf69866a8e740380fe7b818fc78f8571221742d729d9d02d7f8989b87", m_expected_hash)) + if (!epee::string_tools::hex_to_pod("63617665617420656d70746f763617665617420656d70746f72263617665617420656d70746f7201020304", m_data)) return false; return true; @@ -62,11 +60,10 @@ public: bool test() { crypto::hash hash; - crypto::cn_slow_hash(&m_data, sizeof(m_data), hash); - return hash == m_expected_hash; + crypto::cn_slow_hash(&m_data, sizeof(m_data), hash, variant); + return true; } private: data_t m_data; - crypto::hash m_expected_hash; }; diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 71d42073d..ebfe46edf 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 3ebb6f470..5b215cef4 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index 35c4ff062..ad0b3b55f 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index c478a2a99..ba01b57c0 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h index 8d24d7da7..5788ebec8 100644 --- a/tests/performance_tests/equality.h +++ b/tests/performance_tests/equality.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h index 3f7d55182..1a0601c74 100644 --- a/tests/performance_tests/ge_frombytes_vartime.h +++ b/tests/performance_tests/ge_frombytes_vartime.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/ge_tobytes.h b/tests/performance_tests/ge_tobytes.h index 3d46f4838..66e6fce38 100644 --- a/tests/performance_tests/ge_tobytes.h +++ b/tests/performance_tests/ge_tobytes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h index 3f6238265..1f0066589 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index 5155e64a5..df39d7088 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index ede48b6ca..d4d83c07d 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_keypair.h b/tests/performance_tests/generate_keypair.h index 91c830166..cf28f7f58 100644 --- a/tests/performance_tests/generate_keypair.h +++ b/tests/performance_tests/generate_keypair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 9f81995ac..8b4516843 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index bfe6de895..e6558a364 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -77,10 +77,12 @@ int main(int argc, char** argv) const command_line::arg_descriptor<bool> arg_verbose = { "verbose", "Verbose output", false }; const command_line::arg_descriptor<bool> arg_stats = { "stats", "Including statistics (min/median)", false }; const command_line::arg_descriptor<unsigned> arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 }; + const command_line::arg_descriptor<std::string> arg_timings_database = { "timings-database", "Keep timings history in a file" }; command_line::add_arg(desc_options, arg_filter); command_line::add_arg(desc_options, arg_verbose); command_line::add_arg(desc_options, arg_stats); command_line::add_arg(desc_options, arg_loop_multiplier); + command_line::add_arg(desc_options, arg_timings_database); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() @@ -93,7 +95,10 @@ int main(int argc, char** argv) return 1; const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); + const std::string timings_database = command_line::get_arg(vm, arg_timings_database); Params p; + if (!timings_database.empty()) + p.td = TimingsDatabase(timings_database); p.verbose = command_line::get_arg(vm, arg_verbose); p.stats = command_line::get_arg(vm, arg_stats); p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); @@ -193,7 +198,10 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200); - TEST_PERFORMANCE0(filter, p, test_cn_slow_hash); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 0); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 1); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 2); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 4); TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 32); TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 16384); diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index ae9d6ea7a..bdbbee37d 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index d37dda729..606c5980f 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -37,7 +37,9 @@ #include <boost/regex.hpp> #include "misc_language.h" +#include "stats.h" #include "common/perf_timer.h" +#include "common/timings.h" class performance_timer { @@ -67,6 +69,7 @@ private: struct Params { + TimingsDatabase td; bool verbose; bool stats; unsigned loop_multiplier; @@ -85,6 +88,8 @@ public: bool run() { + static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); + T test; if (!test.init()) return false; @@ -106,11 +111,13 @@ public: m_per_call_timers[i].pause(); } m_elapsed = timer.elapsed_ms(); + m_stats.reset(new Stats<tools::PerformanceTimer, uint64_t>(m_per_call_timers)); return true; } int elapsed_time() const { return m_elapsed; } + size_t get_size() const { return m_stats->get_size(); } int time_per_call(int scale = 1) const { @@ -118,59 +125,19 @@ public: return m_elapsed * scale / (T::loop_count * m_params.loop_multiplier); } - uint64_t per_call_min() const - { - uint64_t v = std::numeric_limits<uint64_t>::max(); - for (const auto &pt: m_per_call_timers) - v = std::min(v, pt.value()); - return v; - } - - uint64_t per_call_max() const - { - uint64_t v = std::numeric_limits<uint64_t>::min(); - for (const auto &pt: m_per_call_timers) - v = std::max(v, pt.value()); - return v; - } - - uint64_t per_call_mean() const - { - uint64_t v = 0; - for (const auto &pt: m_per_call_timers) - v += pt.value(); - return v / m_per_call_timers.size(); - } - - uint64_t per_call_median() const - { - std::vector<uint64_t> values; - values.reserve(m_per_call_timers.size()); - for (const auto &pt: m_per_call_timers) - values.push_back(pt.value()); - return epee::misc_utils::median(values); - } + uint64_t get_min() const { return m_stats->get_min(); } + uint64_t get_max() const { return m_stats->get_max(); } + double get_mean() const { return m_stats->get_mean(); } + uint64_t get_median() const { return m_stats->get_median(); } + double get_stddev() const { return m_stats->get_standard_deviation(); } + double get_non_parametric_skew() const { return m_stats->get_non_parametric_skew(); } + std::vector<uint64_t> get_quantiles(size_t n) const { return m_stats->get_quantiles(n); } - uint64_t per_call_stddev() const + bool is_same_distribution(size_t npoints, double mean, double stddev) const { - if (m_per_call_timers.size() <= 1) - return 0; - const uint64_t mean = per_call_mean(); - uint64_t acc = 0; - for (const auto &pt: m_per_call_timers) - { - int64_t dv = pt.value() - mean; - acc += dv * dv; - } - acc /= m_per_call_timers.size () - 1; - return sqrt(acc); + return m_stats->is_same_distribution_99(npoints, mean, stddev); } - uint64_t min_time_ns() const { return tools::ticks_to_ns(per_call_min()); } - uint64_t max_time_ns() const { return tools::ticks_to_ns(per_call_max()); } - uint64_t median_time_ns() const { return tools::ticks_to_ns(per_call_median()); } - uint64_t standard_deviation_time_ns() const { return tools::ticks_to_ns(per_call_stddev()); } - private: /** * Warm up processor core, enabling turbo boost, etc. @@ -191,10 +158,11 @@ private: int m_elapsed; Params m_params; std::vector<tools::PerformanceTimer> m_per_call_timers; + std::unique_ptr<Stats<tools::PerformanceTimer, uint64_t>> m_stats; }; template <typename T> -void run_test(const std::string &filter, const Params ¶ms, const char* test_name) +void run_test(const std::string &filter, Params ¶ms, const char* test_name) { boost::smatch match; if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) @@ -210,10 +178,10 @@ void run_test(const std::string &filter, const Params ¶ms, const char* test_ std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; if (params.stats) { - std::cout << " min: " << runner.min_time_ns() << " ns\n"; - std::cout << " max: " << runner.max_time_ns() << " ns\n"; - std::cout << " median: " << runner.median_time_ns() << " ns\n"; - std::cout << " std dev: " << runner.standard_deviation_time_ns() << " ns\n"; + std::cout << " min: " << runner.get_min() << " ns\n"; + std::cout << " max: " << runner.get_max() << " ns\n"; + std::cout << " median: " << runner.get_median() << " ns\n"; + std::cout << " std dev: " << runner.get_stddev() << " ns\n"; } } else @@ -221,24 +189,48 @@ void run_test(const std::string &filter, const Params ¶ms, const char* test_ std::cout << test_name << " (" << T::loop_count * params.loop_multiplier << " calls) - OK:"; } const char *unit = "ms"; - uint64_t scale = 1000000; - int time_per_call = runner.time_per_call(); - if (time_per_call < 30000) { + double scale = 1000000; + uint64_t time_per_call = runner.time_per_call(); + if (time_per_call < 100) { + scale = 1000; time_per_call = runner.time_per_call(1000); #ifdef _WIN32 unit = "\xb5s"; #else unit = "µs"; #endif - scale = 1000; } + const auto quantiles = runner.get_quantiles(10); + double min = runner.get_min(); + double max = runner.get_max(); + double med = runner.get_median(); + double mean = runner.get_mean(); + double stddev = runner.get_stddev(); + double npskew = runner.get_non_parametric_skew(); + + std::vector<TimingsDatabase::instance> prev_instances = params.td.get(test_name); + params.td.add(test_name, {time(NULL), runner.get_size(), min, max, mean, med, stddev, npskew, quantiles}); + std::cout << (params.verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (params.verbose ? "\n" : ""); if (params.stats) { - uint64_t min_ns = runner.min_time_ns() / scale; - uint64_t med_ns = runner.median_time_ns() / scale; - uint64_t stddev_ns = runner.standard_deviation_time_ns() / scale; - std::cout << " (min " << min_ns << " " << unit << ", median " << med_ns << " " << unit << ", std dev " << stddev_ns << " " << unit << ")"; + uint64_t mins = min / scale; + uint64_t maxs = max / scale; + uint64_t meds = med / scale; + uint64_t p95s = quantiles[9] / scale; + uint64_t stddevs = stddev / scale; + std::string cmp; + if (!prev_instances.empty()) + { + const TimingsDatabase::instance &prev_instance = prev_instances.back(); + if (!runner.is_same_distribution(prev_instance.npoints, prev_instance.mean, prev_instance.stddev)) + { + double pc = fabs(100. * (prev_instance.mean - runner.get_mean()) / prev_instance.mean); + cmp = ", " + std::to_string(pc) + "% " + (mean > prev_instance.mean ? "slower" : "faster"); + } +cmp += " -- " + std::to_string(prev_instance.mean); + } + std::cout << " (min " << mins << " " << unit << ", 90th " << p95s << " " << unit << ", median " << meds << " " << unit << ", std dev " << stddevs << " " << unit << ")" << cmp; } std::cout << std::endl; } diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index e46a7bce4..8a45bea90 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -40,7 +40,7 @@ void set_process_affinity(int core) { -#if defined (__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) +#if defined (__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__sun) return; #elif defined(BOOST_WINDOWS) DWORD_PTR mask = 1; @@ -62,7 +62,7 @@ void set_process_affinity(int core) void set_thread_high_priority() { -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(_NetBSD_) || defined(__sun) return; #elif defined(BOOST_WINDOWS) ::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS); diff --git a/tests/performance_tests/range_proof.h b/tests/performance_tests/range_proof.h index 0ce962cd9..548237814 100644 --- a/tests/performance_tests/range_proof.h +++ b/tests/performance_tests/range_proof.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/rct_mlsag.h b/tests/performance_tests/rct_mlsag.h index 888f4b260..0141710f7 100644 --- a/tests/performance_tests/rct_mlsag.h +++ b/tests/performance_tests/rct_mlsag.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/sc_reduce32.h b/tests/performance_tests/sc_reduce32.h index fc5606414..8db1ca70a 100644 --- a/tests/performance_tests/sc_reduce32.h +++ b/tests/performance_tests/sc_reduce32.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/signature.h b/tests/performance_tests/signature.h index 21110b525..63c63ea46 100644 --- a/tests/performance_tests/signature.h +++ b/tests/performance_tests/signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 365d25444..52fbf5f80 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/subaddress_expand.h b/tests/performance_tests/subaddress_expand.h index 2a13ff5c2..61fcb41f3 100644 --- a/tests/performance_tests/subaddress_expand.h +++ b/tests/performance_tests/subaddress_expand.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/trezor/CMakeLists.txt b/tests/trezor/CMakeLists.txt new file mode 100644 index 000000000..67c2f8438 --- /dev/null +++ b/tests/trezor/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(trezor_tests_sources + trezor_tests.cpp + ../core_tests/chaingen.cpp + ../core_tests/wallet_tools.cpp) + +set(trezor_tests_headers + trezor_tests.h + ../core_tests/chaingen.h + ../core_tests/wallet_tools.h) + +add_executable(trezor_tests + ${trezor_tests_sources} + ${trezor_tests_headers}) + +target_link_libraries(trezor_tests + PRIVATE + multisig + cryptonote_core + p2p + version + epee + device + device_trezor + wallet + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +enable_stack_trace(trezor_tests) +set_property(TARGET trezor_tests + PROPERTY + FOLDER "tests") + +add_test( + NAME trezor_tests + COMMAND trezor_tests) diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp new file mode 100644 index 000000000..c2b46f698 --- /dev/null +++ b/tests/trezor/trezor_tests.cpp @@ -0,0 +1,1409 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "include_base_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_core/cryptonote_tx_utils.h" +#include "misc_language.h" +#include "string_tools.h" + +using namespace cryptonote; + +#include <boost/regex.hpp> +#include "common/util.h" +#include "common/command_line.h" +#include "trezor_tests.h" +#include "device/device_cold.hpp" +#include "device_trezor/device_trezor.hpp" + + +namespace po = boost::program_options; + +namespace +{ + const command_line::arg_descriptor<std::string> arg_filter = { "filter", "Regular expression filter for which tests to run" }; + const command_line::arg_descriptor<bool> arg_generate_and_play_test_data = {"generate_and_play_test_data", ""}; + const command_line::arg_descriptor<std::string> arg_trezor_path = {"trezor_path", "Path to the trezor device to use, has to support debug link", ""}; + const command_line::arg_descriptor<bool> arg_heavy_tests = {"heavy_tests", "Runs expensive tests (volume tests with real device)", false}; + const command_line::arg_descriptor<std::string> arg_chain_path = {"chain_path", "Path to the serialized blockchain, speeds up testing", ""}; + const command_line::arg_descriptor<bool> arg_fix_chain = {"fix_chain", "If chain_patch is given and file cannot be used, it is ignored and overwriten", false}; +} + + +#define TREZOR_ACCOUNT_ORDERING &m_miner_account, &m_alice_account, &m_bob_account, &m_eve_account +#define TREZOR_COMMON_TEST_CASE(genclass, CORE, BASE) \ + rollback_chain(CORE, BASE.head_block()); \ + { \ + genclass ctest; \ + BASE.fork(ctest); \ + GENERATE_AND_PLAY_INSTANCE(genclass, ctest, *(CORE)); \ + } + +#define TREZOR_SETUP_CHAIN(NAME) do { \ + ++tests_count; \ + try { \ + setup_chain(&core, trezor_base, chain_path, fix_chain); \ + } catch (const std::exception& ex) { \ + failed_tests.emplace_back("gen_trezor_base " #NAME); \ + } \ +} while(0) + +static void rollback_chain(cryptonote::core * core, const cryptonote::block & head); +static void setup_chain(cryptonote::core ** core, gen_trezor_base & trezor_base, std::string chain_path, bool fix_chain); + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + tools::on_startup(); + epee::string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + mlog_configure(mlog_get_default_log_path("trezor_tests.log"), true); + mlog_set_log_level(2); + + po::options_description desc_options("Allowed options"); + command_line::add_arg(desc_options, command_line::arg_help); + command_line::add_arg(desc_options, arg_filter); + command_line::add_arg(desc_options, arg_trezor_path); + command_line::add_arg(desc_options, arg_heavy_tests); + command_line::add_arg(desc_options, arg_chain_path); + command_line::add_arg(desc_options, arg_fix_chain); + + 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; + } + + const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); + boost::smatch match; + + size_t tests_count = 0; + std::vector<std::string> failed_tests; + std::string trezor_path = command_line::get_arg(vm, arg_trezor_path); + std::string chain_path = command_line::get_arg(vm, arg_chain_path); + const bool heavy_tests = command_line::get_arg(vm, arg_heavy_tests); + const bool fix_chain = command_line::get_arg(vm, arg_fix_chain); + + hw::trezor::register_all(); + + // Bootstrapping common chain & accounts + cryptonote::core * core = nullptr; + + gen_trezor_base trezor_base; + trezor_base.setup_args(trezor_path, heavy_tests); + trezor_base.rct_config({rct::RangeProofPaddedBulletproof, 1}); // HF9 tests + + TREZOR_SETUP_CHAIN("HF9"); + + // Individual test cases using shared pre-generated blockchain. + TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync, core, trezor_base); + + // Transaction tests + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_short, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_short_integrated, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_long, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_acc1, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_2sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_1norm_2sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_2utxo_sub_acc_to_1norm_2sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_7outs, core, trezor_base); + + if (trezor_base.heavy_tests()) + { + TREZOR_COMMON_TEST_CASE(gen_trezor_many_utxo, core, trezor_base); + } + + core->deinit(); + el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error); + MLOG(level, "\nREPORT:"); + MLOG(level, " Test run: " << tests_count); + MLOG(level, " Failures: " << failed_tests.size()); + if (!failed_tests.empty()) + { + MLOG(level, "FAILED TESTS:"); + BOOST_FOREACH(auto test_name, failed_tests) + { + MLOG(level, " " << test_name); + } + } + + return failed_tests.empty() ? 0 : 1; + + CATCH_ENTRY_L0("main", 1); +} + +static void rollback_chain(cryptonote::core * core, const cryptonote::block & head) +{ + CHECK_AND_ASSERT_THROW_MES(core, "Core is null"); + + block popped_block; + std::vector<transaction> popped_txs; + + crypto::hash head_hash = get_block_hash(head), cur_hash{}; + uint64_t height = get_block_height(head), cur_height=0; + + do { + core->get_blockchain_top(cur_height, cur_hash); + + if (cur_height <= height && head_hash == cur_hash) + return; + + CHECK_AND_ASSERT_THROW_MES(cur_height > height, "Height differs"); + core->get_blockchain_storage().get_db().pop_block(popped_block, popped_txs); + } while(true); +} + +static bool unserialize_chain_from_file(std::vector<test_event_entry>& events, gen_trezor_base &test_base, const std::string& file_path) +{ + TRY_ENTRY(); + std::ifstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::in); + if(data_file.fail()) + return false; + try + { + boost::archive::portable_binary_iarchive a(data_file); + test_base.clear(); + + a >> events; + a >> test_base; + return true; + } + catch(...) + { + MWARNING("Chain deserialization failed"); + return false; + } + CATCH_ENTRY_L0("unserialize_chain_from_file", false); +} + +static bool serialize_chain_to_file(std::vector<test_event_entry>& events, gen_trezor_base &test_base, const std::string& file_path) +{ + TRY_ENTRY(); + std::ofstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if(data_file.fail()) + return false; + try + { + + boost::archive::portable_binary_oarchive a(data_file); + a << events; + a << test_base; + return !data_file.fail(); + } + catch(...) + { + MWARNING("Chain deserialization failed"); + return false; + } + return false; + CATCH_ENTRY_L0("serialize_chain_to_file", false); +} + +static void setup_chain(cryptonote::core ** core, gen_trezor_base & trezor_base, std::string chain_path, bool fix_chain) +{ + std::vector<test_event_entry> events; + const bool do_serialize = !chain_path.empty(); + const bool chain_file_exists = do_serialize && boost::filesystem::exists(chain_path); + bool loaded = false; + bool generated = false; + + if (chain_file_exists) + { + if (!unserialize_chain_from_file(events, trezor_base, chain_path)) + { + MERROR("Failed to deserialize data from file: " << chain_path); + if (!fix_chain) + { + throw std::runtime_error("Chain load error"); + } + } else + { + trezor_base.load(events); + generated = true; + loaded = true; + } + } + + if (!generated) + { + try + { + generated = trezor_base.generate(events); + + if (generated && !loaded && do_serialize) + { + trezor_base.update_trackers(events); + if (!serialize_chain_to_file(events, trezor_base, chain_path)) + { + MERROR("Failed to serialize data to file: " << chain_path); + } + } + } + CATCH_REPLAY(gen_trezor_base); + } + + trezor_base.fix_hf(events); + if (generated && do_replay_events_get_core<gen_trezor_base>(events, core)) + { + MGINFO_GREEN("#TEST-chain-init# Succeeded "); + } + else + { + MERROR("#TEST-chain-init# Failed "); + throw std::runtime_error("Chain init error"); + } +} + +static void add_hforks(std::vector<test_event_entry>& events, const v_hardforks_t& hard_forks) +{ + event_replay_settings repl_set; + repl_set.hard_forks = boost::make_optional(hard_forks); + events.push_back(repl_set); +} + +static void add_top_hfork(std::vector<test_event_entry>& events, const v_hardforks_t& hard_forks) +{ + event_replay_settings repl_set; + v_hardforks_t top_fork; + top_fork.push_back(hard_forks.back()); + repl_set.hard_forks = boost::make_optional(top_fork); + events.push_back(repl_set); +} + +static crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) +{ + std::vector<tx_extra_field> tx_extra_fields; + parse_tx_extra(td.m_tx.extra, tx_extra_fields); + + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0), tools::error::wallet_internal_error, + "Public key wasn't found in the transaction extra"); + const crypto::public_key tx_pub_key = pub_key_field.pub_key; + bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); + if (!two_found) { + return tx_pub_key; + } else { + throw std::runtime_error("Unsupported tx pub resolution"); + } +} + +static void setup_shim(hw::wallet_shim * shim) +{ + shim->get_tx_pub_key_from_received_outs = &get_tx_pub_key_from_received_outs; +} + +bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev) +{ + std::vector<tx_extra_field> tx_extra_fields; + parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed + cryptonote::tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + if (ptx.dests.empty()) + { + MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); + return false; + } + return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); + } + } + return false; +} + +static tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) +{ + tools::wallet2::tx_construction_data construction_data = ptx.construction_data; + crypto::hash8 payment_id = crypto::null_hash8; + if (get_short_payment_id(payment_id, ptx, hwdev)) + { + // Remove encrypted + remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce)); + // Add decrypted + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(construction_data.extra, extra_nonce), + tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); + MDEBUG("Decrypted payment ID: " << payment_id); + } + return construction_data; +} + +static std::string get_payment_id(const std::vector<uint8_t> &tx_extra) +{ + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + cryptonote::parse_tx_extra(tx_extra, tx_extra_fields); // ok if partially parsed + cryptonote::tx_extra_nonce extra_nonce; + + ::crypto::hash payment_id{}; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + ::crypto::hash8 payment_id8{}; + if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + return std::string(payment_id8.data, 8); + } + else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + return std::string(payment_id.data, 32); + } + } + return std::string(); +} + +static crypto::hash8 to_short_payment_id(const std::string & payment_id) +{ + crypto::hash8 payment_id_short; + CHECK_AND_ASSERT_THROW_MES(payment_id.size() == 8, "Invalid argument"); + memcpy(payment_id_short.data, payment_id.data(), 8); + return payment_id_short; +} + +static crypto::hash to_long_payment_id(const std::string & payment_id) +{ + crypto::hash payment_id_long; + CHECK_AND_ASSERT_THROW_MES(payment_id.size() == 32, "Invalid argument"); + memcpy(payment_id_long.data, payment_id.data(), 32); + return payment_id_long; +} + +static std::vector<uint8_t> build_payment_id_extra(const std::string & payment_id) +{ + std::vector<uint8_t> res; + + if (payment_id.size() == 8) { + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, to_short_payment_id(payment_id)); + THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(res, extra_nonce), + tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); + + } else if (payment_id.size() == 32){ + std::string extra_nonce; + set_payment_id_to_tx_extra_nonce(extra_nonce, to_long_payment_id(payment_id)); + THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(res, extra_nonce), + tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); + } + + return res; +} + +static cryptonote::address_parse_info init_addr_parse_info(cryptonote::account_public_address &addr, bool is_sub=false, boost::optional<crypto::hash8> payment_id = boost::none) +{ + cryptonote::address_parse_info res; + res.address = addr; + res.is_subaddress = is_sub; + if (payment_id){ + res.has_payment_id = true; + res.payment_id = payment_id.get(); + } else { + res.has_payment_id = false; + } + return res; +} + +static void expand_tsx(cryptonote::transaction &tx) +{ + auto & rv = tx.rct_signatures; + if (rv.type == rct::RCTTypeFull) + { + rv.p.MGs.resize(1); + rv.p.MGs[0].II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + } + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) + { + CHECK_AND_ASSERT_THROW_MES(rv.p.MGs.size() == tx.vin.size(), "Bad MGs size"); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + rv.p.MGs[n].II.resize(1); + rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + } + } +} + +static std::vector<tools::wallet2*> vct_wallets(tools::wallet2* w1=nullptr, tools::wallet2* w2=nullptr, tools::wallet2* w3=nullptr, tools::wallet2* w4=nullptr, tools::wallet2* w5=nullptr) +{ + std::vector<tools::wallet2*> res; + if (w1) + res.push_back(w1); + if (w2) + res.push_back(w2); + if (w3) + res.push_back(w3); + if (w4) + res.push_back(w4); + if (w5) + res.push_back(w5); + return res; +} + +// gen_trezor_base +const uint64_t gen_trezor_base::m_ts_start = 1338224400; +const uint64_t gen_trezor_base::m_wallet_ts = m_ts_start - 60*60*24*4; +const std::string gen_trezor_base::m_device_name = "Trezor:udp"; +const std::string gen_trezor_base::m_master_seed_str = "14821d0bc5659b24cafbc889dc4fc60785ee08b65d71c525f81eeaba4f3a570f"; +const std::string gen_trezor_base::m_device_seed = "permit universe parent weapon amused modify essay borrow tobacco budget walnut lunch consider gallery ride amazing frog forget treat market chapter velvet useless topple"; +const std::string gen_trezor_base::m_alice_spend_private = m_master_seed_str; +const std::string gen_trezor_base::m_alice_view_private = "a6ccd4ac344a295d1387f8d18c81bdd394f1845de84188e204514ef9370fd403"; + +gen_trezor_base::gen_trezor_base(){ + m_rct_config = {rct::RangeProofPaddedBulletproof, 1}; +} + +gen_trezor_base::gen_trezor_base(const gen_trezor_base &other): + m_generator(other.m_generator), m_bt(other.m_bt), m_miner_account(other.m_miner_account), + m_bob_account(other.m_bob_account), m_alice_account(other.m_alice_account), m_eve_account(other.m_eve_account), + m_hard_forks(other.m_hard_forks), m_trezor(other.m_trezor), m_rct_config(other.m_rct_config) +{ + +} + +void gen_trezor_base::setup_args(const std::string & trezor_path, bool heavy_tests) +{ + m_trezor_path = trezor_path.empty() ? m_device_name : std::string("Trezor:") + trezor_path; + m_heavy_tests = heavy_tests; +} + +void gen_trezor_base::setup_trezor() +{ + hw::device &hwdev = hw::get_device(m_trezor_path); + m_trezor = dynamic_cast<hw::trezor::device_trezor *>(&hwdev); + CHECK_AND_ASSERT_THROW_MES(m_trezor, "Dynamic cast failed"); + + m_trezor->set_debug(true); // debugging commands on Trezor (auto-confirm transactions) + + CHECK_AND_ASSERT_THROW_MES(m_trezor->set_name(m_trezor_path), "Could not set device name " << m_trezor_path); + m_trezor->set_network_type(MAINNET); + m_trezor->set_derivation_path(""); // empty derivation path + + CHECK_AND_ASSERT_THROW_MES(m_trezor->init(), "Could not initialize the device " << m_trezor_path); + CHECK_AND_ASSERT_THROW_MES(m_trezor->connect(), "Could not connect to the device " << m_trezor_path); + m_trezor->wipe_device(); + m_trezor->load_device(m_device_seed); + m_trezor->release(); + m_trezor->disconnect(); +} + +void gen_trezor_base::fork(gen_trezor_base & other) +{ + other.m_generator = m_generator; + other.m_bt = m_bt; + other.m_events = m_events; + other.m_head = m_head; + other.m_hard_forks = m_hard_forks; + other.m_trezor_path = m_trezor_path; + other.m_heavy_tests = m_heavy_tests; + other.m_rct_config = m_rct_config; + + other.m_miner_account = m_miner_account; + other.m_bob_account = m_bob_account; + other.m_alice_account = m_alice_account; + other.m_eve_account = m_eve_account; + other.m_trezor = m_trezor; +} + +void gen_trezor_base::clear() +{ + m_generator = test_generator(); + m_bt = block_tracker(); + m_events.clear(); + m_hard_forks.clear(); + m_trezor = nullptr; +} + +void gen_trezor_base::add_shared_events(std::vector<test_event_entry>& events) +{ + events.reserve(m_events.size()); + for(const test_event_entry & c : m_events){ + events.push_back(c); + } +} + +void gen_trezor_base::init_fields() +{ + m_miner_account.generate(); + DEFAULT_HARDFORKS(m_hard_forks); + + crypto::secret_key master_seed{}; + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_master_seed_str, master_seed), "Hexdecode fails"); + + m_alice_account.generate(master_seed, true); + m_alice_account.set_createtime(m_wallet_ts); +} + +bool gen_trezor_base::generate(std::vector<test_event_entry>& events) +{ + init_fields(); + setup_trezor(); + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + + // Events, custom genesis so it matches wallet genesis + auto & generator = m_generator; // macro shortcut + + cryptonote::block blk_gen; + std::vector<size_t> block_weights; + generate_genesis_block(blk_gen, get_config(MAINNET).GENESIS_TX, get_config(MAINNET).GENESIS_NONCE); + events.push_back(blk_gen); + generator.add_block(blk_gen, 0, block_weights, 0); + + // First event has to be the genesis block + m_bob_account.generate(); + m_eve_account.generate(); + m_bob_account.set_createtime(m_wallet_ts); + m_eve_account.set_createtime(m_wallet_ts); + cryptonote::account_base * accounts[] = {TREZOR_ACCOUNT_ORDERING}; + for(cryptonote::account_base * ac : accounts){ + events.push_back(*ac); + } + + // Another block with predefined timestamp. + // Carefully set reward and already generated coins so it passes miner_tx check. + cryptonote::block blk_0; + { + std::list<cryptonote::transaction> tx_list; + const crypto::hash prev_id = get_block_hash(blk_gen); + const uint64_t already_generated_coins = generator.get_already_generated_coins(prev_id); + block_weights.clear(); + generator.get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + generator.construct_block(blk_0, 1, prev_id, m_miner_account, m_ts_start, already_generated_coins, block_weights, tx_list); + } + + events.push_back(blk_0); + MDEBUG("Gen+1 block has time: " << blk_0.timestamp << " blid: " << get_block_hash(blk_0)); + + // Generate some spendable funds on the Miner account + REWIND_BLOCKS_N(events, blk_3, blk_0, m_miner_account, 40); + + // Rewind so the miners funds are unlocked for initial transactions. + REWIND_BLOCKS(events, blk_3r, blk_3, m_miner_account); + + // Non-rct transactions Miner -> Bob + MAKE_TX_LIST_START(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(10), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(7), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(7), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(14), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(20), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(2), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(2), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(5), blk_3); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, m_miner_account, txs_blk_4); + REWIND_BLOCKS(events, blk_4r, blk_4, m_miner_account); // rewind to unlock + + // Hard fork to bulletproofs version, v9. + const uint8_t CUR_HF = 9; + auto hardfork_height = num_blocks(events); // next block is v9 + ADD_HARDFORK(m_hard_forks, CUR_HF, hardfork_height); + add_hforks(events, m_hard_forks); + MDEBUG("Hardfork height: " << hardfork_height << " at block: " << get_block_hash(blk_4r)); + + // RCT transactions, wallets have to be used, wallet init + m_wl_alice.reset(new tools::wallet2(MAINNET, 1, true)); + m_wl_bob.reset(new tools::wallet2(MAINNET, 1, true)); + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_bob.get(), m_bob_account); + + auto addr_alice_sub_0_1 = m_wl_alice->get_subaddress({0, 1}); + auto addr_alice_sub_0_2 = m_wl_alice->get_subaddress({0, 2}); + auto addr_alice_sub_0_3 = m_wl_alice->get_subaddress({0, 3}); + auto addr_alice_sub_0_4 = m_wl_alice->get_subaddress({0, 4}); + auto addr_alice_sub_0_5 = m_wl_alice->get_subaddress({0, 5}); + auto addr_alice_sub_1_0 = m_wl_alice->get_subaddress({1, 0}); + auto addr_alice_sub_1_1 = m_wl_alice->get_subaddress({1, 1}); + auto addr_alice_sub_1_2 = m_wl_alice->get_subaddress({1, 2}); + + // Miner -> Bob, RCT funds + MAKE_TX_LIST_START_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(5), 10, blk_4); + + const size_t target_rct = m_heavy_tests ? 105 : 15; + for(size_t i = 0; i < target_rct; ++i) + { + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(1) >> 2, 10, blk_4); + } + + // Sub-address destinations + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_1, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_2, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_3, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_4, true, MK_COINS(1) >> 1), 10, blk_4); + + // Sub-address destinations + multi out to force use of additional keys + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_0_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_0_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{m_miner_account, false, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{m_miner_account, false, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + + // Transfer to other accounts + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_1_0, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_1_1, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_1_0, true, MK_COINS(1) >> 1}, {addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_1_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_5, true, MK_COINS(1) >> 1}}), 10, blk_4); + + // Simple RCT transactions + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(7), 10, blk_4); + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(1), 10, blk_4); + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(3), 10, blk_4); + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(4), 10, blk_4); + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_5, blk_4r, m_miner_account, txs_blk_5, CUR_HF); + + // Simple transaction check + bool resx = rct::verRctSemanticsSimple(txs_blk_5.begin()->rct_signatures); + bool resy = rct::verRctNonSemanticsSimple(txs_blk_5.begin()->rct_signatures); + CHECK_AND_ASSERT_THROW_MES(resx, "Tsx5[0] semantics failed"); + CHECK_AND_ASSERT_THROW_MES(resy, "Tsx5[0] non-semantics failed"); + + REWIND_BLOCKS_HF(events, blk_5r, blk_5, m_miner_account, CUR_HF); // rewind to unlock + + // RCT transactions, wallets have to be used + wallet_tools::process_transactions(m_wl_alice.get(), events, blk_5r, m_bt); + wallet_tools::process_transactions(m_wl_bob.get(), events, blk_5r, m_bt); + + // Send Alice -> Bob, manually constructed. Simple TX test, precondition. + cryptonote::transaction tx_1; + std::vector<size_t> selected_transfers; + std::vector<tx_source_entry> sources; + bool res = wallet_tools::fill_tx_sources(m_wl_alice.get(), sources, TREZOR_TEST_MIXIN, boost::none, MK_COINS(2), m_bt, selected_transfers, num_blocks(events) - 1, 0, 1); + CHECK_AND_ASSERT_THROW_MES(res, "TX Fill sources failed"); + + construct_tx_to_key(tx_1, m_wl_alice.get(), m_bob_account, MK_COINS(1), sources, TREZOR_TEST_FEE, true, rct::RangeProofPaddedBulletproof, 1); + events.push_back(tx_1); + MAKE_NEXT_BLOCK_TX1_HF(events, blk_6, blk_5r, m_miner_account, tx_1, CUR_HF); + MDEBUG("Post 1st tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_6)); + + // Simple transaction check + resx = rct::verRctSemanticsSimple(tx_1.rct_signatures); + resy = rct::verRctNonSemanticsSimple(tx_1.rct_signatures); + CHECK_AND_ASSERT_THROW_MES(resx, "tx_1 semantics failed"); + CHECK_AND_ASSERT_THROW_MES(resy, "tx_1 non-semantics failed"); + + REWIND_BLOCKS_HF(events, blk_6r, blk_6, m_miner_account, CUR_HF); + m_head = blk_6r; + m_events = events; + return true; +} + +void gen_trezor_base::load(std::vector<test_event_entry>& events) +{ + init_fields(); + m_events = events; + + unsigned acc_idx = 0; + cryptonote::account_base * accounts[] = {TREZOR_ACCOUNT_ORDERING}; + unsigned accounts_num = (sizeof(accounts) / sizeof(accounts[0])); + + for(auto & ev : events) + { + if (typeid(cryptonote::block) == ev.type()) + { + m_head = boost::get<cryptonote::block>(ev); + } + else if (typeid(cryptonote::account_base) == ev.type()) // accounts + { + const auto & acc = boost::get<cryptonote::account_base>(ev); + if (acc_idx < accounts_num) + { + *accounts[acc_idx++] = acc; + } + } + else if (typeid(event_replay_settings) == ev.type()) // hard forks + { + const auto & rep_settings = boost::get<event_replay_settings>(ev); + if (rep_settings.hard_forks) + { + const auto & hf = rep_settings.hard_forks.get(); + std::copy(hf.begin(), hf.end(), std::back_inserter(m_hard_forks)); + } + } + } + + // Setup wallets, synchronize blocks + m_bob_account.set_createtime(m_wallet_ts); + m_eve_account.set_createtime(m_wallet_ts); + + setup_trezor(); + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + + m_wl_alice.reset(new tools::wallet2(MAINNET, 1, true)); + m_wl_bob.reset(new tools::wallet2(MAINNET, 1, true)); + m_wl_eve.reset(new tools::wallet2(MAINNET, 1, true)); + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_bob.get(), m_bob_account); + wallet_accessor_test::set_account(m_wl_eve.get(), m_eve_account); + + wallet_tools::process_transactions(m_wl_alice.get(), events, m_head, m_bt); + wallet_tools::process_transactions(m_wl_bob.get(), events, m_head, m_bt); +} + +void gen_trezor_base::fix_hf(std::vector<test_event_entry>& events) +{ + // If current test requires higher hard-fork, move it up + const auto current_hf = m_hard_forks.back().first; + if (m_rct_config.bp_version == 2 && current_hf < 10){ + auto hardfork_height = num_blocks(events); + ADD_HARDFORK(m_hard_forks, 10, hardfork_height); + add_top_hfork(events, m_hard_forks); + MDEBUG("Hardfork height: " << hardfork_height); + } +} + +void gen_trezor_base::update_trackers(std::vector<test_event_entry>& events) +{ + wallet_tools::process_transactions(nullptr, events, m_head, m_bt); +} + +void gen_trezor_base::test_setup(std::vector<test_event_entry>& events) +{ + add_shared_events(events); + + setup_trezor(); + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + + m_wl_alice.reset(new tools::wallet2(MAINNET, 1, true)); + m_wl_bob.reset(new tools::wallet2(MAINNET, 1, true)); + m_wl_eve.reset(new tools::wallet2(MAINNET, 1, true)); + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_bob.get(), m_bob_account); + wallet_accessor_test::set_account(m_wl_eve.get(), m_eve_account); + wallet_tools::process_transactions(m_wl_alice.get(), events, m_head, m_bt); + wallet_tools::process_transactions(m_wl_bob.get(), events, m_head, m_bt); + wallet_tools::process_transactions(m_wl_eve.get(), events, m_head, m_bt); +} + +void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std::vector<tools::wallet2::pending_tx>& ptxs, std::vector<cryptonote::address_parse_info>& dsts_info, test_generator &generator, std::vector<tools::wallet2*> wallets, bool is_sweep) +{ + // Construct pending transaction for signature in the Trezor. + const uint64_t height_pre = num_blocks(events) - 1; + cryptonote::block head_block = get_head_block(events); + const crypto::hash head_hash = get_block_hash(head_block); + + // If current test requires higher hard-fork, move it up + const auto current_hf = m_hard_forks.back().first; + const uint8_t tx_hf = m_rct_config.bp_version == 2 ? 10 : 9; + if (tx_hf > current_hf){ + throw std::runtime_error("Too late for HF change"); + } + + tools::wallet2::unsigned_tx_set txs; + std::list<cryptonote::transaction> tx_list; + + for(auto &ptx : ptxs) { + txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(ptx, *m_trezor)); + } + txs.transfers = std::make_pair(0, wallet_accessor_test::get_transfers(m_wl_alice.get())); + + auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + tools::wallet2::signed_tx_set exported_txs; + hw::tx_aux_data aux_data; + hw::wallet_shim wallet_shim; + setup_shim(&wallet_shim); + aux_data.tx_recipients = dsts_info; + dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); + + MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions"); + CHECK_AND_ASSERT_THROW_MES(exported_txs.ptx.size() == ptxs.size(), "Invalid transaction sizes"); + + for (size_t i = 0; i < exported_txs.ptx.size(); ++i){ + auto &c_ptx = exported_txs.ptx[i]; + c_ptx.tx.rct_signatures.mixRing = ptxs[i].tx.rct_signatures.mixRing; + expand_tsx(c_ptx.tx); + + // Simple TX tests, more complex are performed in the core. + MTRACE(cryptonote::obj_to_json_str(c_ptx.tx)); + bool resx = rct::verRctSemanticsSimple(c_ptx.tx.rct_signatures); + bool resy = rct::verRctNonSemanticsSimple(c_ptx.tx.rct_signatures); + CHECK_AND_ASSERT_THROW_MES(resx, "Trezor tx_1 semantics failed"); + CHECK_AND_ASSERT_THROW_MES(resy, "Trezor tx_1 Nonsemantics failed"); + + events.push_back(c_ptx.tx); + tx_list.push_back(c_ptx.tx); + MDEBUG("Transaction: " << dump_data(c_ptx.tx)); + } + + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_7, m_head, m_miner_account, tx_list, tx_hf); + MDEBUG("Trezor tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_7)); + + // TX receive test + uint64_t sum_in = 0; + uint64_t sum_out = 0; + + for(size_t txid = 0; txid < exported_txs.ptx.size(); ++txid) { + auto &c_ptx = exported_txs.ptx[txid]; + auto &c_tx = c_ptx.tx; + const crypto::hash txhash = cryptonote::get_transaction_hash(c_tx); + const size_t num_outs = c_tx.vout.size(); + size_t num_received = 0; + uint64_t cur_sum_in = 0; + uint64_t cur_sum_out = 0; + uint64_t cur_sum_out_recv = 0; + std::unordered_set<size_t> recv_out_idx; + std::string exp_payment_id = get_payment_id(c_ptx.construction_data.extra); + std::string enc_payment_id = get_payment_id(c_tx.extra); + size_t num_payment_id_checks_done = 0; + + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.empty() || exp_payment_id.size() == 8 || exp_payment_id.size() == 32, "Required payment ID invalid"); + CHECK_AND_ASSERT_THROW_MES((exp_payment_id.size() == 32) == (enc_payment_id.size() == 32), "Required and built payment ID size mismatch"); + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.size() <= enc_payment_id.size(), "Required and built payment ID size mismatch"); + + for(auto &src : c_ptx.construction_data.sources){ + cur_sum_in += src.amount; + } + + for(auto &dst : c_ptx.construction_data.splitted_dsts){ + cur_sum_out += dst.amount; + } + + CHECK_AND_ASSERT_THROW_MES(c_tx.rct_signatures.txnFee + cur_sum_out == cur_sum_in, "Tx Input Output amount mismatch"); + + for (size_t widx = 0; widx < wallets.size(); ++widx) { + const bool sender = widx == 0; + tools::wallet2 *wl = wallets[widx]; + + wallet_tools::process_transactions(wl, events, blk_7, m_bt, boost::make_optional(head_hash)); + + tools::wallet2::transfer_container m_trans; + tools::wallet2::transfer_container m_trans_txid; + wl->get_transfers(m_trans); + + std::copy_if(m_trans.begin(), m_trans.end(), std::back_inserter(m_trans_txid), [&txhash](const tools::wallet2::transfer_details& item) { + return item.m_txid == txhash; + }); + + // Testing if the transaction output has been received + num_received += m_trans_txid.size(); + for (auto & ctran : m_trans_txid){ + cur_sum_out_recv += ctran.amount(); + recv_out_idx.insert(ctran.m_internal_output_index); + CHECK_AND_ASSERT_THROW_MES(!ctran.m_spent, "Txout is spent"); + CHECK_AND_ASSERT_THROW_MES(!sender || ctran.m_key_image_known, "Key Image unknown for recipient"); // sender is Trezor, does not need to have KI + } + + // Sender output payment (contains change and stuff) + if (sender) { + std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_transfers; // txid -> tdetail + std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_transfers_txid; // txid -> tdetail + wl->get_payments_out(confirmed_transfers, height_pre); + + std::copy_if(confirmed_transfers.begin(), confirmed_transfers.end(), std::back_inserter(confirmed_transfers_txid), [&txhash](const std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>& item) { + return item.first == txhash; + }); + + CHECK_AND_ASSERT_THROW_MES(confirmed_transfers_txid.size() == 1, "Sender does not have outgoing transfer for the transaction"); + } + + // Received payment from the block + std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; // payment id -> [payment details] multimap + std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments_txid; // payment id -> [payment details] multimap + wl->get_payments(payments, height_pre); + + std::copy_if(payments.begin(), payments.end(), std::back_inserter(payments_txid), [&txhash](const std::pair<crypto::hash, tools::wallet2::payment_details>& item) { + return item.second.m_tx_hash == txhash; + }); + + for(auto &paydet : payments_txid){ + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.empty() || (memcmp(exp_payment_id.data(), paydet.first.data, exp_payment_id.size()) == 0), "Payment ID mismatch"); + num_payment_id_checks_done += 1; + } + } + + CHECK_AND_ASSERT_THROW_MES(c_tx.rct_signatures.txnFee + cur_sum_out_recv == cur_sum_in, "Tx Input Output amount mismatch"); + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.empty() || num_payment_id_checks_done > 0, "No Payment ID checks"); + + if(!is_sweep){ + CHECK_AND_ASSERT_THROW_MES(num_received == num_outs, "Number of received outputs do not match number of outgoing"); + CHECK_AND_ASSERT_THROW_MES(recv_out_idx.size() == num_outs, "Num of outs received do not match"); + } else { + CHECK_AND_ASSERT_THROW_MES(num_received + 1 >= num_outs, "Number of received outputs do not match number of outgoing"); + CHECK_AND_ASSERT_THROW_MES(recv_out_idx.size() + 1 >= num_outs, "Num of outs received do not match"); // can have dummy out + } + + sum_in += cur_sum_in; + sum_out += cur_sum_out + c_tx.rct_signatures.txnFee; + } + + CHECK_AND_ASSERT_THROW_MES(sum_in == sum_out, "Tx amount mismatch"); +} + +#define TREZOR_TEST_PREFIX() \ + test_generator generator(m_generator); \ + test_setup(events); \ + tsx_builder t_builder_o(this); \ + tsx_builder * t_builder = &t_builder_o + +#define TREZOR_TEST_SUFFIX() \ + auto _dsts = t_builder->build(); \ + auto _dsts_info = t_builder->dest_info(); \ + test_trezor_tx(events, _dsts, _dsts_info, generator, vct_wallets(m_wl_alice.get(), m_wl_bob.get(), m_wl_eve.get())); \ + return true + +#define TREZOR_SKIP_IF_VERSION_LEQ(x) if (m_trezor->get_version() <= x) { MDEBUG("Test skipped"); return true; } +#define TREZOR_TEST_PAYMENT_ID "\xde\xad\xc0\xde\xde\xad\xc0\xde" +#define TREZOR_TEST_PAYMENT_ID_LONG "\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde" + +tsx_builder * tsx_builder::sources(std::vector<cryptonote::tx_source_entry> & sources, std::vector<size_t> & selected_transfers) +{ + m_sources = sources; + m_selected_transfers = selected_transfers; + return this; +} + +tsx_builder * tsx_builder::compute_sources(boost::optional<size_t> num_utxo, boost::optional<uint64_t> min_amount, ssize_t offset, int step, boost::optional<fnc_accept_tx_source_t> fnc_accept) +{ + CHECK_AND_ASSERT_THROW_MES(m_tester, "m_tester wallet empty"); + CHECK_AND_ASSERT_THROW_MES(m_from, "m_from wallet empty"); + + // typedef std::function<bool(const tx_source_info_crate_t &info, bool &abort)> fnc_accept_tx_source_t; + boost::optional<fnc_accept_tx_source_t> fnc_accept_to_use = boost::none; + + auto c_account = m_account; + fnc_accept_tx_source_t fnc_acc = [c_account, &fnc_accept] (const tx_source_info_crate_t &info, bool &abort) -> bool { + if (info.td->m_subaddr_index.major != c_account){ + return false; + } + if (fnc_accept){ + return (fnc_accept.get())(info, abort); + } + return true; + }; + + fnc_accept_to_use = fnc_acc; + bool res = wallet_tools::fill_tx_sources(m_from, m_sources, m_mixin, num_utxo, min_amount, m_tester->m_bt, m_selected_transfers, m_cur_height, offset, step, fnc_accept_to_use); + CHECK_AND_ASSERT_THROW_MES(res, "Tx source fill error"); + return this; +} + +tsx_builder * tsx_builder::compute_sources_to_sub(boost::optional<size_t> num_utxo, boost::optional<uint64_t> min_amount, ssize_t offset, int step, boost::optional<fnc_accept_tx_source_t> fnc_accept) +{ + fnc_accept_tx_source_t fnc = [&fnc_accept] (const tx_source_info_crate_t &info, bool &abort) -> bool { + if (info.td->m_subaddr_index.minor == 0){ + return false; + } + if (fnc_accept){ + return (fnc_accept.get())(info, abort); + } + return true; + }; + + return compute_sources(num_utxo, min_amount, offset, step, fnc); +} + +tsx_builder * tsx_builder::compute_sources_to_sub_acc(boost::optional<size_t> num_utxo, boost::optional<uint64_t> min_amount, ssize_t offset, int step, boost::optional<fnc_accept_tx_source_t> fnc_accept) +{ + fnc_accept_tx_source_t fnc = [&fnc_accept] (const tx_source_info_crate_t &info, bool &abort) -> bool { + if (info.td->m_subaddr_index.minor == 0 || info.src->real_out_additional_tx_keys.size() == 0){ + return false; + } + if (fnc_accept){ + return (fnc_accept.get())(info, abort); + } + return true; + }; + + return compute_sources(num_utxo, min_amount, offset, step, fnc); +} + +tsx_builder * tsx_builder::destinations(std::vector<cryptonote::tx_destination_entry> &dsts) +{ + m_destinations_orig = dsts; + return this; +} + +tsx_builder * tsx_builder::add_destination(const cryptonote::tx_destination_entry &dst) +{ + m_destinations_orig.push_back(dst); + return this; +} + +tsx_builder * tsx_builder::add_destination(const var_addr_t addr, bool is_subaddr, uint64_t amount) +{ + m_destinations_orig.push_back(build_dst(addr, is_subaddr, amount)); + return this; +} + +tsx_builder * tsx_builder::add_destination(const tools::wallet2 * wallet, bool is_subaddr, uint64_t amount) +{ + m_destinations_orig.push_back(build_dst(get_address(wallet), is_subaddr, amount)); + return this; +} + +tsx_builder * tsx_builder::set_integrated(size_t idx) +{ + m_integrated.insert(idx); + return this; +} + +tsx_builder * tsx_builder::clear_current() +{ + m_account = 0; + m_selected_transfers.clear(); + m_sources.clear(); + m_destinations.clear(); + m_destinations_orig.clear(); + m_dsts_info.clear(); + m_integrated.clear(); + m_payment_id.clear(); + return this; +} + +tsx_builder * tsx_builder::build_tx() +{ + CHECK_AND_ASSERT_THROW_MES(m_tester, "m_tester wallet empty"); + CHECK_AND_ASSERT_THROW_MES(m_from, "m_from wallet empty"); + + // Amount sanity check input >= fee + outputs + const uint64_t out_amount = sum_amount(m_destinations_orig); + const uint64_t in_amount = sum_amount(m_sources); + CHECK_AND_ASSERT_THROW_MES(in_amount >= out_amount + m_fee, "Not enough input credits for outputs and fees"); + + // Create new pending transaction, init with sources and destinations + m_ptxs.emplace_back(); + auto & ptx = m_ptxs.back(); + + std::vector<uint8_t> extra = build_payment_id_extra(m_payment_id); + fill_tx_destinations(m_from->get_subaddress({m_account, 0}), m_destinations_orig, m_fee, m_sources, m_destinations, true); + construct_pending_tx(ptx, extra); + + ptx.construction_data.subaddr_account = m_account; + + // Build destinations parse info + for(size_t i = 0; i < m_destinations_orig.size(); ++i){ + auto & cdest = m_destinations_orig[i]; + cryptonote::address_parse_info info = init_addr_parse_info(cdest.addr, cdest.is_subaddress); + if (m_integrated.find(i) != m_integrated.end()){ + CHECK_AND_ASSERT_THROW_MES(m_payment_id.size() == 8, "Integrated set but payment_id.size() != 8"); + info.has_payment_id = true; + info.payment_id = to_short_payment_id(m_payment_id); + } + + m_dsts_info.push_back(info); + } + + return this; +} + +tsx_builder * tsx_builder::construct_pending_tx(tools::wallet2::pending_tx &ptx, boost::optional<std::vector<uint8_t>> extra) +{ + CHECK_AND_ASSERT_THROW_MES(m_from, "Wallet not provided"); + + cryptonote::transaction tx; + subaddresses_t & subaddresses = wallet_accessor_test::get_subaddresses(m_from); + crypto::secret_key tx_key; + std::vector<crypto::secret_key> additional_tx_keys; + std::vector<tx_destination_entry> destinations_copy = m_destinations; + + auto change_addr = m_from->get_account().get_keys().m_account_address; + bool r = construct_tx_and_get_tx_key(m_from->get_account().get_keys(), subaddresses, m_sources, destinations_copy, + change_addr, extra ? extra.get() : std::vector<uint8_t>(), tx, 0, tx_key, + additional_tx_keys, true, m_rct_config, nullptr); + + CHECK_AND_ASSERT_THROW_MES(r, "Transaction construction failed"); + + ptx.key_images = ""; + ptx.fee = TESTS_DEFAULT_FEE; + ptx.dust = 0; + ptx.dust_added_to_fee = false; + ptx.tx = tx; + ptx.change_dts = m_destinations.back(); + ptx.selected_transfers = m_selected_transfers; + ptx.tx_key = tx_key; + ptx.additional_tx_keys = additional_tx_keys; + ptx.dests = m_destinations; + ptx.multisig_sigs.clear(); + ptx.construction_data.sources = m_sources; + ptx.construction_data.change_dts = m_destinations.back(); + ptx.construction_data.splitted_dsts = m_destinations; + ptx.construction_data.selected_transfers = ptx.selected_transfers; + ptx.construction_data.extra = tx.extra; + ptx.construction_data.unlock_time = 0; + ptx.construction_data.use_rct = true; + ptx.construction_data.use_bulletproofs = true; + ptx.construction_data.dests = m_destinations_orig; + + ptx.construction_data.subaddr_account = 0; + ptx.construction_data.subaddr_indices.clear(); + for(uint32_t i = 0; i < 20; ++i) + ptx.construction_data.subaddr_indices.insert(i); + + return this; +} + +std::vector<tools::wallet2::pending_tx> tsx_builder::build() +{ + return m_ptxs; +} + +bool gen_trezor_ki_sync::generate(std::vector<test_event_entry>& events) +{ + test_generator generator(m_generator); + test_setup(events); + + auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + std::vector<std::pair<crypto::key_image, crypto::signature>> ski; + tools::wallet2::transfer_container transfers; + hw::wallet_shim wallet_shim; + setup_shim(&wallet_shim); + m_wl_alice->get_transfers(transfers); + + dev_cold->ki_sync(&wallet_shim, transfers, ski); + CHECK_AND_ASSERT_THROW_MES(ski.size() == transfers.size(), "Size mismatch"); + for(size_t i = 0; i < transfers.size(); ++i) + { + auto & td = transfers[i]; + auto & kip = ski[i]; + CHECK_AND_ASSERT_THROW_MES(!td.m_key_image_known || td.m_key_image == kip.first, "Key Image invalid: " << i); + } + + uint64_t spent = 0, unspent = 0; + m_wl_alice->import_key_images(ski, 0, spent, unspent, false); + return true; +} + +bool gen_trezor_1utxo::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_1utxo_paymentid_short::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + TREZOR_SKIP_IF_VERSION_LEQ(hw::trezor::pack_version(2, 0, 9)); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->payment_id(TREZOR_TEST_PAYMENT_ID) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_1utxo_paymentid_short_integrated::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + TREZOR_SKIP_IF_VERSION_LEQ(hw::trezor::pack_version(2, 0, 9)); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->payment_id(TREZOR_TEST_PAYMENT_ID) + ->set_integrated(0) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_1utxo_paymentid_long::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->payment_id(TREZOR_TEST_PAYMENT_ID_LONG) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_acc1::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 1) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_2sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({1, 3}), true, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_1norm_2sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) + ->add_destination(m_wl_eve.get(), false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_2utxo_sub_acc_to_1norm_2sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources_to_sub_acc(2, MK_COINS(1) >> 2, -1, -1) + ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) + ->add_destination(m_wl_eve.get(), false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_7outs::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 2}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 3}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 4}), true, 1000) + ->add_destination(m_wl_eve.get(), false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_many_utxo::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(110, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + diff --git a/tests/trezor/trezor_tests.h b/tests/trezor/trezor_tests.h new file mode 100644 index 000000000..41db1cce5 --- /dev/null +++ b/tests/trezor/trezor_tests.h @@ -0,0 +1,248 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include <device_trezor/device_trezor.hpp> +#include "../core_tests/chaingen.h" +#include "../core_tests/wallet_tools.h" + +#define TREZOR_TEST_FEE 90000000000 +#define TREZOR_TEST_MIXIN 11 + +/************************************************************************/ +/* */ +/************************************************************************/ +class tsx_builder; +class gen_trezor_base : public test_chain_unit_base +{ +public: + friend class tsx_builder; + + gen_trezor_base(); + gen_trezor_base(const gen_trezor_base &other); + virtual ~gen_trezor_base() {}; + + void setup_args(const std::string & trezor_path, bool heavy_tests=false); + virtual bool generate(std::vector<test_event_entry>& events); + virtual void load(std::vector<test_event_entry>& events); // load events, init test obj + void fix_hf(std::vector<test_event_entry>& events); + void update_trackers(std::vector<test_event_entry>& events); + + void fork(gen_trezor_base & other); // fork generated chain to another test + void clear(); // clears m_events, bt, generator, hforks + void add_shared_events(std::vector<test_event_entry>& events); // m_events -> events + void test_setup(std::vector<test_event_entry>& events); // init setup env, wallets + + void test_trezor_tx(std::vector<test_event_entry>& events, + std::vector<tools::wallet2::pending_tx>& ptxs, + std::vector<cryptonote::address_parse_info>& dsts_info, + test_generator &generator, + std::vector<tools::wallet2*> wallets, + bool is_sweep=false); + + crypto::hash head_hash() { return get_block_hash(m_head); } + cryptonote::block head_block() { return m_head; } + bool heavy_tests() { return m_heavy_tests; } + void rct_config(rct::RCTConfig rct_config) { m_rct_config = rct_config; } + uint8_t cur_hf(){ return m_hard_forks.size() > 0 ? m_hard_forks.back().first : 0; } + + // Static configuration + static const uint64_t m_ts_start; + static const uint64_t m_wallet_ts; + static const std::string m_device_name; + static const std::string m_master_seed_str; + static const std::string m_device_seed; + static const std::string m_alice_spend_private; + static const std::string m_alice_view_private; + +protected: + void setup_trezor(); + void init_fields(); + + test_generator m_generator; + block_tracker m_bt; + + v_hardforks_t m_hard_forks; + cryptonote::block m_head; + std::vector<test_event_entry> m_events; + + std::string m_trezor_path; + bool m_heavy_tests; + rct::RCTConfig m_rct_config; + + cryptonote::account_base m_miner_account; + cryptonote::account_base m_bob_account; + cryptonote::account_base m_alice_account; + cryptonote::account_base m_eve_account; + hw::trezor::device_trezor * m_trezor; + std::unique_ptr<tools::wallet2> m_wl_alice; + std::unique_ptr<tools::wallet2> m_wl_bob; + std::unique_ptr<tools::wallet2> m_wl_eve; + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & m_generator; + } +}; + +class tsx_builder { +public: + tsx_builder(): m_tester(nullptr), m_from(nullptr), m_account(0), m_mixin(TREZOR_TEST_MIXIN), m_fee(TREZOR_TEST_FEE), + m_rct_config({rct::RangeProofPaddedBulletproof, 1 }){} + + tsx_builder(gen_trezor_base * tester): m_tester(tester), m_from(nullptr), m_account(0), + m_mixin(TREZOR_TEST_MIXIN), m_fee(TREZOR_TEST_FEE), + m_rct_config({rct::RangeProofPaddedBulletproof, 1 }){} + + tsx_builder * cur_height(uint64_t cur_height) { m_cur_height = cur_height; return this; } + tsx_builder * mixin(size_t mixin=TREZOR_TEST_MIXIN) { m_mixin = mixin; return this; } + tsx_builder * fee(uint64_t fee=TREZOR_TEST_FEE) { m_fee = fee; return this; } + tsx_builder * payment_id(const std::string & payment_id) { m_payment_id = payment_id; return this; } + tsx_builder * from(tools::wallet2 *from, uint32_t account=0) { m_from = from; m_account=account; return this; } + tsx_builder * sources(std::vector<cryptonote::tx_source_entry> & sources, std::vector<size_t> & selected_transfers); + tsx_builder * compute_sources(boost::optional<size_t> num_utxo=boost::none, boost::optional<uint64_t> min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional<fnc_accept_tx_source_t> fnc_accept=boost::none); + tsx_builder * compute_sources_to_sub(boost::optional<size_t> num_utxo=boost::none, boost::optional<uint64_t> min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional<fnc_accept_tx_source_t> fnc_accept=boost::none); + tsx_builder * compute_sources_to_sub_acc(boost::optional<size_t> num_utxo=boost::none, boost::optional<uint64_t> min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional<fnc_accept_tx_source_t> fnc_accept=boost::none); + + tsx_builder * destinations(std::vector<cryptonote::tx_destination_entry> &dsts); + tsx_builder * add_destination(const cryptonote::tx_destination_entry &dst); + tsx_builder * add_destination(const tools::wallet2 * wallet, bool is_subaddr=false, uint64_t amount=1000); + tsx_builder * add_destination(const var_addr_t addr, bool is_subaddr=false, uint64_t amount=1000); + tsx_builder * set_integrated(size_t idx); + tsx_builder * rct_config(const rct::RCTConfig & rct_config) {m_rct_config = rct_config; return this; }; + + tsx_builder * build_tx(); + tsx_builder * construct_pending_tx(tools::wallet2::pending_tx &ptx, boost::optional<std::vector<uint8_t>> extra = boost::none); + tsx_builder * clear_current(); + std::vector<tools::wallet2::pending_tx> build(); + std::vector<cryptonote::address_parse_info> dest_info(){ return m_dsts_info; } + +protected: + gen_trezor_base * m_tester; + uint64_t m_cur_height; + std::vector<tools::wallet2::pending_tx> m_ptxs; // all transactions + + // current transaction + size_t m_mixin; + uint64_t m_fee; + tools::wallet2 * m_from; + uint32_t m_account; + cryptonote::transaction m_tx; + std::vector<size_t> m_selected_transfers; + std::vector<cryptonote::tx_source_entry> m_sources; + std::vector<cryptonote::tx_destination_entry> m_destinations; + std::vector<cryptonote::tx_destination_entry> m_destinations_orig; + std::vector<cryptonote::address_parse_info> m_dsts_info; + std::unordered_set<size_t> m_integrated; + std::string m_payment_id; + rct::RCTConfig m_rct_config; +}; + +class gen_trezor_ki_sync : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo_paymentid_short : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo_paymentid_short_integrated : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo_paymentid_long : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_acc1 : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_2sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_1norm_2sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_2utxo_sub_acc_to_1norm_2sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_7outs : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_many_utxo : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cb421c847..a819f76a4 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -55,6 +55,8 @@ set(unit_tests_sources http.cpp keccak.cpp logging.cpp + long_term_block_weight.cpp + lmdb.cpp main.cpp memwipe.cpp mlocker.cpp @@ -100,6 +102,7 @@ target_link_libraries(unit_tests cryptonote_protocol cryptonote_core blockchain_db + lmdb_lib rpc net serialization diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp index 113622b5e..7073ab3e8 100644 --- a/tests/unit_tests/account.cpp +++ b/tests/unit_tests/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index f6c0ad105..4b06c6487 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/apply_permutation.cpp b/tests/unit_tests/apply_permutation.cpp index e2420eb45..76d10c8ff 100644 --- a/tests/unit_tests/apply_permutation.cpp +++ b/tests/unit_tests/apply_permutation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index 1e764c83e..eb1ee8932 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -56,7 +56,7 @@ public: void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } bool handle_incoming_txs(const std::vector<cryptonote::blobdata>& tx_blob, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } - bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; } + bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; } void pause_mine(){} void resume_mine(){} bool on_idle(){return true;} @@ -65,7 +65,7 @@ public: cryptonote::blockchain_storage &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class test_core."); } bool get_test_drop_download() const {return true;} bool get_test_drop_download_height() const {return true;} - bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks) { return true; } + bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks_entry, std::vector<cryptonote::block> &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 7edb28e62..1996afd04 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/block_queue.cpp b/tests/unit_tests/block_queue.cpp index f7b7c63fd..0cba7c443 100644 --- a/tests/unit_tests/block_queue.cpp +++ b/tests/unit_tests/block_queue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index a897e4140..1dd9072b9 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 7e7ce9bf7..4fbc21ddc 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -163,17 +163,17 @@ protected: block bl; blobdata bd = h2b(i); parse_and_validate_block_from_blob(bd, bl); - m_blocks.push_back(bl); + m_blocks.push_back(std::make_pair(bl, bd)); } for (auto& i : t_transactions) { - std::vector<transaction> txs; + std::vector<std::pair<transaction, blobdata>> txs; for (auto& j : i) { transaction tx; blobdata bd = h2b(j); parse_and_validate_tx_from_blob(bd, tx); - txs.push_back(tx); + txs.push_back(std::make_pair(tx, bd)); } m_txs.push_back(txs); } @@ -187,8 +187,8 @@ protected: BlockchainDB* m_db; HardFork m_hardfork; std::string m_prefix; - std::vector<block> m_blocks; - std::vector<std::vector<transaction> > m_txs; + std::vector<std::pair<block, blobdata>> m_blocks; + std::vector<std::vector<std::pair<transaction, blobdata>>> m_txs; std::vector<std::string> m_filenames; void init_hard_fork() @@ -277,25 +277,25 @@ TYPED_TEST(BlockchainDBTest, AddBlock) // TODO: need at least one more block to make this reasonable, as the // BlockchainDB implementation should not check for parent if // no blocks have been added yet (because genesis has no parent). - //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); + //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); block b; - ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0]))); - ASSERT_NO_THROW(b = this->m_db->get_block(get_block_hash(this->m_blocks[0]))); + ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0].first))); + ASSERT_NO_THROW(b = this->m_db->get_block(get_block_hash(this->m_blocks[0].first))); - ASSERT_TRUE(compare_blocks(this->m_blocks[0], b)); + ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b)); ASSERT_NO_THROW(b = this->m_db->get_block_from_height(0)); - ASSERT_TRUE(compare_blocks(this->m_blocks[0], b)); + ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b)); // assert that we can't add the same block twice - ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); + ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); - for (auto& h : this->m_blocks[0].tx_hashes) + for (auto& h : this->m_blocks[0].first.tx_hashes) { transaction tx; ASSERT_TRUE(this->m_db->tx_exists(h)); @@ -317,31 +317,31 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData) this->get_filenames(); this->init_hard_fork(); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0)); ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0)); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1)); - ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), this->m_db->get_block_hash_from_height(0)); + ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), this->m_db->get_block_hash_from_height(0)); std::vector<block> blks; ASSERT_NO_THROW(blks = this->m_db->get_blocks_range(0, 1)); ASSERT_EQ(2, blks.size()); - ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), get_block_hash(blks[0])); - ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1]), get_block_hash(blks[1])); + ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), get_block_hash(blks[0])); + ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1].first), get_block_hash(blks[1])); std::vector<crypto::hash> hashes; ASSERT_NO_THROW(hashes = this->m_db->get_hashes_range(0, 1)); ASSERT_EQ(2, hashes.size()); - ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), hashes[0]); - ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1]), hashes[1]); + ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), hashes[0]); + ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1].first), hashes[1]); } } // anonymous namespace diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 4f07415b0..9ac0df45b 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/canonical_amounts.cpp b/tests/unit_tests/canonical_amounts.cpp index 227a64a7f..74fcc9548 100644 --- a/tests/unit_tests/canonical_amounts.cpp +++ b/tests/unit_tests/canonical_amounts.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/chacha.cpp b/tests/unit_tests/chacha.cpp index 699890522..e06081f8f 100644 --- a/tests/unit_tests/chacha.cpp +++ b/tests/unit_tests/chacha.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index ec09c596b..90229a0b8 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/command_line.cpp b/tests/unit_tests/command_line.cpp index 327fceefe..6d5afb708 100644 --- a/tests/unit_tests/command_line.cpp +++ b/tests/unit_tests/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index e09ec7f7a..7100c8013 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp index 11c6189e5..5049dd83d 100644 --- a/tests/unit_tests/decompose_amount_into_digits.cpp +++ b/tests/unit_tests/decompose_amount_into_digits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index 2b3627f02..6bffc01d8 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index 41554f948..32989f545 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 9ea71875b..697845f60 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 18fb262c2..946731826 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -389,6 +389,25 @@ TEST(ToHex, String) } +TEST(FromHex, String) +{ + // the source data to encode and decode + std::vector<uint8_t> source{{ 0x00, 0xFF, 0x0F, 0xF0 }}; + + // encode and decode the data + auto hex = epee::to_hex::string({ source.data(), source.size() }); + auto decoded = epee::from_hex::vector(hex); + + // encoded should be twice the size and should decode to the exact same data + EXPECT_EQ(source.size() * 2, hex.size()); + EXPECT_EQ(source, decoded); + + // we will now create a padded hex string, we want to explicitly allow + // decoding it this way also, ignoring spaces and colons between the numbers + hex.assign("00:ff 0f:f0"); + EXPECT_EQ(source, epee::from_hex::vector(hex)); +} + TEST(ToHex, Array) { EXPECT_EQ( diff --git a/tests/unit_tests/fee.cpp b/tests/unit_tests/fee.cpp index 8ccb38fc9..3b0bc1f09 100644 --- a/tests/unit_tests/fee.cpp +++ b/tests/unit_tests/fee.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index 54ba473a6..75452894a 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index ec8d1d202..bb26b5533 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -34,7 +34,7 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" -#include "testdb.h" +#include "blockchain_db/testdb.h" using namespace cryptonote; @@ -44,11 +44,12 @@ using namespace cryptonote; namespace { -class TestDB: public BaseTestDB { +class TestDB: public cryptonote::BaseTestDB { public: virtual uint64_t height() const { return blocks.size(); } virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -106,20 +107,20 @@ TEST(major, Only) ASSERT_FALSE(hf.add(mkblock(0, 2), 0)); ASSERT_FALSE(hf.add(mkblock(2, 2), 0)); ASSERT_TRUE(hf.add(mkblock(1, 2), 0)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, crypto::hash()); // block height 1, only version 1 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 1)); ASSERT_FALSE(hf.add(mkblock(2, 2), 1)); ASSERT_TRUE(hf.add(mkblock(1, 2), 1)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, crypto::hash()); // block height 2, only version 2 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 2)); ASSERT_FALSE(hf.add(mkblock(1, 2), 2)); ASSERT_FALSE(hf.add(mkblock(3, 2), 2)); ASSERT_TRUE(hf.add(mkblock(2, 2), 2)); - db.add_block(mkblock(2, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(2, 1), 0, 0, 0, 0, 0, crypto::hash()); } TEST(empty_hardforks, Success) @@ -133,7 +134,7 @@ TEST(empty_hardforks, Success) ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); for (uint64_t h = 0; h <= 10; ++h) { - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } ASSERT_EQ(hf.get(0), 1); @@ -167,14 +168,14 @@ TEST(check_for_height, Success) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 10; ++h) { ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -191,19 +192,19 @@ TEST(get, next_version) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_EQ(2, hf.get_next_version()); - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 9; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 10; h <= 15; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -244,7 +245,7 @@ TEST(steps_asap, Success) hf.init(); for (uint64_t h = 0; h < 10; ++h) { - db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -271,7 +272,7 @@ TEST(steps_1, Success) hf.init(); for (uint64_t h = 0 ; h < 10; ++h) { - db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -296,7 +297,7 @@ TEST(reorganize, Same) // index 0 1 2 3 4 5 6 7 8 9 static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -327,7 +328,7 @@ TEST(reorganize, Changed) static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); } @@ -347,7 +348,7 @@ TEST(reorganize, Changed) ASSERT_EQ(db.height(), 3); hf.reorganize_from_block_height(2); for (uint64_t h = 3; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ (ret, h < 15); } @@ -371,7 +372,7 @@ TEST(voting, threshold) for (uint64_t h = 0; h <= 8; ++h) { uint8_t v = 1 + !!(h % 8); - db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); if (h >= 8 && threshold == 87) { // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1 @@ -405,7 +406,7 @@ TEST(voting, different_thresholds) static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ(ret, true); } @@ -459,7 +460,7 @@ TEST(voting, info) ASSERT_EQ(expected_thresholds[h], threshold); ASSERT_EQ(4, voting); - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -522,7 +523,7 @@ TEST(reorganize, changed) #define ADD(v, h, a) \ do { \ cryptonote::block b = mkblock(hf, h, v); \ - db.add_block(b, 0, 0, 0, 0, crypto::hash()); \ + db.add_block(b, 0, 0, 0, 0, 0, crypto::hash()); \ ASSERT_##a(hf.add(b, h)); \ } while(0) #define ADD_TRUE(v, h) ADD(v, h, TRUE) diff --git a/tests/unit_tests/hashchain.cpp b/tests/unit_tests/hashchain.cpp index df87f5df2..d07dfdb7a 100644 --- a/tests/unit_tests/hashchain.cpp +++ b/tests/unit_tests/hashchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp index 372448764..938f444d7 100644 --- a/tests/unit_tests/http.cpp +++ b/tests/unit_tests/http.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/lmdb.cpp b/tests/unit_tests/lmdb.cpp new file mode 100644 index 000000000..c37c83a32 --- /dev/null +++ b/tests/unit_tests/lmdb.cpp @@ -0,0 +1,404 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <boost/range/algorithm_ext/iota.hpp> +#include <boost/range/algorithm/equal.hpp> +#include <gtest/gtest.h> + +#include "lmdb/database.h" +#include "lmdb/table.h" +#include "lmdb/transaction.h" +#include "lmdb/util.h" + +namespace +{ + enum class choice : unsigned {}; + enum class big_choice : unsigned long {}; + + struct bytes { + char data[16]; + }; + + MONERO_CURSOR(test_cursor); + + template<typename T> + int run_compare(T left, T right, MDB_cmp_func* cmp) + { + MDB_val left_val = lmdb::to_val(left); + MDB_val right_val = lmdb::to_val(right); + return (*cmp)(&left_val, &right_val); + } +} + +TEST(LMDB, Traits) +{ + EXPECT_TRUE((std::is_same<void, lmdb::identity<void>::type>())); + EXPECT_TRUE((std::is_same<unsigned, lmdb::identity<unsigned>::type>())); + + EXPECT_TRUE((std::is_same<void, lmdb::native_type<void>>())); + EXPECT_TRUE((std::is_same<unsigned, lmdb::native_type<unsigned>>())); + EXPECT_TRUE((std::is_same<unsigned, lmdb::native_type<choice>>())); + EXPECT_TRUE((std::is_same<unsigned long, lmdb::native_type<big_choice>>())); +} + +TEST(LMDB, ToNative) +{ + enum class negative_choice : int {}; + + EXPECT_TRUE((std::is_same<unsigned, decltype(lmdb::to_native(choice(0)))>())); + EXPECT_TRUE( + (std::is_same<unsigned long, decltype(lmdb::to_native(big_choice(0)))>()) + ); + EXPECT_TRUE( + (std::is_same<int, decltype(lmdb::to_native(negative_choice(0)))>()) + ); + + EXPECT_EQ(unsigned(0), lmdb::to_native(choice(0))); + EXPECT_EQ(unsigned(0xffffffff), lmdb::to_native(choice(0xffffffff))); + EXPECT_EQ(-1, lmdb::to_native(negative_choice(-1))); + + // test constexpr + static_assert(100 == lmdb::to_native(choice(100)), "to_native failed"); + static_assert(-100 == lmdb::to_native(negative_choice(-100)), "to_native failed"); +} + +TEST(LMDB, Conversions) +{ + struct one + { + big_choice i; + choice j; + }; + + const one test{big_choice(100), choice(95)}; + one test2{big_choice(1000), choice(950)}; + + EXPECT_EQ(&test, lmdb::to_val(test).mv_data); + EXPECT_NE(&test2, lmdb::to_val(test).mv_data); + EXPECT_EQ( + &test, + static_cast<const void*>(lmdb::to_byte_span(lmdb::to_val(test)).begin()) + ); + EXPECT_EQ(sizeof(test), lmdb::to_val(test).mv_size); + EXPECT_EQ(sizeof(test), lmdb::to_byte_span(lmdb::to_val(test)).size()); + + EXPECT_EQ(&test2, lmdb::to_val(test2).mv_data); + EXPECT_NE(&test, lmdb::to_val(test2).mv_data); + EXPECT_EQ( + &test2, + static_cast<const void*>(lmdb::to_byte_span(lmdb::to_val(test2)).begin()) + ); + EXPECT_EQ(sizeof(test2), lmdb::to_val(test2).mv_size); + EXPECT_EQ(sizeof(test2), lmdb::to_byte_span(lmdb::to_val(test2)).size()); +} + +TEST(LMDB, LessSort) +{ + struct one + { + unsigned i; + unsigned j; + }; + + struct two + { + unsigned i; + choice j; + }; + + EXPECT_EQ(0, run_compare(0u, 0u, &lmdb::less<unsigned>)); + EXPECT_EQ(-1, run_compare(0u, 1u, &lmdb::less<unsigned>)); + EXPECT_EQ(1, run_compare(1u, 0u, &lmdb::less<unsigned>)); + + EXPECT_EQ(0, run_compare<one>({0, 1}, {0, 1}, &lmdb::less<unsigned, sizeof(unsigned)>)); + EXPECT_EQ(-1, run_compare<one>({0, 0}, {0, 1}, &lmdb::less<unsigned, sizeof(unsigned)>)); + EXPECT_EQ(1, run_compare<one>({0, 1}, {0, 0}, &lmdb::less<unsigned, sizeof(unsigned)>)); + + EXPECT_EQ(0, run_compare<one>({0, 1}, {0, 1}, MONERO_SORT_BY(one, j))); + EXPECT_EQ(-1, run_compare<one>({0, 0}, {0, 1}, MONERO_SORT_BY(one, j))); + EXPECT_EQ(1, run_compare<one>({0, 1}, {0, 0}, MONERO_SORT_BY(one, j))); + + EXPECT_EQ(0, run_compare<two>({0, choice(1)}, {0, choice(1)}, MONERO_SORT_BY(two, j))); + EXPECT_EQ(-1, run_compare<two>({0, choice(0)}, {0, choice(1)}, MONERO_SORT_BY(two, j))); + EXPECT_EQ(1, run_compare<two>({0, choice(1)}, {0, choice(0)}, MONERO_SORT_BY(two, j))); + + // compare function addresses + EXPECT_EQ((MONERO_SORT_BY(one, i)), (MONERO_SORT_BY(two, i))); + EXPECT_EQ((MONERO_SORT_BY(one, j)), (MONERO_SORT_BY(two, j))); + EXPECT_NE((MONERO_SORT_BY(one, i)), (MONERO_SORT_BY(two, j))); + EXPECT_NE((MONERO_SORT_BY(one, j)), (MONERO_SORT_BY(two, i))); +} + +TEST(LMDB, SortCompare) +{ + struct one + { + unsigned i; + bytes j; + }; + + one test{55}; + boost::iota(test.j.data, 10); + + const one test2 = test; + + EXPECT_EQ(0, run_compare(test, test2, MONERO_COMPARE(one, j))); + + test.j.data[15] = 1; + EXPECT_GT(0, run_compare(test, test2, MONERO_COMPARE(one, j))); + + test.j.data[15] = 100; + EXPECT_LT(0, run_compare(test, test2, MONERO_COMPARE(one, j))); +} + +TEST(LMDB, Table) +{ + struct one + { + bytes i; + bytes j; + }; + + constexpr lmdb::basic_table<choice, bytes> test{"foo"}; + + EXPECT_STREQ("foo", test.name); + static_assert(test.flags == 0, "bad flags"); + static_assert(&lmdb::less<unsigned> == test.key_cmp, "bad key_cmp"); + static_assert(test.value_cmp == nullptr, "bad value_cmp"); + EXPECT_TRUE(test.get_value<bytes>(MDB_val{}).matches(std::errc::invalid_argument)); + + lmdb::basic_table<big_choice, one> test2{ + "foo2", MDB_DUPSORT, &lmdb::compare<one> + }; + + EXPECT_STREQ("foo2", test2.name); + EXPECT_EQ((MDB_DUPSORT | MDB_DUPFIXED), test2.flags); + EXPECT_EQ(&lmdb::less<unsigned long>, test2.key_cmp); + EXPECT_EQ(&lmdb::compare<one>, test2.value_cmp); + EXPECT_TRUE(test2.get_value<one>(MDB_val{}).matches(std::errc::invalid_argument)); + + one record{}; + boost::iota(record.i.data, 0); + boost::iota(record.i.data, 20); + + const one record_copy = MONERO_UNWRAP(test2.get_value<one>(lmdb::to_val(record))); + EXPECT_TRUE(boost::equal(record.i.data, record_copy.i.data)); + EXPECT_TRUE(boost::equal(record.j.data, record_copy.j.data)); + + const bytes j_copy = MONERO_UNWRAP( + test2.get_value<MONERO_FIELD(one, j)>(lmdb::to_val(record)) + ); + EXPECT_TRUE(boost::equal(record.j.data, j_copy.data)); + + EXPECT_TRUE( + test.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument) + ); + EXPECT_TRUE( + test2.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument) + ); + + + EXPECT_TRUE( + test.get_value_stream(choice(0), test_cursor{}).matches(std::errc::invalid_argument) + ); + EXPECT_TRUE( + test2.get_value_stream(big_choice(0), test_cursor{}).matches(std::errc::invalid_argument) + ); +} + +TEST(LMDB, InvalidDatabase) +{ + lmdb::database test{lmdb::environment{}}; + + EXPECT_TRUE(test.resize().matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.create_read_txn().matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.reset_txn(lmdb::read_txn{}).matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.create_write_txn().matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.commit(lmdb::write_txn{}).matches(std::errc::invalid_argument)); + + EXPECT_TRUE( + test.try_write( [](MDB_txn&) { return success(); } ).matches(std::errc::invalid_argument) + ); +} + +TEST(LMDB, InvalidValueStream) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + lmdb::value_stream<one, close_test_cursor> test{test_cursor{}}; + + EXPECT_TRUE((std::is_same<one, decltype(*(test.make_iterator()))>())); + EXPECT_TRUE((std::is_same<one, decltype(*(test.make_range().begin()))>())); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test.make_iterator<MONERO_FIELD(one, k)>()))>()) + ); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test.make_range<MONERO_FIELD(one, k)>().begin()))>()) + ); + + EXPECT_NO_THROW(test.reset()); + EXPECT_EQ(0u, test.count()); + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); + + EXPECT_EQ(0u, test.count()); + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); +} + +TEST(LMDB, InvalidValueIterator) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + lmdb::value_iterator<one> test1{}; + + EXPECT_TRUE((std::is_same<one, decltype(*test1)>())); + EXPECT_TRUE( + (std::is_same<bytes, decltype(test1.get_value<MONERO_FIELD(one, k)>())>()) + ); + + EXPECT_TRUE(test1.is_end()); + EXPECT_NO_THROW(++test1); + EXPECT_NO_THROW(test1++); + EXPECT_TRUE(test1.is_end()); + + lmdb::value_iterator<one> test2{nullptr}; + + EXPECT_TRUE(test2.is_end()); + EXPECT_NO_THROW(++test2); + EXPECT_NO_THROW(test2++); + EXPECT_TRUE(test2.is_end()); + + EXPECT_TRUE(test1.equal(test2)); + EXPECT_TRUE(test2.equal(test1)); + EXPECT_TRUE(test1 == test2); + EXPECT_TRUE(test2 == test1); + EXPECT_FALSE(test1 != test2); + EXPECT_FALSE(test2 != test1); + + lmdb::value_iterator<MONERO_FIELD(one, k)> test3{}; + + EXPECT_TRUE((std::is_same<bytes, decltype(*test3)>())); + EXPECT_TRUE((std::is_same<one, decltype(test3.get_value<one>())>())); + EXPECT_TRUE( + (std::is_same<choice, decltype(test1.get_value<MONERO_FIELD(one, j)>())>()) + ); + + EXPECT_TRUE(test3.is_end()); + EXPECT_NO_THROW(++test3); + EXPECT_NO_THROW(test3++); + EXPECT_TRUE(test3.is_end()); +} + +TEST(LMDB, InvalidKeyStream) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + using record = std::pair<choice, boost::iterator_range<lmdb::value_iterator<one>>>; + + lmdb::key_stream<choice, one, close_test_cursor> test{test_cursor{}}; + + EXPECT_TRUE((std::is_same<record, decltype(*(test.make_iterator()))>())); + EXPECT_TRUE((std::is_same<record, decltype(*(test.make_range().begin()))>())); + + EXPECT_NO_THROW(test.reset()); + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); + + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); +} + +TEST(LMDB, InvalidKeyIterator) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + using record = std::pair<choice, boost::iterator_range<lmdb::value_iterator<one>>>; + + lmdb::key_iterator<choice, one> test1{}; + + EXPECT_TRUE((std::is_same<record, decltype(*test1)>())); + EXPECT_TRUE((std::is_same<choice, decltype(test1.get_key())>())); + EXPECT_TRUE((std::is_same<one, decltype(*(test1.make_value_iterator()))>())); + EXPECT_TRUE((std::is_same<one, decltype(*(test1.make_value_range().begin()))>())); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test1.make_value_iterator<MONERO_FIELD(one, k)>()))>()) + ); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test1.make_value_range<MONERO_FIELD(one, k)>().begin()))>()) + ); + + EXPECT_TRUE(test1.is_end()); + EXPECT_NO_THROW(++test1); + EXPECT_NO_THROW(test1++); + EXPECT_TRUE(test1.is_end()); + EXPECT_TRUE(test1.make_value_iterator().is_end()); + EXPECT_TRUE(test1.make_value_range().empty()); + + lmdb::key_iterator<choice, one> test2{nullptr}; + + EXPECT_TRUE(test2.is_end()); + EXPECT_NO_THROW(++test2); + EXPECT_NO_THROW(test2++); + EXPECT_TRUE(test2.is_end()); + EXPECT_TRUE(test2.make_value_iterator().is_end()); + EXPECT_TRUE(test2.make_value_range().empty()); + + EXPECT_TRUE(test1.equal(test2)); + EXPECT_TRUE(test2.equal(test1)); + EXPECT_TRUE(test1 == test2); + EXPECT_TRUE(test2 == test1); + EXPECT_FALSE(test1 != test2); + EXPECT_FALSE(test2 != test1); +} + + diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp index 476e92bef..12d49e2fb 100644 --- a/tests/unit_tests/logging.cpp +++ b/tests/unit_tests/logging.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp new file mode 100644 index 000000000..f37b608b6 --- /dev/null +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -0,0 +1,386 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#define IN_UNIT_TESTS + +#include "gtest/gtest.h" +#include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "blockchain_db/testdb.h" + +#define TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW 5000 + +namespace +{ + +class TestDB: public cryptonote::BaseTestDB +{ +private: + struct block_t + { + size_t weight; + uint64_t long_term_weight; + }; + +public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override { + blocks.push_back({block_weight, long_term_block_weight}); + } + virtual uint64_t height() const override { return blocks.size(); } + virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } + virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { + uint64_t h = height(); + crypto::hash top = crypto::null_hash; + if (h) + *(uint64_t*)&top = h - 1; + if (block_height) + *block_height = h - 1; + return top; + } + virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); } + +private: + std::vector<block_t> blocks; +}; + +static uint32_t lcg_seed = 0; + +static uint32_t lcg() +{ + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff; + return lcg_seed; +} + +} + +#define PREFIX_WINDOW(hf_version,window) \ + std::unique_ptr<cryptonote::Blockchain> bc; \ + cryptonote::tx_memory_pool txpool(*bc); \ + bc.reset(new cryptonote::Blockchain(txpool)); \ + struct get_test_options { \ + const std::pair<uint8_t, uint64_t> hard_forks[3]; \ + const cryptonote::test_options test_options = { \ + hard_forks, \ + window, \ + }; \ + get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \ + } opts; \ + cryptonote::Blockchain *blockchain = bc.get(); \ + bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \ + ASSERT_TRUE(r) + +#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) + +TEST(long_term_block_weight, empty_short) +{ + PREFIX(9); + + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 * 2); +} + +TEST(long_term_block_weight, identical_before_fork) +{ + PREFIX(9); + + for (uint64_t h = 1; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < CRYPTONOTE_REWARD_BLOCKS_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (uint64_t h = 0; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h)); + } +} + +TEST(long_term_block_weight, identical_after_fork_before_long_term_window) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h)); + } +} + +TEST(long_term_block_weight, ceiling_at_30000000) +{ + PREFIX(10); + + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 2 - 1; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), 15000000); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), 30000000); +} + +TEST(long_term_block_weight, multi_pop) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + 20; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + + for (uint64_t h = 0; h < 4; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); +} + +TEST(long_term_block_weight, multiple_updates) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= 3 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } +} + +TEST(long_term_block_weight, pop_invariant_max) +{ + PREFIX(10); + + for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + for (int n = 0; n < 1000; ++n) + { + // pop some blocks, then add some more + int remove = 1 + (n * 17) % 8; + int add = (n * 23) % 12; + + // save long term block weights we're about to remove + uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; + for (int i = -2; i < remove; ++i) + { + old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i); + } + + for (int i = 0; i < remove; ++i) + { + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (int i = 0; i < add; ++i) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + // check the new values are the same as the old ones + for (int i = -2; i < std::min(add, remove); ++i) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]); + } + } +} + +TEST(long_term_block_weight, pop_invariant_random) +{ + PREFIX(10); + + for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + for (int n = 0; n < 1000; ++n) + { + // pop some blocks, then add some more + int remove = 1 + (n * 17) % 8; + int add = (n * 23) % 123; + + // save long term block weights we're about to remove + uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; + for (int i = -2; i < remove; ++i) + { + old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i); + } + + for (int i = 0; i < remove; ++i) + { + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } + for (int i = 0; i < add; ++i) + { + lcg_seed = bc->get_db().height(); + uint32_t r = lcg(); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } + + // check the new values are the same as the old ones + for (int i = -2; i < std::min(add, remove); ++i) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]); + } + } +} + +TEST(long_term_block_weight, long_growth_spike_and_drop) +{ + PREFIX(10); + + uint64_t long_term_effective_median_block_weight; + + // constant init + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_EQ(long_term_effective_median_block_weight, 300000); + + // slow 10% yearly for a year (scaled down by 100000 / TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) -> 8% change + for (uint64_t h = 0; h < 365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + //size_t w = bc->get_current_cumulative_block_weight_median() * rate; + float t = h / float(365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000); + size_t w = 300000 + t * 30000; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); + + // spike over three weeks - does not move much + for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + size_t w = bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); + + // drop - does not move much + for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + size_t w = bc->get_current_cumulative_block_weight_median() * .25; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); +} diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index f7251a09e..76d17f2ad 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/memwipe.cpp b/tests/unit_tests/memwipe.cpp index dd98b8142..e0f5ada20 100644 --- a/tests/unit_tests/memwipe.cpp +++ b/tests/unit_tests/memwipe.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 59642828d..16634e7a1 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 768b95d4b..b11f715cd 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index eb3196863..9a74b3dce 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index ceeba8649..4de4a8d0a 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -82,7 +82,6 @@ TEST(notify, works) ok = true; break; } - std::cout << "got: [" << s << "]" << std::endl; } } boost::filesystem::remove(name_template); diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp index 649752ac7..45f2c135b 100644 --- a/tests/unit_tests/output_distribution.cpp +++ b/tests/unit_tests/output_distribution.cpp @@ -33,7 +33,7 @@ #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/tx_pool.h" #include "cryptonote_core/blockchain.h" -#include "testdb.h" +#include "blockchain_db/testdb.h" static const uint64_t test_distribution[32] = { 0, 0, 0, 0, 0, 1, 5, 1, 4, 0, 0, 1, 0, 1, 2, 3, 1, 0, 2, 0, 1, 3, 8, 1, 3, 5, 7, 1, 5, 0, 2, 3 @@ -43,7 +43,7 @@ static const size_t test_distribution_size = sizeof(test_distribution) / sizeof( namespace { -class TestDB: public BaseTestDB +class TestDB: public cryptonote::BaseTestDB { public: TestDB(size_t bc_height = test_distribution_size): blockchain_height(bc_height) { m_open = true; } diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index fecd547f7..a528679e4 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index eb8c925b1..f4f57f90f 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 3f302cb83..e239154cf 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 343a11c37..eb70caefc 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/sha256.cpp b/tests/unit_tests/sha256.cpp index 0d1788f3e..898c9e4b3 100644 --- a/tests/unit_tests/sha256.cpp +++ b/tests/unit_tests/sha256.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp index 436259bee..4f13e00e6 100644 --- a/tests/unit_tests/slow_memmem.cpp +++ b/tests/unit_tests/slow_memmem.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp index 67802d736..385a2a8ab 100644 --- a/tests/unit_tests/subaddress.cpp +++ b/tests/unit_tests/subaddress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index 03aa48ea0..dbb3aaf96 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index d385bbc42..7329c0d23 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index 55c76c3b6..d8d760b07 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h deleted file mode 100644 index 8f5cf70e8..000000000 --- a/tests/unit_tests/testdb.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2014-2018, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -#pragma once - -#include <string> -#include <vector> -#include <map> -#include "gtest/gtest.h" - -#include "blockchain_db/blockchain_db.h" - -class BaseTestDB: public cryptonote::BlockchainDB { -public: - BaseTestDB() {} - virtual void open(const std::string& filename, const int db_flags = 0) { } - virtual void close() {} - virtual void sync() {} - virtual void safesyncmode(const bool onoff) {} - virtual void reset() {} - virtual std::vector<std::string> get_filenames() const { return std::vector<std::string>(); } - virtual bool remove_data_file(const std::string& folder) const { return true; } - virtual std::string get_db_name() const { return std::string(); } - virtual bool lock() { return true; } - virtual void unlock() { } - virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } - virtual void batch_stop() {} - virtual void set_batch_transactions(bool) {} - virtual void block_txn_start(bool readonly=false) {} - virtual void block_txn_stop() {} - virtual void block_txn_abort() {} - virtual void drop_hard_fork_info() {} - virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } - virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } - virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const { return cryptonote::blobdata(); } - virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } - virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } - virtual cryptonote::block_header get_block_header(const crypto::hash& h) const { return cryptonote::block_header(); } - virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } - virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; } - virtual uint64_t get_top_block_timestamp() const { return 0; } - virtual size_t get_block_weight(const uint64_t& height) const { return 128; } - virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } - virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } - virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } - virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } - virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<cryptonote::block>(); } - virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); } - virtual crypto::hash top_block_hash() const { return crypto::hash(); } - virtual cryptonote::block get_top_block() const { return cryptonote::block(); } - virtual uint64_t height() const { return 1; } - virtual bool tx_exists(const crypto::hash& h) const { return false; } - virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } - virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } - virtual cryptonote::transaction get_tx(const crypto::hash& h) const { return cryptonote::transaction(); } - virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const { return false; } - virtual uint64_t get_tx_count() const { return 0; } - virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const { return std::vector<cryptonote::transaction>(); } - virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } - virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } - virtual uint64_t get_indexing_base() const { return 0; } - virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const { return cryptonote::output_data_t(); } - virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); } - virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); } - virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const {} - virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const {} - virtual bool can_thread_bulk_indices() const { return false; } - virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); } - virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const { return std::vector<std::vector<uint64_t>>(); } - virtual bool has_key_image(const crypto::key_image& img) const { return false; } - virtual void remove_block() { } - virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const cryptonote::transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} - virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) {} - virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} - virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) {} - virtual void add_spent_key(const crypto::key_image& k_image) {} - virtual void remove_spent_key(const crypto::key_image& k_image) {} - - virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const { return true; } - virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const { return true; } - virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const { return true; } - virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const { return true; } - virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const { return true; } - virtual bool is_read_only() const { return false; } - virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); } - virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const { return false; } - - virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) {} - virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) {} - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } - virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } - virtual void remove_txpool_tx(const crypto::hash& txid) {} - virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const { return false; } - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } - virtual uint64_t get_database_size() const { return 0; } - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } - - virtual void add_block( const cryptonote::block& blk - , size_t block_weight - , const cryptonote::difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , uint64_t num_rct_outs - , const crypto::hash& blk_hash - ) { } - virtual cryptonote::block get_block_from_height(const uint64_t& height) const { return cryptonote::block(); } - virtual void set_hard_fork_version(uint64_t height, uint8_t version) {} - virtual uint8_t get_hard_fork_version(uint64_t height) const { return 0; } - virtual void check_hard_fork_info() {} - - virtual uint32_t get_blockchain_pruning_seed() const { return 0; } - virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } - virtual bool update_pruning() { return true; } - virtual bool check_pruning() { return true; } - virtual void prune_outputs(uint64_t amount) {} -}; - diff --git a/tests/unit_tests/unbound.cpp b/tests/unit_tests/unbound.cpp index 3676e88f0..d122b2ad2 100644 --- a/tests/unit_tests/unbound.cpp +++ b/tests/unit_tests/unbound.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index ecd97e3d5..5944bd55a 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/uri.cpp b/tests/unit_tests/uri.cpp index 999c117c2..df1dbc130 100644 --- a/tests/unit_tests/uri.cpp +++ b/tests/unit_tests/uri.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp index db675c888..ca0900682 100644 --- a/tests/unit_tests/varint.cpp +++ b/tests/unit_tests/varint.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/vercmp.cpp b/tests/unit_tests/vercmp.cpp index 43045979e..77399fa89 100644 --- a/tests/unit_tests/vercmp.cpp +++ b/tests/unit_tests/vercmp.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // |