From 056c9967036175e78b7ac1758d9a2943a9283bfc Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 29 Sep 2023 19:11:20 +0200 Subject: [PATCH 1/2] fix chaingen tests - fix tx create from sources, input locking. Originally, creating a synthetic transactions with chaingen could create a transaction with outputs that are still locked in the current block, thus failing chain validation by the daemon. Simple unlock check was added. Some buggy tests were fixed as well as new unlock-checking version of tx creation rejected those, fixes are simple - mostly using correct block after a rewind to construct a transaction --- tests/core_tests/block_reward.cpp | 10 ++--- tests/core_tests/block_validation.cpp | 2 +- tests/core_tests/chain_split_1.cpp | 6 +-- tests/core_tests/chain_switch_1.cpp | 24 +++++------ tests/core_tests/chaingen.cpp | 59 ++++++++++++++++++--------- tests/core_tests/chaingen.h | 44 ++++++++++++++++---- tests/core_tests/chaingen001.cpp | 12 +++--- tests/core_tests/double_spend.h | 2 +- tests/core_tests/double_spend.inl | 14 ++----- tests/core_tests/integer_overflow.cpp | 4 +- tests/core_tests/ring_signature_1.cpp | 36 ++++++++-------- tests/core_tests/tx_pool.cpp | 14 +++---- tests/core_tests/tx_validation.cpp | 42 +++++++++---------- tests/core_tests/wallet_tools.cpp | 51 ++++++++++++++--------- tests/core_tests/wallet_tools.h | 6 ++- 15 files changed, 191 insertions(+), 135 deletions(-) diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index bd2fc2a9b..7f75aea5b 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -172,21 +172,21 @@ bool gen_block_reward::generate(std::vector& events) const return false; // Test: fee increases block reward - transaction tx_0(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * TESTS_DEFAULT_FEE)); + transaction tx_0(construct_tx_with_fee(events, blk_5r, miner_account, bob_account, MK_COINS(1), 3 * TESTS_DEFAULT_FEE)); MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_account, tx_0); DO_CALLBACK(events, "mark_checked_block"); // Test: fee from all block transactions increase block reward std::list txs_0; - txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * TESTS_DEFAULT_FEE)); - txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * TESTS_DEFAULT_FEE)); + txs_0.push_back(construct_tx_with_fee(events, blk_5r, miner_account, bob_account, MK_COINS(1), 5 * TESTS_DEFAULT_FEE)); + txs_0.push_back(construct_tx_with_fee(events, blk_5r, miner_account, bob_account, MK_COINS(1), 7 * TESTS_DEFAULT_FEE)); MAKE_NEXT_BLOCK_TX_LIST(events, blk_7, blk_6, miner_account, txs_0); DO_CALLBACK(events, "mark_checked_block"); // Test: block reward == transactions fee { - transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE); - transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE); + transaction tx_1 = construct_tx_with_fee(events, blk_5r, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE); + transaction tx_2 = construct_tx_with_fee(events, blk_5r, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE); size_t txs_1_weight = get_transaction_weight(tx_1) + get_transaction_weight(tx_2); uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2); diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 37f19d94b..071d2198e 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -580,7 +580,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev while (diffic < 1500); blk_last = boost::get(events.back()); - MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(30), boost::get(events[1])); + MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(30), blk_last); DO_CALLBACK(events, "corrupt_blocks_boundary"); block blk_test; diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index 8f49b1af8..cffabb9c1 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -114,9 +114,9 @@ bool gen_simple_chain_split_1::generate(std::vector &events) c REWIND_BLOCKS(events, blk_23r, blk_23, first_miner_account); // 30...N1 GENERATE_ACCOUNT(alice); - MAKE_TX(events, tx_0, first_miner_account, alice, MK_COINS(10), blk_23); // N1+1 - MAKE_TX(events, tx_1, first_miner_account, alice, MK_COINS(20), blk_23); // N1+2 - MAKE_TX(events, tx_2, first_miner_account, alice, MK_COINS(30), blk_23); // N1+3 + MAKE_TX(events, tx_0, first_miner_account, alice, MK_COINS(10), blk_23r); // N1+1 + MAKE_TX(events, tx_1, first_miner_account, alice, MK_COINS(20), blk_23r); // N1+2 + MAKE_TX(events, tx_2, first_miner_account, alice, MK_COINS(30), blk_23r); // N1+3 DO_CALLBACK(events, "check_mempool_1"); // N1+4 MAKE_NEXT_BLOCK_TX1(events, blk_24, blk_23r, first_miner_account, tx_0); // N1+5 DO_CALLBACK(events, "check_mempool_2"); // N1+6 diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 8271ab783..2d9cdcbd8 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -72,37 +72,37 @@ bool gen_chain_switch_1::generate(std::vector& events) const MAKE_ACCOUNT(events, recipient_account_3); // 3 MAKE_ACCOUNT(events, recipient_account_4); // 4 REWIND_BLOCKS(events, blk_0r, blk_0, miner_account) // - MAKE_TX(events, tx_00, miner_account, recipient_account_1, MK_COINS(5), blk_0); // 5 + N + MAKE_TX(events, tx_00, miner_account, recipient_account_1, MK_COINS(5), blk_0r); // 5 + N MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_00); // 6 + N MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 7 + N REWIND_BLOCKS(events, blk_2r, blk_2, miner_account) // // Transactions to test account balances after switch - MAKE_TX_LIST_START(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(7), blk_2); // 8 + 2N - MAKE_TX_LIST_START(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(11), blk_2); // 9 + 2N - MAKE_TX_LIST_START(events, txs_blk_5, miner_account, recipient_account_4, MK_COINS(13), blk_2); // 10 + 2N + MAKE_TX_LIST_START(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(7), blk_2r); // 8 + 2N + MAKE_TX_LIST_START(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(11), blk_2r); // 9 + 2N + MAKE_TX_LIST_START(events, txs_blk_5, miner_account, recipient_account_4, MK_COINS(13), blk_2r); // 10 + 2N std::list txs_blk_6; txs_blk_6.push_back(txs_blk_4.front()); // Transactions, that has different order in alt block chains - MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(1), blk_2); // 11 + 2N + MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(1), blk_2r); // 11 + 2N txs_blk_5.push_back(txs_blk_3.back()); - MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(2), blk_2); // 12 + 2N + MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_1, MK_COINS(2), blk_2r); // 12 + 2N txs_blk_6.push_back(txs_blk_3.back()); - MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(1), blk_2); // 13 + 2N + MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(1), blk_2r); // 13 + 2N txs_blk_5.push_back(txs_blk_3.back()); - MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_2, MK_COINS(2), blk_2); // 14 + 2N + MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_2, MK_COINS(2), blk_2r); // 14 + 2N txs_blk_5.push_back(txs_blk_4.back()); - MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_3, MK_COINS(1), blk_2); // 15 + 2N + MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_3, MK_COINS(1), blk_2r); // 15 + 2N txs_blk_6.push_back(txs_blk_3.back()); - MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(2), blk_2); // 16 + 2N + MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(2), blk_2r); // 16 + 2N txs_blk_5.push_back(txs_blk_4.back()); - MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_4, MK_COINS(1), blk_2); // 17 + 2N + MAKE_TX_LIST(events, txs_blk_4, miner_account, recipient_account_4, MK_COINS(1), blk_2r); // 17 + 2N txs_blk_5.push_back(txs_blk_4.back()); - MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_4, MK_COINS(2), blk_2); // 18 + 2N + MAKE_TX_LIST(events, txs_blk_3, miner_account, recipient_account_4, MK_COINS(2), blk_2r); // 18 + 2N txs_blk_6.push_back(txs_blk_3.back()); MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_blk_3); // 19 + 2N diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index b66aa7924..9aa0de8e5 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -504,7 +504,7 @@ bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, return true; } -bool fill_output_entries(std::vector& out_indices, size_t sender_out, size_t nmix, size_t& real_entry_idx, std::vector& output_entries) +bool fill_output_entries(std::vector& out_indices, size_t sender_out, size_t nmix, size_t& real_entry_idx, std::vector& output_entries, uint64_t cur_height, const boost::optional& fnc_accept = boost::none) { if (out_indices.size() <= nmix) return false; @@ -516,6 +516,10 @@ bool fill_output_entries(std::vector& out_indices, size_t sender_o const output_index& oi = out_indices[i]; if (oi.spent) continue; + if (oi.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && oi.unlock_time > cur_height + 1) + continue; + if (fnc_accept && !(fnc_accept.get())({.oi=oi, .cur_height=cur_height})) + continue; bool append = false; if (i == sender_out) @@ -542,7 +546,8 @@ bool fill_output_entries(std::vector& out_indices, size_t sender_o } bool fill_tx_sources(std::vector& sources, const std::vector& events, - const block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix) + const block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix, + bool check_unlock_time, const boost::optional& fnc_accept) { map_output_idx_t outs; map_output_t outs_mine; @@ -559,6 +564,7 @@ bool fill_tx_sources(std::vector& sources, const std::vector::max() - 1; uint64_t sources_amount = 0; bool sources_found = false; BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine) @@ -571,13 +577,17 @@ bool fill_tx_sources(std::vector& sources, const std::vector head_height + 1) + continue; + if (fnc_accept && !(fnc_accept.get())({.oi=oi, .cur_height=head_height})) + continue; cryptonote::tx_source_entry ts; ts.amount = oi.amount; ts.real_output_in_tx_index = oi.out_no; ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key size_t realOutput; - if (!fill_output_entries(outs[o.first], sender_out, nmix, realOutput, ts.outputs)) + if (!fill_output_entries(outs[o.first], sender_out, nmix, realOutput, ts.outputs, head_height, fnc_accept)) continue; ts.real_output = realOutput; @@ -692,7 +702,7 @@ void block_tracker::global_indices(const cryptonote::transaction *tx, std::vecto } } -void block_tracker::get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector &outs){ +void block_tracker::get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector &outs, const boost::optional& fnc_accept){ auto & vct = m_outs[amount]; const size_t n_outs = vct.size(); CHECK_AND_ASSERT_THROW_MES(n_outs > 0, "n_outs is 0"); @@ -717,15 +727,16 @@ void block_tracker::get_fake_outs(size_t num_outs, uint64_t amount, uint64_t glo continue; if (oi.out.type() != typeid(cryptonote::txout_to_key)) continue; - if (oi.unlock_time > cur_height) + if (oi.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && oi.unlock_time > cur_height + 1) continue; if (used.find(oi_idx) != used.end()) continue; + if (fnc_accept && !(fnc_accept.get())({.oi=oi, .cur_height=cur_height})) + continue; rct::key comm = oi.commitment(); auto out = boost::get(oi.out); - auto item = std::make_tuple(oi.idx, out.key, comm); - outs.push_back(item); + outs.emplace_back(oi.idx, out.key, comm); used.insert(oi_idx); } } @@ -924,13 +935,14 @@ void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_publ void fill_tx_sources_and_destinations(const std::vector& 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& sources, - std::vector& destinations) + std::vector& destinations, bool check_unlock_time, + const boost::optional& fnc_tx_in_accept) { 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"); + if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix, check_unlock_time, fnc_tx_in_accept)) + throw tx_construct_tx_fill_error(); fill_tx_destinations(from, to, amount, fee, sources, destinations, false); } @@ -938,9 +950,10 @@ void fill_tx_sources_and_destinations(const std::vector& event void fill_tx_sources_and_destinations(const std::vector& 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& sources, - std::vector& destinations) + std::vector& destinations, bool check_unlock_time, + const boost::optional& fnc_tx_in_accept) { - fill_tx_sources_and_destinations(events, blk_head, from, to.get_keys().m_account_address, amount, fee, nmix, sources, destinations); + fill_tx_sources_and_destinations(events, blk_head, from, to.get_keys().m_account_address, amount, fee, nmix, sources, destinations, check_unlock_time, fnc_tx_in_accept); } cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr, uint64_t amount) @@ -952,10 +965,14 @@ cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr return de; } -std::vector build_dsts(const var_addr_t& to1, bool sub1, uint64_t am1) +std::vector build_dsts(const var_addr_t& to1, bool sub1, uint64_t am1, size_t repeat) { std::vector res; - res.push_back(build_dst(to1, sub1, am1)); + res.reserve(repeat); + for(size_t i = 0; i < repeat; ++i) + { + res.emplace_back(build_dst(to1, sub1, am1)); + } return res; } @@ -1019,25 +1036,27 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins bool construct_tx_to_key(const std::vector& 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) + uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version, + bool check_unlock_time, const boost::optional& fnc_tx_in_accept) { vector sources; vector destinations; - fill_tx_sources_and_destinations(events, blk_head, from, get_address(to), amount, fee, nmix, sources, destinations); + fill_tx_sources_and_destinations(events, blk_head, from, get_address(to), amount, fee, nmix, sources, destinations, check_unlock_time, fnc_tx_in_accept); return construct_tx_rct(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector(), tx, 0, rct, range_proof_type, bp_version); } bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, - const cryptonote::account_base& from, std::vector destinations, - uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version) + const cryptonote::account_base& from, const std::vector& destinations, + uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version, + bool check_unlock_time, const boost::optional& fnc_tx_in_accept) { vector sources; vector 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"); + if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix, check_unlock_time, fnc_tx_in_accept)) + throw tx_construct_tx_fill_error(); fill_tx_destinations(from, destinations, fee, sources, destinations_all, false); diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 31580502d..4d990f439 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -372,6 +372,13 @@ typedef struct { uint64_t amount; } dest_wrapper_t; +typedef struct { + const output_index &oi; + uint64_t cur_height; +} fnc_accept_output_crate_t; + +typedef std::function fnc_accept_output_t; + // Daemon functionality class block_tracker { @@ -388,7 +395,7 @@ public: void process(const std::vector& 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 &indices); - void get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector &outs); + void get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector &outs, const boost::optional& fnc_accept = boost::none); std::string dump_data(); void dump_data(const std::string & fname); @@ -405,6 +412,19 @@ private: } }; +class tx_construct_error : public std::runtime_error +{ +public: + tx_construct_error(const char *s) : runtime_error(s) { } +}; + +class tx_construct_tx_fill_error : public tx_construct_error +{ +public: + tx_construct_tx_fill_error() : tx_construct_error("Couldn't fill transaction sources") { } + tx_construct_tx_fill_error(const char *s) : tx_construct_error(s) { } +}; + 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); @@ -416,7 +436,7 @@ inline cryptonote::difficulty_type get_test_difficulty(const boost::optional& hf_ver=boost::none){ return !hf_ver || hf_ver.get() <= 1 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; } cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr=false, uint64_t amount=0); -std::vector build_dsts(const var_addr_t& to1, bool sub1=false, uint64_t am1=0); +std::vector build_dsts(const var_addr_t& to1, bool sub1=false, uint64_t am1=0, size_t repeat=1); std::vector build_dsts(std::initializer_list inps); uint64_t sum_amount(const std::vector& destinations); uint64_t sum_amount(const std::vector& sources); @@ -428,11 +448,13 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins bool construct_tx_to_key(const std::vector& 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=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0, + bool check_unlock_time = true, const boost::optional& fnc_tx_in_accept = boost::none); bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, - const cryptonote::account_base& from, std::vector destinations, - uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + const cryptonote::account_base& from, const std::vector& destinations, + uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0, + bool check_unlock_time = true, const boost::optional& fnc_tx_in_accept = boost::none); bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, std::vector &sources, @@ -463,6 +485,10 @@ bool trim_block_chain(std::vector& blockchain, const c bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); +bool fill_tx_sources(std::vector& sources, const std::vector& events, + const cryptonote::block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix, + bool check_unlock_time = true, const boost::optional& fnc_accept = boost::none); + void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, uint64_t amount, uint64_t fee, const std::vector &sources, @@ -486,13 +512,17 @@ void fill_tx_sources_and_destinations(const std::vector& event const cryptonote::account_base& from, const cryptonote::account_public_address& to, uint64_t amount, uint64_t fee, size_t nmix, std::vector& sources, - std::vector& destinations); + std::vector& destinations, + bool check_unlock_time = true, + const boost::optional& fnc_tx_in_accept = boost::none); void fill_tx_sources_and_destinations(const std::vector& 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& sources, - std::vector& destinations); + std::vector& destinations, + bool check_unlock_time = true, + const boost::optional& fnc_tx_in_accept = boost::none); uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx); diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index 8738585e0..1dde16ff5 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -120,18 +120,18 @@ bool gen_simple_chain_001::generate(std::vector &events) std::cout << "BALANCE = " << get_balance(miner, chain, mtx) << std::endl; REWIND_BLOCKS(events, blk_2r, blk_2, miner); - MAKE_TX_LIST_START(events, txlist_0, miner, alice, MK_COINS(1), blk_2); - MAKE_TX_LIST(events, txlist_0, miner, alice, MK_COINS(2), blk_2); - MAKE_TX_LIST(events, txlist_0, miner, alice, MK_COINS(4), blk_2); + MAKE_TX_LIST_START(events, txlist_0, miner, alice, MK_COINS(1), blk_2r); + MAKE_TX_LIST(events, txlist_0, miner, alice, MK_COINS(2), blk_2r); + MAKE_TX_LIST(events, txlist_0, miner, alice, MK_COINS(4), blk_2r); MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner, txlist_0); REWIND_BLOCKS(events, blk_3r, blk_3, miner); - MAKE_TX(events, tx_1, miner, alice, MK_COINS(50), blk_3); + MAKE_TX(events, tx_1, miner, alice, MK_COINS(50), blk_3r); MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3r, miner, tx_1); REWIND_BLOCKS(events, blk_4r, blk_4, miner); - MAKE_TX(events, tx_2, miner, alice, MK_COINS(50), blk_4); + MAKE_TX(events, tx_2, miner, alice, MK_COINS(50), blk_4r); MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner, tx_2); REWIND_BLOCKS(events, blk_5r, blk_5, miner); - MAKE_TX(events, tx_3, miner, alice, MK_COINS(50), blk_5); + MAKE_TX(events, tx_3, miner, alice, MK_COINS(50), blk_5r); MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner, tx_3); DO_CALLBACK(events, "verify_callback_1"); diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index b1f071f38..85c855bcf 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -144,7 +144,7 @@ public: MAKE_ACCOUNT(events, bob_account); \ MAKE_ACCOUNT(events, alice_account); \ REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); \ - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); \ + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); \ MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_account, tx_0); \ REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 4b60da3a3..b6e3726cc 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -126,20 +126,14 @@ bool gen_double_spend_in_tx::generate(std::vector sources; - cryptonote::tx_source_entry se; - se.amount = tx_0.vout[0].amount; - se.push_output(0, boost::get(tx_0.vout[0].target).key, se.amount); - se.real_output = 0; - se.rct = false; - se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0); - se.real_output_in_tx_index = 0; - sources.push_back(se); + CHECK_AND_ASSERT_THROW_MES(fill_tx_sources(sources, events, blk_1r, bob_account, send_amount, 0), "Source find error"); + // Double spend! - sources.push_back(se); + sources.push_back(sources[0]); cryptonote::tx_destination_entry de; de.addr = alice_account.get_keys().m_account_address; - de.amount = 2 * se.amount - TESTS_DEFAULT_FEE; + de.amount = 2 * send_amount - TESTS_DEFAULT_FEE; std::vector destinations; destinations.push_back(de); diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 42ad0d26a..6ffc3786b 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -123,8 +123,8 @@ bool gen_uint_overflow_1::generate(std::vector& events) const events.push_back(blk_2); REWIND_BLOCKS(events, blk_2r, blk_2, miner_account); - MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2); - MAKE_TX_LIST(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2); + MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2r); + MAKE_TX_LIST(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2r); MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_0); REWIND_BLOCKS(events, blk_3r, blk_3, miner_account); diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index f3f8109c3..218747f21 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -70,19 +70,19 @@ bool gen_ring_signature_1::generate(std::vector& events) const MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_account); // 8 REWIND_BLOCKS(events, blk_5, blk_4, miner_account); // REWIND_BLOCKS(events, blk_5r, blk_5, miner_account); // - MAKE_TX_LIST_START(events, txs_blk_6, miner_account, bob_account, MK_COINS(1), blk_5); // 9 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5); // 10 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5); // 11 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(20) + rnd_20, blk_5); // 12 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 13 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 14 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5); // 15 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 16 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 17 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 18 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5); // 19 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(20) + rnd_20, blk_5); // 20 + 2N - MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_2, MK_COINS(20) + rnd_20, blk_5); // 21 + 2N + MAKE_TX_LIST_START(events, txs_blk_6, miner_account, bob_account, MK_COINS(1), blk_5r); // 9 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5r); // 10 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(11) + rnd_11, blk_5r); // 11 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(20) + rnd_20, blk_5r); // 12 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5r); // 13 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5r); // 14 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, bob_account, MK_COINS(29) + rnd_29, blk_5r); // 15 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5r); // 16 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5r); // 17 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5r); // 18 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(11) + rnd_11, blk_5r); // 19 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_1, MK_COINS(20) + rnd_20, blk_5r); // 20 + 2N + MAKE_TX_LIST(events, txs_blk_6, miner_account, some_account_2, MK_COINS(20) + rnd_20, blk_5r); // 21 + 2N MAKE_NEXT_BLOCK_TX_LIST(events, blk_6, blk_5r, miner_account, txs_blk_6); // 22 + 2N DO_CALLBACK(events, "check_balances_1"); // 23 + 2N REWIND_BLOCKS(events, blk_6r, blk_6, miner_account); // @@ -161,14 +161,14 @@ bool gen_ring_signature_2::generate(std::vector& events) const MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_account); // 4 MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_account); // 5 REWIND_BLOCKS(events, blk_3r, blk_3, miner_account); // - MAKE_TX_LIST_START(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 6 + N - MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 7 + N - MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 8 + N - MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3); // 9 + N + MAKE_TX_LIST_START(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3r); // 6 + N + MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3r); // 7 + N + MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3r); // 8 + N + MAKE_TX_LIST(events, txs_blk_4, miner_account, bob_account, MK_COINS(13), blk_3r); // 9 + N MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_blk_4); // 10 + N DO_CALLBACK(events, "check_balances_1"); // 11 + N REWIND_BLOCKS(events, blk_4r, blk_4, miner_account); // - MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(52) - TESTS_DEFAULT_FEE, 3, blk_4); // 12 + 2N + MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(52) - TESTS_DEFAULT_FEE, 3, blk_4r); // 12 + 2N MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner_account, tx_0); // 13 + 2N DO_CALLBACK(events, "check_balances_2"); // 14 + 2N diff --git a/tests/core_tests/tx_pool.cpp b/tests/core_tests/tx_pool.cpp index d3dc5d9bb..1b8480526 100644 --- a/tests/core_tests/tx_pool.cpp +++ b/tests/core_tests/tx_pool.cpp @@ -98,7 +98,7 @@ bool txpool_spend_key_public::generate(std::vector& events) co INIT_MEMPOOL_TEST(); DO_CALLBACK(events, "check_txpool_spent_keys"); - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); DO_CALLBACK(events, "increase_broadcasted_tx_count"); DO_CALLBACK(events, "increase_all_tx_count"); DO_CALLBACK(events, "check_txpool_spent_keys"); @@ -112,7 +112,7 @@ bool txpool_spend_key_all::generate(std::vector& events) SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_do_not_relay); DO_CALLBACK(events, "check_txpool_spent_keys"); - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); DO_CALLBACK(events, "increase_all_tx_count"); DO_CALLBACK(events, "check_txpool_spent_keys"); @@ -502,7 +502,7 @@ bool txpool_double_spend_norelay::generate(std::vector& events SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_do_not_relay); DO_CALLBACK(events, "mark_no_new"); - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); DO_CALLBACK(events, "increase_all_tx_count"); DO_CALLBACK(events, "check_txpool_spent_keys"); @@ -539,7 +539,7 @@ bool txpool_double_spend_local::generate(std::vector& events) SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_local_relay); DO_CALLBACK(events, "mark_no_new"); - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); DO_CALLBACK(events, "increase_all_tx_count"); DO_CALLBACK(events, "check_txpool_spent_keys"); @@ -574,7 +574,7 @@ bool txpool_double_spend_keyimage::generate(std::vector& event DO_CALLBACK(events, "mark_no_new"); const std::size_t tx_index1 = events.size(); - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_stem); DO_CALLBACK(events, "increase_all_tx_count"); @@ -595,7 +595,7 @@ bool txpool_double_spend_keyimage::generate(std::vector& event auto events_copy = events; events_copy.erase(events_copy.begin() + tx_index1); events_copy.erase(events_copy.begin() + tx_index2 - 1); - MAKE_TX(events_copy, tx_temp, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events_copy, tx_temp, miner_account, bob_account, send_amount, blk_0r); tx_1 = tx_temp; } @@ -616,7 +616,7 @@ bool txpool_stem_loop::generate(std::vector& events) const SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_stem); DO_CALLBACK(events, "mark_no_new"); - MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); DO_CALLBACK(events, "increase_all_tx_count"); DO_CALLBACK(events, "check_txpool_spent_keys"); diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index cfc8f089d..bc8622539 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -141,7 +141,7 @@ namespace { std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations, false); tx_builder builder; builder.step1_init(1, unlock_time); @@ -191,7 +191,7 @@ bool gen_tx_big_version::generate(std::vector& events) const std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(1 + 1, 0); @@ -217,7 +217,7 @@ bool gen_tx_unlock_time::generate(std::vector& events) const auto make_tx_with_unlock_time = [&](uint64_t unlock_time) -> transaction { - return make_simple_tx_with_unlock_time(events, blk_1, miner_account, miner_account, MK_COINS(1), unlock_time); + return make_simple_tx_with_unlock_time(events, blk_1r, miner_account, miner_account, MK_COINS(1), unlock_time); }; std::list txs_0; @@ -266,7 +266,7 @@ bool gen_tx_input_is_not_txin_to_key::generate(std::vector& ev { std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -309,7 +309,7 @@ bool gen_tx_no_inputs_has_outputs::generate(std::vector& event std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_destinations(miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), TESTS_DEFAULT_FEE, sources, destinations, false); tx_builder builder; builder.step1_init(); @@ -331,7 +331,7 @@ bool gen_tx_has_inputs_no_outputs::generate(std::vector& event std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); destinations.clear(); tx_builder builder; @@ -357,7 +357,7 @@ bool gen_tx_invalid_input_amount::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); sources.front().amount++; tx_builder builder; @@ -383,7 +383,7 @@ bool gen_tx_input_wo_key_offsets::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -414,8 +414,8 @@ bool gen_tx_key_offest_points_to_foreign_key::generate(std::vector sources_bob; @@ -451,7 +451,7 @@ bool gen_tx_sender_key_offest_not_exist::generate(std::vector& std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -478,8 +478,8 @@ bool gen_tx_mixed_key_offest_not_exist::generate(std::vector& REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); MAKE_ACCOUNT(events, alice_account); MAKE_ACCOUNT(events, bob_account); - MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); - MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); + MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1r); + MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1r); MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0); std::vector sources; @@ -511,7 +511,7 @@ bool gen_tx_key_image_not_derive_from_tx_key::generate(std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -547,7 +547,7 @@ bool gen_tx_key_image_is_invalid::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -591,7 +591,7 @@ bool gen_tx_check_input_unlock_time::generate(std::vector& eve std::list txs_0; auto make_tx_to_acc = [&](size_t acc_idx, uint64_t unlock_time) { - txs_0.push_back(make_simple_tx_with_unlock_time(events, blk_1, miner_account, accounts[acc_idx], + txs_0.push_back(make_simple_tx_with_unlock_time(events, blk_1r, miner_account, accounts[acc_idx], MK_COINS(1) + TESTS_DEFAULT_FEE, unlock_time)); events.push_back(txs_0.back()); }; @@ -641,7 +641,7 @@ bool gen_tx_txout_to_key_has_invalid_key::generate(std::vector std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -670,7 +670,7 @@ bool gen_tx_output_with_zero_amount::generate(std::vector& eve std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -698,7 +698,7 @@ bool gen_tx_output_is_not_txout_to_key::generate(std::vector& std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0r, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -740,8 +740,8 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); MAKE_ACCOUNT(events, alice_account); MAKE_ACCOUNT(events, bob_account); - MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); - MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); + MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1r); + MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1r); MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0); MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(60), blk_2); diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp index a3b66e835..5378b0254 100644 --- a/tests/core_tests/wallet_tools.cpp +++ b/tests/core_tests/wallet_tools.cpp @@ -4,6 +4,7 @@ #include "wallet_tools.h" #include +#include using namespace std; using namespace epee; @@ -31,10 +32,17 @@ void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::acco void wallet_accessor_test::process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added) { - wallet->process_parsed_blocks(start_height, blocks, parsed_blocks, blocks_added); + if (wallet != nullptr) { + wallet->process_parsed_blocks(start_height, blocks, parsed_blocks, blocks_added); + } } void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional& blk_tail) +{ + process_transactions(boost::assign::list_of(wallet), events, blk_head, bt, blk_tail); +} + +void wallet_tools::process_transactions(const std::vector& wallets, const std::vector& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional& blk_tail) { map_hash2tx_t mtx; std::vector blockchain; @@ -44,10 +52,14 @@ void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vect trim_block_chain(blockchain, blk_tail.get()); } - process_transactions(wallet, blockchain, mtx, bt); + process_transactions(wallets, blockchain, mtx, bt); } -void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt){ + process_transactions(boost::assign::list_of(wallet), blockchain, mtx, bt); +} + +void wallet_tools::process_transactions(const std::vector& wallets, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) { uint64_t start_height=0, blocks_added=0; std::vector v_bche; @@ -67,11 +79,12 @@ void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vect wallet_tools::gen_block_data(bt, bl, mtx, v_bche.back(), v_parsed_block.back(), idx == 1 ? start_height : height); } - if (wallet) + for(auto wallet: wallets) { 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& sources, size_t mixin, const boost::optional& num_utxo, const boost::optional& min_amount, block_tracker &bt, std::vector &selected, uint64_t cur_height, ssize_t offset, int step, const boost::optional& fnc_accept) +bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector& sources, size_t mixin, const boost::optional& num_utxo, const boost::optional& min_amount, block_tracker &bt, std::vector &selected, uint64_t cur_height, ssize_t offset, int step, const boost::optional& fnc_accept, const boost::optional& fnc_in_accept) { CHECK_AND_ASSERT_THROW_MES(step != 0, "Step is zero"); sources.clear(); @@ -84,7 +97,7 @@ bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector= brk_thresh) @@ -106,7 +119,7 @@ bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector cur_height) + if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > cur_height + 1) continue; if (selected_idx.find((size_t)i) != selected_idx.end()){ MERROR("Should not happen (selected_idx not found): " << i); @@ -119,18 +132,12 @@ bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector= brk_thresh; + const auto res = brk_cond >= brk_thresh; + if (!res) { + MDEBUG("fill_tx_sources fails, brk_cond: " << brk_cond << ", brk_thresh: " << brk_thresh << ", utxos: " << cur_utxo << ", sum: " << sum); + } + return res; #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) +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, const boost::optional& fnc_accept) { CHECK_AND_ASSERT_THROW_MES(mixin != 0, "mixin is zero"); src.amount = td.amount(); src.rct = td.is_rct(); std::vector outs; - bt.get_fake_outs(mixin, td.is_rct() ? 0 : td.amount(), td.m_global_output_index, cur_height, outs); + bt.get_fake_outs(mixin, td.is_rct() ? 0 : td.amount(), td.m_global_output_index, cur_height, outs, fnc_accept); for (size_t n = 0; n < mixin; ++n) { diff --git a/tests/core_tests/wallet_tools.h b/tests/core_tests/wallet_tools.h index 1fb8017bf..2201e11f4 100644 --- a/tests/core_tests/wallet_tools.h +++ b/tests/core_tests/wallet_tools.h @@ -68,12 +68,14 @@ public: 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_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt, const boost::optional& fnc_accept = boost::none); 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 &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors); static void process_transactions(tools::wallet2 * wallet, const std::vector& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional& blk_tail=boost::none); + static void process_transactions(const std::vector& wallets, const std::vector& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional& blk_tail=boost::none); static void process_transactions(tools::wallet2 * wallet, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt); - static bool fill_tx_sources(tools::wallet2 * wallet, std::vector& sources, size_t mixin, const boost::optional& num_utxo, const boost::optional& min_amount, block_tracker &bt, std::vector &selected, uint64_t cur_height, ssize_t offset=0, int step=1, const boost::optional& fnc_accept=boost::none); + static void process_transactions(const std::vector& wallets, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt); + static bool fill_tx_sources(tools::wallet2 * wallet, std::vector& sources, size_t mixin, const boost::optional& num_utxo, const boost::optional& min_amount, block_tracker &bt, std::vector &selected, uint64_t cur_height, ssize_t offset=0, int step=1, const boost::optional& fnc_accept=boost::none, const boost::optional& fnc_in_accept = boost::none); }; cryptonote::account_public_address get_address(const tools::wallet2*); From c444a7e002036e834bfb4c68f04a121ce1af5825 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 29 Sep 2023 19:13:37 +0200 Subject: [PATCH 2/2] trezor: support v2.5.2+, add more trezor tests, fix chaingen and tests - passphrase logic: remove backward compatibility for 2.4.3, code cleanup. - fix LibUSB cmake for static builds on OSX - tests: all tests now work with passphrase logic enabled. Passphrase test added with different passphrase. no_passphrase test added, Trezor pin test added. Testing wallet opening with correct and incorrect passphrase. Trezor test chain revamp, cleanup. Smaller chain, chain file versioning added. - tests: Trezor tests support TEST_MINING_ENABLED, TEST_MINING_TIMEOUT env vars to change mining-related tests behaviour. - requires protobuf@21 on osx for now (c++14), building with unlinked protobuf: `CMAKE_PREFIX_PATH=$(find /opt/homebrew/Cellar/protobuf@21 -maxdepth 1 -type d -name "21.*" -print -quit) \ make debug-test-trezor -j8` --- .github/workflows/build.yml | 12 +- .github/workflows/depends.yml | 1 + README.md | 10 + cmake/CheckTrezor.cmake | 224 +++-- cmake/FindLibUSB.cmake | 47 +- contrib/brew/Brewfile | 3 +- contrib/depends/packages/libusb.mk | 3 + contrib/depends/packages/native_protobuf.mk | 7 +- contrib/depends/packages/packages.mk | 8 +- contrib/depends/packages/protobuf.mk | 6 +- .../patches/libusb/fix_osx_avail.patch | 12 + .../depends/patches/protobuf/visibility.patch | 159 ---- contrib/depends/toolchain.cmake.in | 11 +- external/trezor-common | 2 +- src/device_trezor/CMakeLists.txt | 13 +- src/device_trezor/README.md | 74 ++ src/device_trezor/device_trezor.cpp | 11 +- src/device_trezor/device_trezor.hpp | 2 +- src/device_trezor/device_trezor_base.cpp | 31 +- src/device_trezor/device_trezor_base.hpp | 7 +- src/device_trezor/trezor/debug_link.cpp | 2 +- src/device_trezor/trezor/transport.cpp | 15 +- tests/core_tests/chaingen.h | 39 - tests/trezor/daemon.cpp | 7 +- tests/trezor/daemon.h | 2 +- tests/trezor/trezor_tests.cpp | 818 +++++++++++++----- tests/trezor/trezor_tests.h | 107 ++- 27 files changed, 1030 insertions(+), 603 deletions(-) create mode 100644 contrib/depends/patches/libusb/fix_osx_avail.patch delete mode 100644 contrib/depends/patches/protobuf/visibility.patch create mode 100644 src/device_trezor/README.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7aae84977..bf7b24f61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,7 @@ env: CCACHE_SETTINGS: | ccache --max-size=150M ccache --set-config=compression=true + USE_DEVICE_TREZOR_MANDATORY: ON jobs: build-macos: @@ -39,7 +40,9 @@ jobs: key: ccache-${{ runner.os }}-build-${{ github.sha }} restore-keys: ccache-${{ runner.os }}-build- - name: install dependencies - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm miniupnpc expat libunwind-headers protobuf ccache + run: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm miniupnpc expat libunwind-headers protobuf@21 ccache + brew link protobuf@21 - name: build run: | ${{env.CCACHE_SETTINGS}} @@ -65,7 +68,12 @@ jobs: - uses: msys2/setup-msys2@v2 with: update: true - install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-ccache mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb mingw-w64-x86_64-unbound git + install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-ccache mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-libusb mingw-w64-x86_64-unbound git + - shell: msys2 {0} + run: | + curl -O https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst + curl -O https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-protobuf-21.9-1-any.pkg.tar.zst + pacman --noconfirm -U mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst mingw-w64-x86_64-protobuf-21.9-1-any.pkg.tar.zst - name: build run: | ${{env.CCACHE_SETTINGS}} diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml index 27b294503..c7de55cfe 100644 --- a/.github/workflows/depends.yml +++ b/.github/workflows/depends.yml @@ -18,6 +18,7 @@ env: CCACHE_SETTINGS: | ccache --max-size=150M ccache --set-config=compression=true + USE_DEVICE_TREZOR_MANDATORY: ON jobs: build-cross: diff --git a/README.md b/README.md index 3e39bddcd..7d5156232 100644 --- a/README.md +++ b/README.md @@ -590,6 +590,16 @@ Using `depends` might also be easier to compile Monero on Windows than using MSY The produced binaries still link libc dynamically. If the binary is compiled on a current distribution, it might not run on an older distribution with an older installation of libc. Passing `-DBACKCOMPAT=ON` to cmake will make sure that the binary will run on systems having at least libc version 2.17. +### Trezor hardware wallet support + +If you have an issue with building Monero with Trezor support, you can disable it by setting `USE_DEVICE_TREZOR=OFF`, e.g., + +```bash +USE_DEVICE_TREZOR=OFF make release +``` + +For more information, please check out Trezor [src/device_trezor/README.md](src/device_trezor/README.md). + ### Gitian builds See [contrib/gitian/README.md](contrib/gitian/README.md). diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake index f600cc0bb..05b8ebd93 100644 --- a/cmake/CheckTrezor.cmake +++ b/cmake/CheckTrezor.cmake @@ -1,8 +1,27 @@ -OPTION(USE_DEVICE_TREZOR "Trezor support compilation" ON) -OPTION(USE_DEVICE_TREZOR_LIBUSB "Trezor LibUSB compilation" ON) -OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" OFF) -OPTION(USE_DEVICE_TREZOR_DEBUG "Trezor Debugging enabled" OFF) -OPTION(TREZOR_DEBUG "Main trezor debugging switch" OFF) +# Function for setting default options default values via env vars +function(_trezor_default_val val_name val_default) + if(NOT DEFINED ENV{${val_name}}) + set(ENV{${val_name}} ${val_default}) + endif() +endfunction() + +# Define default options via env vars +_trezor_default_val(USE_DEVICE_TREZOR ON) +_trezor_default_val(USE_DEVICE_TREZOR_MANDATORY OFF) +_trezor_default_val(USE_DEVICE_TREZOR_PROTOBUF_TEST ON) +_trezor_default_val(USE_DEVICE_TREZOR_LIBUSB ON) +_trezor_default_val(USE_DEVICE_TREZOR_UDP_RELEASE OFF) +_trezor_default_val(USE_DEVICE_TREZOR_DEBUG OFF) +_trezor_default_val(TREZOR_DEBUG OFF) + +# Main options +OPTION(USE_DEVICE_TREZOR "Trezor support compilation" $ENV{USE_DEVICE_TREZOR}) +OPTION(USE_DEVICE_TREZOR_MANDATORY "Trezor compilation is mandatory, fail build if Trezor support cannot be compiled" $ENV{USE_DEVICE_TREZOR_MANDATORY}) +OPTION(USE_DEVICE_TREZOR_PROTOBUF_TEST "Trezor Protobuf test" $ENV{USE_DEVICE_TREZOR_PROTOBUF_TEST}) +OPTION(USE_DEVICE_TREZOR_LIBUSB "Trezor LibUSB compilation" $ENV{USE_DEVICE_TREZOR_LIBUSB}) +OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" $ENV{USE_DEVICE_TREZOR_UDP_RELEASE}) +OPTION(USE_DEVICE_TREZOR_DEBUG "Trezor Debugging enabled" $ENV{USE_DEVICE_TREZOR_DEBUG}) +OPTION(TREZOR_DEBUG "Main Trezor debugging switch" $ENV{TREZOR_DEBUG}) # Helper function to fix cmake < 3.6.0 FindProtobuf variables function(_trezor_protobuf_fix_vars) @@ -30,33 +49,62 @@ function(_trezor_protobuf_fix_vars) endif() endfunction() +macro(trezor_fatal_msg msg) + if ($ENV{USE_DEVICE_TREZOR_MANDATORY}) + message(FATAL_ERROR + "${msg}\n" + "==========================================================================\n" + "[ERROR] To compile without Trezor support, set USE_DEVICE_TREZOR=OFF. " + "It is possible both via cmake variable and environment variable, e.g., " + "`USE_DEVICE_TREZOR=OFF make release`\n" + "For more information, please check src/device_trezor/README.md\n" + ) + else() + message(WARNING + "${msg}\n" + "==========================================================================\n" + "[WARNING] Trezor support cannot be compiled! Skipping Trezor compilation. \n" + "For more information, please check src/device_trezor/README.md\n") + set(USE_DEVICE_TREZOR OFF) + return() # finish this cmake file processing (as this is macro). + endif() +endmacro() + # Use Trezor master switch if (USE_DEVICE_TREZOR) # Protobuf is required to build protobuf messages for Trezor include(FindProtobuf OPTIONAL) - find_package(Protobuf) + + FIND_PACKAGE(Protobuf CONFIG) + if (NOT Protobuf_FOUND) + FIND_PACKAGE(Protobuf) + endif() + _trezor_protobuf_fix_vars() - # Protobuf handling the cache variables set in docker. - if(NOT Protobuf_FOUND AND NOT Protobuf_LIBRARY AND NOT Protobuf_PROTOC_EXECUTABLE AND NOT Protobuf_INCLUDE_DIR) - message(STATUS "Could not find Protobuf") + # Early fail for optional Trezor support + if(${Protobuf_VERSION} GREATER 21) + trezor_fatal_msg("Trezor: Unsupported Protobuf version ${Protobuf_VERSION}. Please, use Protobuf v21.") + elseif(NOT Protobuf_FOUND AND NOT Protobuf_LIBRARY AND NOT Protobuf_PROTOC_EXECUTABLE AND NOT Protobuf_INCLUDE_DIR) + trezor_fatal_msg("Trezor: Could not find Protobuf") elseif(NOT Protobuf_LIBRARY OR NOT EXISTS "${Protobuf_LIBRARY}") - message(STATUS "Protobuf library not found: ${Protobuf_LIBRARY}") + trezor_fatal_msg("Trezor: Protobuf library not found: ${Protobuf_LIBRARY}") unset(Protobuf_FOUND) elseif(NOT Protobuf_PROTOC_EXECUTABLE OR NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") - message(STATUS "Protobuf executable not found: ${Protobuf_PROTOC_EXECUTABLE}") + trezor_fatal_msg("Trezor: Protobuf executable not found: ${Protobuf_PROTOC_EXECUTABLE}") unset(Protobuf_FOUND) elseif(NOT Protobuf_INCLUDE_DIR OR NOT EXISTS "${Protobuf_INCLUDE_DIR}") - message(STATUS "Protobuf include dir not found: ${Protobuf_INCLUDE_DIR}") + trezor_fatal_msg("Trezor: Protobuf include dir not found: ${Protobuf_INCLUDE_DIR}") unset(Protobuf_FOUND) else() - message(STATUS "Protobuf lib: ${Protobuf_LIBRARY}, inc: ${Protobuf_INCLUDE_DIR}, protoc: ${Protobuf_PROTOC_EXECUTABLE}") + message(STATUS "Trezor: Protobuf lib: ${Protobuf_LIBRARY}, inc: ${Protobuf_INCLUDE_DIR}, protoc: ${Protobuf_PROTOC_EXECUTABLE}") set(Protobuf_INCLUDE_DIRS ${Protobuf_INCLUDE_DIR}) - set(Protobuf_FOUND 1) # override found if all rquired info was provided by variables + set(Protobuf_FOUND 1) # override found if all required info was provided by variables endif() if(TREZOR_DEBUG) set(USE_DEVICE_TREZOR_DEBUG 1) + message(STATUS "Trezor: debug build enabled") endif() # Compile debugging support (for tests) @@ -64,7 +112,7 @@ if (USE_DEVICE_TREZOR) add_definitions(-DWITH_TREZOR_DEBUGGING=1) endif() else() - message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR") + message(STATUS "Trezor: support disabled by USE_DEVICE_TREZOR") endif() if(Protobuf_FOUND AND USE_DEVICE_TREZOR) @@ -85,7 +133,7 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR) endif() if(NOT TREZOR_PYTHON) - message(STATUS "Trezor: Python not found") + trezor_fatal_msg("Trezor: Python not found") endif() endif() @@ -93,27 +141,42 @@ endif() if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) execute_process(COMMAND ${Protobuf_PROTOC_EXECUTABLE} -I "${CMAKE_CURRENT_LIST_DIR}" -I "${Protobuf_INCLUDE_DIR}" "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.proto" --cpp_out ${CMAKE_BINARY_DIR} RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) if(RET) - message(STATUS "Protobuf test generation failed: ${OUT} ${ERR}") + trezor_fatal_msg("Trezor: Protobuf test generation failed: ${OUT} ${ERR}") endif() - try_compile(Protobuf_COMPILE_TEST_PASSED - "${CMAKE_BINARY_DIR}" - SOURCES - "${CMAKE_BINARY_DIR}/test-protobuf.pb.cc" - "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.cpp" - CMAKE_FLAGS - "-DINCLUDE_DIRECTORIES=${Protobuf_INCLUDE_DIR};${CMAKE_BINARY_DIR}" - "-DCMAKE_CXX_STANDARD=11" - LINK_LIBRARIES ${Protobuf_LIBRARY} - OUTPUT_VARIABLE OUTPUT - ) - if(NOT Protobuf_COMPILE_TEST_PASSED) - message(STATUS "Protobuf Compilation test failed: ${OUTPUT}.") + if(ANDROID) + set(CMAKE_TRY_COMPILE_LINKER_FLAGS "${CMAKE_TRY_COMPILE_LINKER_FLAGS} -llog") + set(CMAKE_TRY_COMPILE_LINK_LIBRARIES "${CMAKE_TRY_COMPILE_LINK_LIBRARIES} log") + endif() + + if(USE_DEVICE_TREZOR_PROTOBUF_TEST) + # For now, Protobuf v21 is the maximum supported version as v23 requires C++17. TODO: Remove once we move to C++17 + if(${Protobuf_VERSION} GREATER 21) + trezor_fatal_msg("Trezor: Unsupported Protobuf version ${Protobuf_VERSION}. Please, use Protobuf v21.") + endif() + + try_compile(Protobuf_COMPILE_TEST_PASSED + "${CMAKE_BINARY_DIR}" + SOURCES + "${CMAKE_BINARY_DIR}/test-protobuf.pb.cc" + "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.cpp" + CMAKE_FLAGS + CMAKE_EXE_LINKER_FLAGS ${CMAKE_TRY_COMPILE_LINKER_FLAGS} + "-DINCLUDE_DIRECTORIES=${Protobuf_INCLUDE_DIR};${CMAKE_BINARY_DIR}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + LINK_LIBRARIES ${Protobuf_LIBRARY} ${CMAKE_TRY_COMPILE_LINK_LIBRARIES} + OUTPUT_VARIABLE OUTPUT + ) + if(NOT Protobuf_COMPILE_TEST_PASSED) + trezor_fatal_msg("Trezor: Protobuf Compilation test failed: ${OUTPUT}.") + endif() + else () + message(STATUS "Trezor: Protobuf Compilation test skipped, build may fail later") endif() endif() # Try to build protobuf messages -if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON AND Protobuf_COMPILE_TEST_PASSED) +if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIR}") set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}") set(TREZOR_PROTOBUF_PARAMS "") @@ -123,59 +186,62 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON AND Protobuf_COMPILE_T execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py ${TREZOR_PROTOBUF_PARAMS} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) if(RET) - message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." + trezor_fatal_msg("Trezor: protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." "OUT: ${OUT}, ERR: ${ERR}." "Please read src/device_trezor/trezor/tools/README.md") - else() - message(STATUS "Trezor protobuf messages regenerated out: \"${OUT}.\"") - set(DEVICE_TREZOR_READY 1) - add_definitions(-DDEVICE_TREZOR_READY=1) - add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0) + endif() - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_definitions(-DTREZOR_DEBUG=1) + message(STATUS "Trezor: protobuf messages regenerated out: \"${OUT}.\"") + set(DEVICE_TREZOR_READY 1) + add_definitions(-DDEVICE_TREZOR_READY=1) + add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DTREZOR_DEBUG=1) + endif() + + if(USE_DEVICE_TREZOR_UDP_RELEASE) + message(STATUS "Trezor: UDP transport enabled (emulator)") + add_definitions(-DUSE_DEVICE_TREZOR_UDP_RELEASE=1) + endif() + + if (Protobuf_INCLUDE_DIR) + include_directories(${Protobuf_INCLUDE_DIR}) + endif() + + # LibUSB support, check for particular version + # Include support only if compilation test passes + if (USE_DEVICE_TREZOR_LIBUSB) + find_package(LibUSB) + endif() + + if (LibUSB_COMPILE_TEST_PASSED) + add_definitions(-DHAVE_TREZOR_LIBUSB=1) + if(LibUSB_INCLUDE_DIRS) + include_directories(${LibUSB_INCLUDE_DIRS}) + endif() + endif() + + set(TREZOR_LIBUSB_LIBRARIES "") + if(LibUSB_COMPILE_TEST_PASSED) + list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES} ${LIBUSB_DEP_LINKER}) + message(STATUS "Trezor: compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}") + elseif(USE_DEVICE_TREZOR_LIBUSB AND NOT ANDROID) + trezor_fatal_msg("Trezor: LibUSB not found or test failed, please install libusb-1.0.26") + endif() + + if (BUILD_GUI_DEPS) + set(TREZOR_DEP_LIBS "") + set(TREZOR_DEP_LINKER "") + + if (Protobuf_LIBRARY) + list(APPEND TREZOR_DEP_LIBS ${Protobuf_LIBRARY}) + string(APPEND TREZOR_DEP_LINKER " -lprotobuf") endif() - if(USE_DEVICE_TREZOR_UDP_RELEASE) - add_definitions(-DUSE_DEVICE_TREZOR_UDP_RELEASE=1) - endif() - - if (Protobuf_INCLUDE_DIR) - include_directories(${Protobuf_INCLUDE_DIR}) - endif() - - # LibUSB support, check for particular version - # Include support only if compilation test passes - if (USE_DEVICE_TREZOR_LIBUSB) - find_package(LibUSB) - endif() - - if (LibUSB_COMPILE_TEST_PASSED) - add_definitions(-DHAVE_TREZOR_LIBUSB=1) - if(LibUSB_INCLUDE_DIRS) - include_directories(${LibUSB_INCLUDE_DIRS}) - endif() - endif() - - set(TREZOR_LIBUSB_LIBRARIES "") - if(LibUSB_COMPILE_TEST_PASSED) - list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES} ${LIBUSB_DEP_LINKER}) - message(STATUS "Trezor compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}") - endif() - - if (BUILD_GUI_DEPS) - set(TREZOR_DEP_LIBS "") - set(TREZOR_DEP_LINKER "") - - if (Protobuf_LIBRARY) - list(APPEND TREZOR_DEP_LIBS ${Protobuf_LIBRARY}) - string(APPEND TREZOR_DEP_LINKER " -lprotobuf") - endif() - - if (TREZOR_LIBUSB_LIBRARIES) - list(APPEND TREZOR_DEP_LIBS ${TREZOR_LIBUSB_LIBRARIES}) - string(APPEND TREZOR_DEP_LINKER " -lusb-1.0 ${LIBUSB_DEP_LINKER}") - endif() + if (TREZOR_LIBUSB_LIBRARIES) + list(APPEND TREZOR_DEP_LIBS ${TREZOR_LIBUSB_LIBRARIES}) + string(APPEND TREZOR_DEP_LINKER " -lusb-1.0 ${LIBUSB_DEP_LINKER}") endif() endif() endif() diff --git a/cmake/FindLibUSB.cmake b/cmake/FindLibUSB.cmake index f780628f8..f701b6398 100644 --- a/cmake/FindLibUSB.cmake +++ b/cmake/FindLibUSB.cmake @@ -95,11 +95,35 @@ if ( LibUSB_FOUND ) endif ( LibUSB_FOUND ) if ( LibUSB_FOUND ) + if (APPLE) + if(DEPENDS) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit -framework Security") + else() + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + find_library(SECURITY_FRAMEWORK Security) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${IOKIT}) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${COREFOUNDATION}) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${SECURITY_FRAMEWORK}) + + if(STATIC) + find_library(OBJC objc.a) + set(LIBUSB_DEP_LINKER ${OBJC}) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LIBUSB_DEP_LINKER}) + endif() + endif() + endif() + if (WIN32) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES setupapi) + endif() + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LibUSB_LIBRARIES}) + set(CMAKE_REQUIRED_LIBRARIES ${TEST_COMPILE_EXTRA_LIBRARIES}) + check_library_exists ( "${LibUSB_LIBRARIES}" usb_open "" LibUSB_FOUND ) check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_device_list "" LibUSB_VERSION_1.0 ) check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_port_numbers "" LibUSB_VERSION_1.0.16 ) - if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux") OR ANDROID) + if((STATIC AND UNIX AND NOT APPLE AND NOT FREEBSD) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux") OR ANDROID) find_library(LIBUDEV_LIBRARY udev) if(LIBUDEV_LIBRARY) set(LibUSB_LIBRARIES "${LibUSB_LIBRARIES};${LIBUDEV_LIBRARY}") @@ -111,27 +135,6 @@ if ( LibUSB_FOUND ) # Library 1.0.16+ compilation test. # The check_library_exists does not work well on Apple with shared libs. if (APPLE OR LibUSB_VERSION_1.0.16 OR STATIC) - if (APPLE) - if(DEPENDS) - list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit -framework Security") - else() - find_library(COREFOUNDATION CoreFoundation) - find_library(IOKIT IOKit) - list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${IOKIT}) - list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${COREFOUNDATION}) - - if(STATIC) - find_library(OBJC objc.a) - set(LIBUSB_DEP_LINKER ${OBJC}) - list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LIBUSB_DEP_LINKER}) - endif() - endif() - endif() - if (WIN32) - list(APPEND TEST_COMPILE_EXTRA_LIBRARIES setupapi) - endif() - list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LibUSB_LIBRARIES}) - try_compile(LibUSB_COMPILE_TEST_PASSED ${CMAKE_BINARY_DIR} "${CMAKE_CURRENT_LIST_DIR}/test-libusb-version.c" diff --git a/contrib/brew/Brewfile b/contrib/brew/Brewfile index c74e7b2a2..c2fae0ffc 100644 --- a/contrib/brew/Brewfile +++ b/contrib/brew/Brewfile @@ -31,4 +31,5 @@ brew "doxygen" brew "graphviz" brew "libunwind-headers" brew "xz" -brew "protobuf" +brew "protobuf@21", link: true +brew "libusb" diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk index c1d9fe6a9..fb6646685 100644 --- a/contrib/depends/packages/libusb.mk +++ b/contrib/depends/packages/libusb.mk @@ -3,8 +3,10 @@ $(package)_version=1.0.26 $(package)_download_path=https://github.com/libusb/libusb/releases/download/v$($(package)_version) $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=12ce7a61fc9854d1d2a1ffe095f7b5fac19ddba095c259e6067a46500381b5a5 +$(package)_patches=fix_osx_avail.patch define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/fix_osx_avail.patch &&\ autoreconf -i endef @@ -13,6 +15,7 @@ define $(package)_set_vars $(package)_config_opts_linux=--with-pic --disable-udev $(package)_config_opts_mingw32=--disable-udev $(package)_config_opts_darwin=--disable-udev + $(package)_config_opts_freebsd=--with-pic --disable-udev endef ifneq ($(host_os),darwin) diff --git a/contrib/depends/packages/native_protobuf.mk b/contrib/depends/packages/native_protobuf.mk index 35f648b9a..2dc11b23c 100644 --- a/contrib/depends/packages/native_protobuf.mk +++ b/contrib/depends/packages/native_protobuf.mk @@ -1,8 +1,9 @@ package=protobuf3 -$(package)_version=3.6.1 +$(package)_version=21.12 +$(package)_version_protobuf_cpp=3.21.12 $(package)_download_path=https://github.com/protocolbuffers/protobuf/releases/download/v$($(package)_version)/ -$(package)_file_name=protobuf-cpp-$($(package)_version).tar.gz -$(package)_sha256_hash=b3732e471a9bb7950f090fd0457ebd2536a9ba0891b7f3785919c654fe2a2529 +$(package)_file_name=protobuf-cpp-$($(package)_version_protobuf_cpp).tar.gz +$(package)_sha256_hash=4eab9b524aa5913c6fffb20b2a8abf5ef7f95a80bc0701f3a6dbb4c607f73460 $(package)_cxxflags=-std=c++11 define $(package)_set_vars diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index ea4a0effd..35dc2abc7 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -8,15 +8,15 @@ endif hardware_packages := hidapi protobuf libusb hardware_native_packages := native_protobuf -android_native_packages = android_ndk -android_packages = ncurses readline sodium +android_native_packages = android_ndk $(hardware_native_packages) +android_packages = ncurses readline sodium protobuf darwin_native_packages = $(hardware_native_packages) darwin_packages = ncurses readline sodium $(hardware_packages) # not really native... -freebsd_native_packages = freebsd_base -freebsd_packages = ncurses readline sodium +freebsd_native_packages = freebsd_base $(hardware_native_packages) +freebsd_packages = ncurses readline sodium protobuf libusb linux_packages = eudev ncurses readline sodium $(hardware_packages) linux_native_packages = $(hardware_native_packages) diff --git a/contrib/depends/packages/protobuf.mk b/contrib/depends/packages/protobuf.mk index ddec1eb59..780357c90 100644 --- a/contrib/depends/packages/protobuf.mk +++ b/contrib/depends/packages/protobuf.mk @@ -1,21 +1,17 @@ package=protobuf $(package)_version=$(native_$(package)_version) +$(package)_version_protobuf_cpp=$(native_$(package)_version_protobuf_cpp) $(package)_download_path=$(native_$(package)_download_path) $(package)_file_name=$(native_$(package)_file_name) $(package)_sha256_hash=$(native_$(package)_sha256_hash) $(package)_dependencies=native_$(package) $(package)_cxxflags=-std=c++11 -$(package)_patches=visibility.patch define $(package)_set_vars $(package)_config_opts=--disable-shared --with-protoc=$(build_prefix)/bin/protoc $(package)_config_opts_linux=--with-pic endef -define $(package)_preprocess_cmds - patch -p0 < $($(package)_patch_dir)/visibility.patch -endef - define $(package)_config_cmds $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef diff --git a/contrib/depends/patches/libusb/fix_osx_avail.patch b/contrib/depends/patches/libusb/fix_osx_avail.patch new file mode 100644 index 000000000..2474993f5 --- /dev/null +++ b/contrib/depends/patches/libusb/fix_osx_avail.patch @@ -0,0 +1,12 @@ +--- a/libusb/os/darwin_usb.h 2023-03-19 12:07:53 ++++ b/libusb/os/darwin_usb.h 2023-03-19 12:07:47 +@@ -165,7 +165,8 @@ + #define __has_builtin(x) 0 // Compatibility with non-clang compilers. + #endif + #if __has_builtin(__builtin_available) +- #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *) ++// #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *) ++ #define HAS_CAPTURE_DEVICE() 0 + #else + #define HAS_CAPTURE_DEVICE() 0 + #endif diff --git a/contrib/depends/patches/protobuf/visibility.patch b/contrib/depends/patches/protobuf/visibility.patch deleted file mode 100644 index e66d5961f..000000000 --- a/contrib/depends/patches/protobuf/visibility.patch +++ /dev/null @@ -1,159 +0,0 @@ ---- src/google/protobuf/descriptor.cc.O 2018-07-30 22:16:10.000000000 +0000 -+++ src/google/protobuf/descriptor.cc 2022-05-06 13:38:14.827309092 +0000 -@@ -32,6 +32,9 @@ - // Based on original Protocol Buffers design by - // Sanjay Ghemawat, Jeff Dean, and others. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif - #include - #include - #include -@@ -7274,3 +7277,6 @@ - - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/extension_set.cc.O 2018-07-23 20:56:42.000000000 +0000 -+++ src/google/protobuf/extension_set.cc 2022-05-06 14:48:55.369877050 +0000 -@@ -32,6 +32,9 @@ - // Based on original Protocol Buffers design by - // Sanjay Ghemawat, Jeff Dean, and others. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif - #include - #include - #include -@@ -1914,3 +1917,6 @@ - } // namespace internal - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/extension_set_heavy.cc.O 2018-07-30 22:16:10.000000000 +0000 -+++ src/google/protobuf/extension_set_heavy.cc 2022-05-06 14:14:27.847320946 +0000 -@@ -35,6 +35,10 @@ - // Contains methods defined in extension_set.h which cannot be part of the - // lite library because they use descriptors or reflection. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif -+ - #include - #include - #include -@@ -814,3 +818,6 @@ - } // namespace internal - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/generated_message_reflection.cc.O 2018-07-23 20:56:42.000000000 +0000 -+++ src/google/protobuf/generated_message_reflection.cc 2022-05-06 13:38:49.655540772 +0000 -@@ -32,6 +32,9 @@ - // Based on original Protocol Buffers design by - // Sanjay Ghemawat, Jeff Dean, and others. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif - #include - #include - -@@ -2420,3 +2423,6 @@ - } // namespace internal - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/map_field.cc.O 2018-07-23 20:56:42.000000000 +0000 -+++ src/google/protobuf/map_field.cc 2022-05-06 13:34:44.913905697 +0000 -@@ -28,6 +28,10 @@ - // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif -+ - #include - #include - -@@ -462,3 +466,6 @@ - } // namespace internal - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/text_format.cc.O 2018-07-30 22:16:11.000000000 +0000 -+++ src/google/protobuf/text_format.cc 2022-05-06 13:34:58.881999517 +0000 -@@ -32,6 +32,10 @@ - // Based on original Protocol Buffers design by - // Sanjay Ghemawat, Jeff Dean, and others. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif -+ - #include - #include - #include -@@ -2258,3 +2262,6 @@ - - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/wire_format.cc.O 2018-07-23 20:56:42.000000000 +0000 -+++ src/google/protobuf/wire_format.cc 2022-05-06 13:06:23.294219228 +0000 -@@ -32,6 +32,10 @@ - // Based on original Protocol Buffers design by - // Sanjay Ghemawat, Jeff Dean, and others. - -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif -+ - #include - #include - #include -@@ -1445,3 +1449,7 @@ - } // namespace internal - } // namespace protobuf - } // namespace google -+ -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif ---- src/google/protobuf/stubs/status.cc.O 2018-07-23 20:56:42.000000000 +0000 -+++ src/google/protobuf/stubs/status.cc 2022-05-06 15:18:53.393208814 +0000 -@@ -27,6 +27,11 @@ - // 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. -+ -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility push(hidden) -+#endif -+ - #include - - #include -@@ -132,3 +137,6 @@ - } // namespace util - } // namespace protobuf - } // namespace google -+#if defined(__APPLE__) && defined(__arm64__) -+#pragma GCC visibility pop -+#endif diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 570065560..ba0119456 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -27,12 +27,6 @@ SET(Terminfo_LIBRARY @prefix@/lib/libtinfo.a) SET(UNBOUND_INCLUDE_DIR @prefix@/include) SET(UNBOUND_LIBRARIES @prefix@/lib/libunbound.a) -if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") -SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) -SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) -SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) - -if(NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") SET(LIBUSB-1.0_LIBRARY @prefix@/lib/libusb-1.0.a) SET(LIBUDEV_LIBRARY @prefix@/lib/libudev.a) @@ -41,8 +35,11 @@ SET(Protobuf_PROTOC_EXECUTABLE @prefix@/native/bin/protoc CACHE FILEPATH "Path t SET(Protobuf_INCLUDE_DIR @prefix@/include CACHE PATH "Protobuf include dir") SET(Protobuf_INCLUDE_DIRS @prefix@/include CACHE PATH "Protobuf include dir") SET(Protobuf_LIBRARY @prefix@/lib/libprotobuf.a CACHE FILEPATH "Protobuf library") -endif() +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") +SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) +SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) +SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) endif() SET(ZMQ_INCLUDE_PATH @prefix@/include) diff --git a/external/trezor-common b/external/trezor-common index bff7fdfe4..bc28c316d 160000 --- a/external/trezor-common +++ b/external/trezor-common @@ -1 +1 @@ -Subproject commit bff7fdfe436c727982cc553bdfb29a9021b423b0 +Subproject commit bc28c316d05bf1e9ebfe3d7df1ab25831d98d168 diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index c30fb2b16..2d5614507 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -65,12 +65,16 @@ set(trezor_private_headers) # Protobuf and LibUSB processed by CheckTrezor if(DEVICE_TREZOR_READY) - message(STATUS "Trezor support enabled") + message(STATUS "Trezor: support enabled") if(USE_DEVICE_TREZOR_DEBUG) list(APPEND trezor_headers trezor/debug_link.hpp trezor/messages/messages-debug.pb.h) list(APPEND trezor_sources trezor/debug_link.cpp trezor/messages/messages-debug.pb.cc) - message(STATUS "Trezor debugging enabled") + message(STATUS "Trezor: debugging enabled") + endif() + + if(ANDROID) + set(TREZOR_EXTRA_LIBRARIES "log") endif() monero_private_headers(device_trezor @@ -93,10 +97,11 @@ if(DEVICE_TREZOR_READY) ${Protobuf_LIBRARY} ${TREZOR_LIBUSB_LIBRARIES} PRIVATE - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + ${TREZOR_EXTRA_LIBRARIES}) else() - message(STATUS "Trezor support disabled") + message(STATUS "Trezor: support disabled") monero_private_headers(device_trezor) monero_add_library(device_trezor device_trezor.cpp) target_link_libraries(device_trezor PUBLIC cncrypto) diff --git a/src/device_trezor/README.md b/src/device_trezor/README.md new file mode 100644 index 000000000..ce08c0009 --- /dev/null +++ b/src/device_trezor/README.md @@ -0,0 +1,74 @@ +# Trezor hardware wallet support + +This module adds [Trezor] hardware support to Monero. + + +## Basic information + +Trezor integration is based on the following original proposal: https://github.com/ph4r05/monero-trezor-doc + +A custom high-level transaction signing protocol uses Trezor in a similar way a cold wallet is used. +Transaction is build incrementally on the device. + +Trezor implements the signing protocol in [trezor-firmware] repository, in the [monero](https://github.com/trezor/trezor-firmware/tree/master/core/src/apps/monero) application. +Please, refer to [monero readme](https://github.com/trezor/trezor-firmware/blob/master/core/src/apps/monero/README.md) for more information. + +## Dependencies + +Trezor uses [Protobuf](https://protobuf.dev/) library. As Monero is compiled with C++14, the newest Protobuf library version cannot be compiled because it requires C++17 (through its dependency Abseil library). +This can result in a compilation failure. + +Protobuf v21 is the latest compatible protobuf version. + +If you want to compile Monero with Trezor support, please make sure the Protobuf v21 is installed. + +More about this limitation: [PR #8752](https://github.com/monero-project/monero/pull/8752), +[1](https://github.com/monero-project/monero/pull/8752#discussion_r1246174755), [2](https://github.com/monero-project/monero/pull/8752#discussion_r1246480393) + +### OSX + +To build with installed, but not linked protobuf: + +```bash +CMAKE_PREFIX_PATH=$(find /opt/homebrew/Cellar/protobuf@21 -maxdepth 1 -type d -name "21.*" -print -quit) \ +make release +``` + +or to install and link as a default protobuf version: +```bash +# Either install all requirements as +brew update && brew bundle --file=contrib/brew/Brewfile + +# or install protobufv21 specifically +brew install protobuf@21 && brew link protobuf@21 +``` + +### MSYS32 + +```bash +curl -O https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst +curl -O https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-protobuf-21.9-1-any.pkg.tar.zst +pacman --noconfirm -U mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst mingw-w64-x86_64-protobuf-21.9-1-any.pkg.tar.zst +``` + +### Other systems + +- install protobufv21 +- point `CMAKE_PREFIX_PATH` environment variable to Protobuf v21 installation. + +## Troubleshooting + +To disable Trezor support, set `USE_DEVICE_TREZOR=OFF`, e.g.: + +```shell +USE_DEVICE_TREZOR=OFF make release +``` + +## Resources: + +- First pull request https://github.com/monero-project/monero/pull/4241 +- Integration proposal https://github.com/ph4r05/monero-trezor-doc +- Integration readme in trezor-firmware https://github.com/trezor/trezor-firmware/blob/master/core/src/apps/monero/README.md + +[Trezor]: https://trezor.io/ +[trezor-firmware]: https://github.com/trezor/trezor-firmware/ \ No newline at end of file diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 9c8148ed6..fa1e7c088 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -165,7 +165,7 @@ namespace trezor { auto res = get_address(); cryptonote::address_parse_info info{}; - bool r = cryptonote::get_account_address_from_str(info, this->network_type, res->address()); + bool r = cryptonote::get_account_address_from_str(info, this->m_network_type, res->address()); CHECK_AND_ASSERT_MES(r, false, "Could not parse returned address. Address parse failed: " + res->address()); CHECK_AND_ASSERT_MES(!info.is_subaddress, false, "Trezor returned a sub address"); @@ -693,14 +693,11 @@ namespace trezor { unsigned device_trezor::client_version() { auto trezor_version = get_version(); - if (trezor_version < pack_version(2, 4, 3)){ - throw exc::TrezorException("Minimal Trezor firmware version is 2.4.3. Please update."); + if (trezor_version < pack_version(2, 5, 2)){ + throw exc::TrezorException("Minimal Trezor firmware version is 2.5.2. Please update."); } - unsigned client_version = 3; - if (trezor_version >= pack_version(2, 5, 2)){ - client_version = 4; - } + unsigned client_version = 4; // since 2.5.2 #ifdef WITH_TREZOR_DEBUGGING // Override client version for tests diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index 35bb78927..804ed62c8 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -102,7 +102,7 @@ namespace trezor { bool has_ki_cold_sync() const override { return true; } bool has_tx_cold_sign() const override { return true; } - void set_network_type(cryptonote::network_type network_type) override { this->network_type = network_type; } + void set_network_type(cryptonote::network_type network_type) override { this->m_network_type = network_type; } void set_live_refresh_enabled(bool enabled) { m_live_refresh_enabled = enabled; } bool live_refresh_enabled() const { return m_live_refresh_enabled; } diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index a8a3d9f67..f65870be5 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -300,9 +300,6 @@ namespace trezor { case messages::MessageType_PassphraseRequest: on_passphrase_request(input, dynamic_cast(input.m_msg.get())); return true; - case messages::MessageType_Deprecated_PassphraseStateRequest: - on_passphrase_state_request(input, dynamic_cast(input.m_msg.get())); - return true; case messages::MessageType_PinMatrixRequest: on_pin_request(input, dynamic_cast(input.m_msg.get())); return true; @@ -475,21 +472,9 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); MDEBUG("on_passhprase_request"); - // Backward compatibility, migration clause. - if (msg->has__on_device() && msg->_on_device()){ - messages::common::PassphraseAck m; - resp = call_raw(&m); - return; - } - m_seen_passphrase_entry_message = true; - bool on_device = true; - if (msg->has__on_device() && !msg->_on_device()){ - on_device = false; // do not enter on device, old devices. - } - - if (on_device && m_features && m_features->capabilities_size() > 0){ - on_device = false; + bool on_device = false; + if (m_features){ for (auto it = m_features->capabilities().begin(); it != m_features->capabilities().end(); it++) { if (*it == messages::management::Features::Capability_PassphraseEntry){ on_device = true; @@ -526,18 +511,6 @@ namespace trezor { resp = call_raw(&m); } - void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::Deprecated_PassphraseStateRequest * msg) - { - MDEBUG("on_passhprase_state_request"); - CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - - if (msg->has_state()) { - m_device_session_id = msg->state(); - } - messages::common::Deprecated_PassphraseStateAck m; - resp = call_raw(&m); - } - #ifdef WITH_TREZOR_DEBUGGING void device_trezor_base::wipe_device() { diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 3ec21e157..df6e42b1f 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -100,7 +100,7 @@ namespace trezor { boost::optional m_passphrase; messages::MessageType m_last_msg_type; - cryptonote::network_type network_type; + cryptonote::network_type m_network_type; bool m_reply_with_empty_passphrase; bool m_always_use_empty_passphrase; bool m_seen_passphrase_entry_message; @@ -227,9 +227,9 @@ namespace trezor { } if (network_type){ - msg->set_network_type(static_cast(network_type.get())); + msg->set_network_type(static_cast(network_type.get())); } else { - msg->set_network_type(static_cast(this->network_type)); + msg->set_network_type(static_cast(this->m_network_type)); } } @@ -318,7 +318,6 @@ namespace trezor { void on_button_pressed(); void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg); - void on_passphrase_state_request(GenericMessage & resp, const messages::common::Deprecated_PassphraseStateRequest * msg); #ifdef WITH_TREZOR_DEBUGGING void set_debug(bool debug){ diff --git a/src/device_trezor/trezor/debug_link.cpp b/src/device_trezor/trezor/debug_link.cpp index ff7cf0ad6..76adcb164 100644 --- a/src/device_trezor/trezor/debug_link.cpp +++ b/src/device_trezor/trezor/debug_link.cpp @@ -67,7 +67,7 @@ namespace trezor{ void DebugLink::input_button(bool button){ messages::debug::DebugLinkDecision decision; - decision.set_yes_no(button); + decision.set_button(button ? messages::debug::DebugLinkDecision_DebugButton_YES : messages::debug::DebugLinkDecision_DebugButton_NO); call(decision, boost::none, true); } diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 8c6b30787..23a04f9c7 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -154,6 +154,7 @@ namespace trezor{ // Helpers // +#define PROTO_MAGIC_SIZE 3 #define PROTO_HEADER_SIZE 6 static size_t message_size(const google::protobuf::Message &req){ @@ -193,7 +194,7 @@ namespace trezor{ } serialize_message_header(buff, msg_wire_num, msg_size); - if (!req.SerializeToArray(buff + 6, msg_size)){ + if (!req.SerializeToArray(buff + PROTO_HEADER_SIZE, msg_size)){ throw exc::EncodingException("Message serialization error"); } } @@ -252,16 +253,16 @@ namespace trezor{ throw exc::CommunicationException("Read chunk has invalid size"); } - if (memcmp(chunk_buff_raw, "?##", 3) != 0){ + if (memcmp(chunk_buff_raw, "?##", PROTO_MAGIC_SIZE) != 0){ throw exc::CommunicationException("Malformed chunk"); } uint16_t tag; uint32_t len; - nread -= 3 + 6; - deserialize_message_header(chunk_buff_raw + 3, tag, len); + nread -= PROTO_MAGIC_SIZE + PROTO_HEADER_SIZE; + deserialize_message_header(chunk_buff_raw + PROTO_MAGIC_SIZE, tag, len); - epee::wipeable_string data_acc(chunk_buff_raw + 3 + 6, nread); + epee::wipeable_string data_acc(chunk_buff_raw + PROTO_MAGIC_SIZE + PROTO_HEADER_SIZE, nread); data_acc.reserve(len); while(nread < len){ @@ -482,7 +483,7 @@ namespace trezor{ uint16_t msg_tag; uint32_t msg_len; deserialize_message_header(bin_data->data(), msg_tag, msg_len); - if (bin_data->size() != msg_len + 6){ + if (bin_data->size() != msg_len + PROTO_HEADER_SIZE){ throw exc::CommunicationException("Response is not well hexcoded"); } @@ -491,7 +492,7 @@ namespace trezor{ } std::shared_ptr msg_wrap(MessageMapper::get_message(msg_tag)); - if (!msg_wrap->ParseFromArray(bin_data->data() + 6, msg_len)){ + if (!msg_wrap->ParseFromArray(bin_data->data() + PROTO_HEADER_SIZE, msg_len)){ throw exc::EncodingException("Response is not well hexcoded"); } msg = msg_wrap; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 4d990f439..41f7275d3 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -913,15 +913,6 @@ 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 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); \ @@ -946,18 +937,12 @@ inline bool do_replay_file(const std::string& filename) #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) \ @@ -968,36 +953,12 @@ inline bool do_replay_file(const std::string& filename) 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 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 SET_NAME; \ - MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD); - #define MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(TX, BLK, HF_VERSION, KEY) \ transaction TX; \ if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \ diff --git a/tests/trezor/daemon.cpp b/tests/trezor/daemon.cpp index 47c0cde9b..de4f9bc51 100644 --- a/tests/trezor/daemon.cpp +++ b/tests/trezor/daemon.cpp @@ -309,11 +309,10 @@ void mock_daemon::stop_p2p() m_server.send_stop_signal(); } -void mock_daemon::mine_blocks(size_t num_blocks, const std::string &miner_address) +void mock_daemon::mine_blocks(size_t num_blocks, const std::string &miner_address, std::chrono::seconds timeout) { bool blocks_mined = false; const uint64_t start_height = get_height(); - const auto mining_timeout = std::chrono::seconds(360); MDEBUG("Current height before mining: " << start_height); start_mining(miner_address); @@ -331,14 +330,14 @@ void mock_daemon::mine_blocks(size_t num_blocks, const std::string &miner_addres } auto current_time = std::chrono::system_clock::now(); - if (mining_timeout < current_time - mining_started) + if (timeout < current_time - mining_started) { break; } } stop_mining(); - CHECK_AND_ASSERT_THROW_MES(blocks_mined, "Mining failed in the time limit"); + CHECK_AND_ASSERT_THROW_MES(blocks_mined, "Mining failed in the time limit: " << timeout.count()); } constexpr const std::chrono::seconds mock_daemon::rpc_timeout; diff --git a/tests/trezor/daemon.h b/tests/trezor/daemon.h index b87fd5036..3c0b6b78b 100644 --- a/tests/trezor/daemon.h +++ b/tests/trezor/daemon.h @@ -139,7 +139,7 @@ public: void stop_and_deinit(); void try_init_and_run(boost::optional initial_port=boost::none); - void mine_blocks(size_t num_blocks, const std::string &miner_address); + void mine_blocks(size_t num_blocks, const std::string &miner_address, std::chrono::seconds timeout = std::chrono::seconds(360)); void start_mining(const std::string &miner_address, uint64_t threads_count=1, bool do_background_mining=false, bool ignore_battery=true); void stop_mining(); uint64_t get_height(); diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp index 10c52ff29..b21da95c0 100644 --- a/tests/trezor/trezor_tests.cpp +++ b/tests/trezor/trezor_tests.cpp @@ -37,6 +37,7 @@ using namespace cryptonote; +#include #include #include #include "common/util.h" @@ -51,18 +52,20 @@ namespace po = boost::program_options; namespace { - const command_line::arg_descriptor arg_filter = { "filter", "Regular expression filter for which tests to run" }; - const command_line::arg_descriptor arg_trezor_path = {"trezor_path", "Path to the trezor device to use, has to support debug link", ""}; - const command_line::arg_descriptor arg_heavy_tests = {"heavy_tests", "Runs expensive tests (volume tests with real device)", false}; - const command_line::arg_descriptor arg_chain_path = {"chain_path", "Path to the serialized blockchain, speeds up testing", ""}; - const command_line::arg_descriptor arg_fix_chain = {"fix_chain", "If chain_patch is given and file cannot be used, it is ignored and overwriten", false}; + const command_line::arg_descriptor arg_filter = {"filter", "Regular expression filter for which tests to run" }; + const command_line::arg_descriptor arg_trezor_path = {"trezor-path", "Path to the trezor device to use, has to support debug link", ""}; + const command_line::arg_descriptor arg_heavy_tests = {"heavy-tests", "Runs expensive tests (volume tests)", false}; + const command_line::arg_descriptor arg_chain_path = {"chain-path", "Path to the serialized blockchain, speeds up testing", ""}; + const command_line::arg_descriptor arg_fix_chain = {"fix-chain", "If chain-patch is given and file cannot be used, it is ignored and overwriten", false}; } #define HW_TREZOR_NAME "Trezor" -#define TREZOR_ACCOUNT_ORDERING &m_miner_account, &m_alice_account, &m_bob_account, &m_eve_account -#define TREZOR_COMMON_TEST_CASE(genclass, CORE, BASE) do { \ - rollback_chain(CORE, BASE.head_block()); \ - { \ +#define TEST_DECOY_MINING_COUNTS 3 +#define TREZOR_ACCOUNT_ORDERING &m_miner_account, &m_alice_account, &m_alice2_account, &m_bob_account, &m_eve_account +#define TREZOR_ALL_WALLET_VCT vct_wallets(m_wl_alice.get(), m_wl_alice2.get(), m_wl_bob.get(), m_wl_eve.get()) +#define TREZOR_COMMON_TEST_CASE(genclass, CORE, BASE) do { \ + if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) { \ + rollback_chain(CORE, BASE.head_block()); \ genclass ctest; \ BASE.fork(ctest); \ GENERATE_AND_PLAY_INSTANCE(genclass, ctest, *(CORE)); \ @@ -79,17 +82,27 @@ namespace } \ } while(0) +typedef struct { + bool heavy_tests; +} chain_file_opts_t; +static const std::string CUR_CHAIN_MAGIC = "MoneroTrezorTestsEventFile"; +static const unsigned long CUR_CHAIN_VERSION = 2; static device_trezor_test *trezor_device = nullptr; static device_trezor_test *ensure_trezor_test_device(); 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, const po::variables_map & vm_core); -static long get_env_long(const char * flag_name, boost::optional def = boost::none){ - const char *env_data = getenv(flag_name); +static long get_env_long(const char * env_name, boost::optional def = boost::none){ + const char *env_data = getenv(env_name); return env_data ? atol(env_data) : (def ? def.get() : 0); } +static std::string get_env_string(const char * env_name, boost::optional def = boost::none){ + const char *env_data = getenv(env_name); + return env_data ? std::string(env_data) : (def ? def.get() : std::string()); +} + int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -137,9 +150,13 @@ int main(int argc, char* argv[]) hw::register_device(HW_TREZOR_NAME, ensure_trezor_test_device()); // shim device for call tracking // Bootstrapping common chain & accounts - const uint8_t initial_hf = (uint8_t)get_env_long("TEST_MIN_HF", HF_VERSION_BULLETPROOF_PLUS); - const uint8_t max_hf = (uint8_t)get_env_long("TEST_MAX_HF", HF_VERSION_BULLETPROOF_PLUS); - auto sync_test = get_env_long("TEST_KI_SYNC", 1); + const std::string trezor_path_env = get_env_string("TREZOR_PATH"); + const uint8_t initial_hf = (uint8_t)get_env_long("TEST_MIN_HF", TREZOR_TEST_MIN_HF_DEFAULT); + const uint8_t max_hf = (uint8_t)get_env_long("TEST_MAX_HF", TREZOR_TEST_MAX_HF_DEFAULT); + const bool mining_enabled = get_env_long("TEST_MINING_ENABLED", TREZOR_TEST_MINING_ENABLED_DEFAULT || heavy_tests) > 0; + const long mining_timeout = get_env_long("TEST_MINING_TIMEOUT", TREZOR_TEST_MINING_TIMEOUT_DEFAULT); + const auto sync_test = get_env_long("TEST_KI_SYNC", TREZOR_TEST_KI_SYNC_DEFAULT); + const bool env_gen_heavy = get_env_long("TEST_GEN_HEAVY", 0) > 0 || heavy_tests; MINFO("Test versions " << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); MINFO("Testing hardforks [" << (int)initial_hf << ", " << (int)max_hf << "], sync-test: " << sync_test); @@ -148,7 +165,8 @@ int main(int argc, char* argv[]) std::shared_ptr daemon = nullptr; gen_trezor_base trezor_base; - trezor_base.setup_args(trezor_path, heavy_tests); + trezor_base.setup_args(!trezor_path.empty() ? trezor_path : trezor_path_env, heavy_tests, mining_enabled, mining_timeout); + trezor_base.heavy_test_set(env_gen_heavy || heavy_tests); trezor_base.set_hard_fork(initial_hf); // Arguments for core & daemon @@ -194,6 +212,10 @@ int main(int argc, char* argv[]) TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync_with_refresh, core, trezor_base); } + TREZOR_COMMON_TEST_CASE(gen_trezor_no_passphrase, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_wallet_passphrase, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_passphrase, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_pin, core, trezor_base); 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); @@ -205,6 +227,7 @@ int main(int argc, char* argv[]) 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); TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_15outs, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_16utxo_to_sub, core, trezor_base); TREZOR_COMMON_TEST_CASE(wallet_api_tests, core, trezor_base); } @@ -242,7 +265,7 @@ static void rollback_chain(cryptonote::core * core, const cryptonote::block & he crypto::hash head_hash = get_block_hash(head), cur_hash{}; uint64_t height = get_block_height(head), cur_height=0; - MDEBUG("Rollbacking to " << height << " to hash " << head_hash); + MDEBUG("Rolling back to " << height << " to hash " << head_hash); do { core->get_blockchain_top(cur_height, cur_hash); @@ -255,11 +278,12 @@ static void rollback_chain(cryptonote::core * core, const cryptonote::block & he } while(true); } -static bool unserialize_chain_from_file(std::vector& events, gen_trezor_base &test_base, const std::string& file_path) +static bool deserialize_chain_from_file(std::vector& events, gen_trezor_base &test_base, unsigned long &version, const std::string& file_path, chain_file_opts_t& opts) { TRY_ENTRY(); std::ifstream data_file; data_file.open( file_path, std::ios_base::binary | std::ios_base::in); + std::string magic; if(data_file.fail()) return false; try @@ -267,8 +291,20 @@ static bool unserialize_chain_from_file(std::vector& events, g boost::archive::portable_binary_iarchive a(data_file); test_base.clear(); + a >> magic; + CHECK_AND_ASSERT_THROW_MES(magic == CUR_CHAIN_MAGIC, "Chain file load error - magic differs"); + + a >> version; + a >> opts.heavy_tests; + a >> magic; + CHECK_AND_ASSERT_THROW_MES(magic == CUR_CHAIN_MAGIC, "Chain file load error - magic differs"); + a >> events; a >> test_base; + a >> magic; + CHECK_AND_ASSERT_THROW_MES(magic == CUR_CHAIN_MAGIC, "Chain file load error - magic differs"); + CHECK_AND_ASSERT_THROW_MES(!data_file.fail(), "Chain file load error - file.fail()"); + CHECK_AND_ASSERT_THROW_MES(!data_file.bad(), "Chain file load error - bad read"); return true; } catch(...) @@ -276,10 +312,10 @@ static bool unserialize_chain_from_file(std::vector& events, g MWARNING("Chain deserialization failed"); return false; } - CATCH_ENTRY_L0("unserialize_chain_from_file", false); + CATCH_ENTRY_L0("deserialize_chain_from_file", false); } -static bool serialize_chain_to_file(std::vector& events, gen_trezor_base &test_base, const std::string& file_path) +static bool serialize_chain_to_file(std::vector& events, gen_trezor_base &test_base, const std::string& file_path, const chain_file_opts_t& opts) { TRY_ENTRY(); std::ofstream data_file; @@ -290,13 +326,19 @@ static bool serialize_chain_to_file(std::vector& events, gen_t { boost::archive::portable_binary_oarchive a(data_file); + a << CUR_CHAIN_MAGIC; + a << CUR_CHAIN_VERSION; + a << opts.heavy_tests; + a << CUR_CHAIN_MAGIC; + a << events; a << test_base; + a << CUR_CHAIN_MAGIC; return !data_file.fail(); } catch(...) { - MWARNING("Chain deserialization failed"); + MWARNING("Chain serialization failed"); return false; } return false; @@ -339,19 +381,39 @@ static void setup_chain(cryptonote::core * core, gen_trezor_base & trezor_base, const bool chain_file_exists = do_serialize && boost::filesystem::exists(chain_path); bool loaded = false; bool generated = false; + unsigned long chain_version = 0; + chain_file_opts_t opts = {.heavy_tests=trezor_base.heavy_tests()}; if (chain_file_exists) { - if (!unserialize_chain_from_file(events, trezor_base, chain_path)) - { - MERROR("Failed to deserialize data from file: " << chain_path); - CHECK_AND_ASSERT_THROW_MES(fix_chain, "Chain load error"); - } else + chain_file_opts_t loaded_opts; + const auto fix_suggestion = fix_chain ? "Chain file will be regenerated." : "Use --fix_chain to regenerate chain file"; + bool deserialize_ok = deserialize_chain_from_file(events, trezor_base, chain_version, chain_path, loaded_opts); + if (!deserialize_ok) { + MERROR("Failed to deserialize data from file: " << chain_path << ". " << fix_suggestion); + CHECK_AND_ASSERT_THROW_MES(fix_chain, "Chain load error. " << fix_suggestion); + } + else if (chain_version != CUR_CHAIN_VERSION) { + MERROR("Loaded chain version " << chain_version << " differs from current target" << CUR_CHAIN_VERSION << " in the chain file: " << chain_path << ". " << fix_suggestion); + CHECK_AND_ASSERT_THROW_MES(fix_chain, "Chain load error - versions differ. " << fix_suggestion); + deserialize_ok = false; + } + else if (opts.heavy_tests && !loaded_opts.heavy_tests) { + MERROR("Loaded chain does not include transactions for heavy test, the chain file: " << chain_path << ". " << fix_suggestion); + CHECK_AND_ASSERT_THROW_MES(fix_chain, "Chain does not heavy tx set - versions differ. " << fix_suggestion); + deserialize_ok = false; + } + + if (deserialize_ok) { trezor_base.load(events); generated = true; loaded = true; } + else { + events.clear(); + trezor_base.clear(); + } } if (!generated) @@ -360,11 +422,12 @@ static void setup_chain(cryptonote::core * core, gen_trezor_base & trezor_base, { trezor_base.clear(); generated = trezor_base.generate(events); + trezor_base.fix_hf(events); if (generated && !loaded && do_serialize) { trezor_base.update_trackers(events); - if (!serialize_chain_to_file(events, trezor_base, chain_path)) + if (!serialize_chain_to_file(events, trezor_base, chain_path, opts)) { MERROR("Failed to serialize data to file: " << chain_path); } @@ -385,6 +448,10 @@ static void setup_chain(cryptonote::core * core, gen_trezor_base & trezor_base, } } +static fnc_accept_output_t fnc_trezor_default_acceptor_tx_in = [] (const fnc_accept_output_crate_t &info) -> bool { + return info.oi.is_coin_base || info.oi.blk_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= info.cur_height + 1; +}; + static device_trezor_test *ensure_trezor_test_device(){ if (!trezor_device) { trezor_device = new device_trezor_test(); @@ -393,6 +460,20 @@ static device_trezor_test *ensure_trezor_test_device(){ return trezor_device; } +static size_t min_mixin_for_hf(uint8_t hf) +{ + if (hf >= HF_VERSION_MIN_MIXIN_15) + return 15; + if (hf >= HF_VERSION_MIN_MIXIN_10) + return 10; + return 0; +} + +static int bp_version_from_hf(uint8_t hf) +{ + return hf >= HF_VERSION_BULLETPROOF_PLUS ? 4 : hf >= HF_VERSION_CLSAG ? 3 : hf >= HF_VERSION_SMALLER_BP ? 2 : 1; +} + static void add_hforks(std::vector& events, const v_hardforks_t& hard_forks) { event_replay_settings repl_set; @@ -426,6 +507,52 @@ static crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2 } } +static void generate_tx_block(std::vector& events, std::list &txs_lst, cryptonote::block & head, + const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, size_t repeat, + size_t mixin, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + const size_t per_tx_limit = 14; + const size_t iters = repeat ? std::ceil(repeat / (double)per_tx_limit) : 1; + + for(size_t j = 0; j < iters; ++j) + { + const auto dests = build_dsts(to, false, amount, j < iters - 1 ? per_tx_limit : repeat - j * per_tx_limit); + + cryptonote::transaction t; + construct_tx_to_key(events, t, head, from, dests, TESTS_DEFAULT_FEE, mixin, rct, range_proof_type, bp_version, true, fnc_trezor_default_acceptor_tx_in); + txs_lst.push_back(t); + events.push_back(t); + } +} + +static void generate_tx_block(std::vector& events, std::list &txs_lst, cryptonote::block & head, + const cryptonote::account_base& from, const std::vector& destinations, size_t repeat, + size_t mixin, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + CHECK_AND_ASSERT_THROW_MES(destinations.size() < 16, "Keep destinations below 16, BP+ limit"); + for(size_t j = 0; j < repeat; ++j) + { + cryptonote::transaction t; + construct_tx_to_key(events, t, head, from, destinations, TESTS_DEFAULT_FEE, mixin, rct, range_proof_type, bp_version, true, fnc_trezor_default_acceptor_tx_in); + txs_lst.push_back(t); + events.push_back(t); + } +} + +static void generate_tx_block(std::vector& events, std::list &txs_lst, cryptonote::block & head, + const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, size_t repeat = 1, + uint8_t hf = 1) +{ + generate_tx_block(events, txs_lst, head, from, to, amount, repeat, min_mixin_for_hf(hf), hf >= 8, rct::RangeProofPaddedBulletproof, bp_version_from_hf(hf)); +} + +static void generate_tx_block(std::vector& events, std::list &txs_lst, cryptonote::block & head, + const cryptonote::account_base& from, const std::vector& destinations, size_t repeat = 1, + uint8_t hf = 1) +{ + generate_tx_block(events, txs_lst, head, from, destinations, repeat, min_mixin_for_hf(hf), hf >= 8, rct::RangeProofPaddedBulletproof, bp_version_from_hf(hf)); +} + static void setup_shim(hw::wallet_shim * shim) { shim->get_tx_pub_key_from_received_outs = &get_tx_pub_key_from_received_outs; @@ -593,29 +720,57 @@ static std::vector vct_wallets(tools::wallet2* w1=nullptr, tool return res; } -static uint64_t get_available_funds(tools::wallet2* wallet, uint32_t account=0) +#define TREZOR_ALL_ACCOUNTS 0xffffff +static uint64_t get_available_funds(tools::wallet2* wallet, uint32_t account=0, uint64_t * nutxos = nullptr, uint64_t * nutxos_rct = nullptr) { tools::wallet2::transfer_container transfers; wallet->get_transfers(transfers); uint64_t sum = 0; for(const auto & cur : transfers) { - sum += !cur.m_spent && cur.m_subaddr_index.major == account ? cur.amount() : 0; + const bool utxo = !cur.m_spent && (account == TREZOR_ALL_ACCOUNTS || cur.m_subaddr_index.major == account); + sum += utxo ? cur.amount() : 0; + if (nutxos) { + *nutxos += utxo; + } + if (nutxos_rct && cur.is_rct()) { + *nutxos_rct += utxo; + } } return sum; } +static std::string wallet_desc_str(tools::wallet2* wallet, uint32_t account=0) +{ + std::stringstream ss; + uint64_t nutxos = 0, nutxos_rct = 0, nutxos_all = 0, nutxos_rct_all = 0; + uint64_t funds = get_available_funds(wallet, account, &nutxos, &nutxos_rct); + uint64_t funds_all = get_available_funds(wallet, TREZOR_ALL_ACCOUNTS, &nutxos_all, &nutxos_rct_all); + ss << "funds: " << std::setfill(' ') << std::setw(12) << ((double)funds / COIN) + << ", utxos: " << std::setfill(' ') << std::setw(3) << nutxos + << ", rct: " << std::setfill(' ') << std::setw(3) << nutxos_rct + << "; all accounts: " << std::setfill(' ') << std::setw(12) << ((double)funds_all / COIN) + << ", utxos: " << std::setfill(' ') << std::setw(3) << nutxos_all + << ", rct: " << std::setfill(' ') << std::setw(3) << nutxos_rct_all; + return ss.str(); +} + // gen_trezor_base const uint64_t gen_trezor_base::m_ts_start = 1397862000; // As default wallet timestamp is 1397516400 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_miner_master_seed_str = "8b133a3868993176b613738816247a7f4d357cae555996519cf5b543e9b3554b"; // sha256(miner) 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"; +const std::string gen_trezor_base::m_alice2_passphrase = "a"; +const std::string gen_trezor_base::m_alice2_master_seed = "0b86cf0e71204ca3cdc389daf0bb1cf654ac7d54edfed68a9faf921ba140a708"; +const std::string gen_trezor_base::m_bob_master_seed = "81b637d8fcd2c6da6359e6963113a1170de795e4b725b84d1e0b4cfd9ec58ce9"; // sha256(bob) +const std::string gen_trezor_base::m_eve_master_seed = "85262adf74518bbb70c7cb94cd6159d91669e5a81edf1efebd543eadbda9fa2b"; // sha256(eve) gen_trezor_base::gen_trezor_base(){ - m_rct_config = {rct::RangeProofPaddedBulletproof, 1}; + m_rct_config = {rct::RangeProofPaddedBulletproof, 4}; m_test_get_tx_key = true; m_network_type = cryptonote::TESTNET; } @@ -623,26 +778,37 @@ gen_trezor_base::gen_trezor_base(){ 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), m_top_hard_fork(other.m_top_hard_fork), + m_hard_forks(other.m_hard_forks), m_head(other.m_head), m_events(other.m_events), + m_trezor(other.m_trezor), m_rct_config(other.m_rct_config), m_top_hard_fork(other.m_top_hard_fork), m_heavy_tests(other.m_heavy_tests), m_test_get_tx_key(other.m_test_get_tx_key), m_live_refresh_enabled(other.m_live_refresh_enabled), - m_network_type(other.m_network_type), m_daemon(other.m_daemon) + m_network_type(other.m_network_type), m_daemon(other.m_daemon), m_alice2_account(other.m_alice2_account), + m_trezor_path(other.m_trezor_path), m_trezor_pin(other.m_trezor_pin), m_trezor_passphrase(other.m_trezor_passphrase), + m_trezor_use_passphrase(other.m_trezor_use_passphrase), m_trezor_use_alice2(other.m_trezor_use_alice2), + m_gen_heavy_test_set(other.m_gen_heavy_test_set), + m_mining_enabled(other.m_mining_enabled), + m_mining_timeout(other.m_mining_timeout), + m_no_change_in_tested_tx(other.m_no_change_in_tested_tx) { } -void gen_trezor_base::setup_args(const std::string & trezor_path, bool heavy_tests) +void gen_trezor_base::setup_args(const std::string & trezor_path, bool heavy_tests, bool mining_enabled, long mining_timeout) { m_trezor_path = trezor_path.empty() ? m_device_name : std::string("Trezor:") + trezor_path; m_heavy_tests = heavy_tests; + m_gen_heavy_test_set |= heavy_tests; + m_mining_enabled = mining_enabled; + m_mining_timeout = mining_timeout; } -void gen_trezor_base::setup_trezor() +void gen_trezor_base::setup_trezor(bool use_passphrase, const std::string & pin) { hw::device &hwdev = hw::get_device(m_trezor_path); auto trezor = dynamic_cast(&hwdev); CHECK_AND_ASSERT_THROW_MES(trezor, "Dynamic cast failed"); - trezor->setup_for_tests(m_trezor_path, m_device_seed, m_network_type); + trezor->set_callback(this); + trezor->setup_for_tests(m_trezor_path, m_device_seed, m_network_type, use_passphrase, pin); m_trezor = trezor; } @@ -658,15 +824,25 @@ void gen_trezor_base::fork(gen_trezor_base & other) other.m_top_hard_fork = m_top_hard_fork; other.m_trezor_path = m_trezor_path; other.m_heavy_tests = m_heavy_tests; + other.m_gen_heavy_test_set = m_gen_heavy_test_set; other.m_rct_config = m_rct_config; other.m_test_get_tx_key = m_test_get_tx_key; other.m_live_refresh_enabled = m_live_refresh_enabled; + other.m_trezor_pin = m_trezor_pin; + other.m_trezor_passphrase = m_trezor_passphrase; + other.m_trezor_use_passphrase = m_trezor_use_passphrase; + other.m_trezor_use_alice2 = m_trezor_use_alice2; + other.m_mining_enabled = m_mining_enabled; + other.m_mining_timeout = m_mining_timeout; + other.m_miner_account = m_miner_account; other.m_bob_account = m_bob_account; other.m_alice_account = m_alice_account; + other.m_alice2_account = m_alice2_account; other.m_eve_account = m_eve_account; other.m_trezor = m_trezor; + other.m_no_change_in_tested_tx = m_no_change_in_tested_tx; other.m_generator.set_events(&other.m_events); other.m_generator.set_network_type(m_network_type); } @@ -688,16 +864,55 @@ void gen_trezor_base::add_shared_events(std::vector& events) } } -void gen_trezor_base::init_fields() +void gen_trezor_base::init_accounts() { - m_miner_account.generate(); + crypto::secret_key master_seed{}; + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_miner_master_seed_str, master_seed), "Hexdecode fails: m_miner_master_seed_str"); + + m_miner_account.generate(master_seed, true); 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"); - + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_master_seed_str, master_seed), "Hexdecode fails: m_master_seed_str"); m_alice_account.generate(master_seed, true); m_alice_account.set_createtime(m_wallet_ts); + + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_alice2_master_seed, master_seed), "Hexdecode fails: m_alice2_master_seed"); + m_alice2_account.generate(master_seed, true); + m_alice2_account.set_createtime(m_wallet_ts); + + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_bob_master_seed, master_seed), "Hexdecode fails: m_bob_master_seed"); + m_bob_account.generate(master_seed, true); + m_bob_account.set_createtime(m_wallet_ts); + + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_eve_master_seed, master_seed), "Hexdecode fails: m_eve_master_seed"); + m_eve_account.generate(master_seed, true); + m_eve_account.set_createtime(m_wallet_ts); +} + +void gen_trezor_base::init_wallets() +{ + m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_alice2.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_bob.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_eve.reset(new tools::wallet2(m_network_type, 1, true)); + + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_alice2.get(), m_alice2_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); + + m_wl_alice->expand_subaddresses({5, 20}); + m_wl_alice2->expand_subaddresses({5, 20}); + m_wl_bob->expand_subaddresses({5, 20}); + m_wl_eve->expand_subaddresses({5, 20}); +} + +void gen_trezor_base::log_wallets_desc() +{ + MDEBUG("Wallet for Alice: " << wallet_desc_str(m_wl_alice.get())); + MDEBUG("Wallet for Alice2: " << wallet_desc_str(m_wl_alice2.get())); + MDEBUG("Wallet for Bob: " << wallet_desc_str(m_wl_bob.get())); + MDEBUG("Wallet for Eve: " << wallet_desc_str(m_wl_eve.get())); } void gen_trezor_base::update_client_settings() @@ -710,14 +925,8 @@ void gen_trezor_base::update_client_settings() bool gen_trezor_base::generate(std::vector& events) { - init_fields(); - setup_trezor(); - - m_live_refresh_enabled = false; - update_client_settings(); - - m_alice_account.create_from_device(*m_trezor); - m_alice_account.set_createtime(m_wallet_ts); + init_accounts(); + setup_trezor(m_trezor_use_passphrase, m_trezor_pin); // Events, custom genesis so it matches wallet genesis auto & generator = m_generator; // macro shortcut @@ -733,10 +942,6 @@ bool gen_trezor_base::generate(std::vector& events) generator.add_block(blk_gen, 0, block_weights, 0, rew); // 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); @@ -745,6 +950,7 @@ bool gen_trezor_base::generate(std::vector& events) // Another block with predefined timestamp. // Carefully set reward and already generated coins so it passes miner_tx check. cryptonote::block blk_0; + cryptonote::block & last_block = blk_0; { std::list tx_list; const crypto::hash prev_id = get_block_hash(blk_gen); @@ -754,40 +960,37 @@ bool gen_trezor_base::generate(std::vector& events) 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)); + events.push_back(last_block); + MDEBUG("Gen+1 block has time: " << last_block.timestamp << " blid: " << get_block_hash(last_block)); // Generate some spendable funds on the Miner account - REWIND_BLOCKS_N(events, blk_3, blk_0, m_miner_account, 40); + REWIND_BLOCKS_N(events, blk_3, last_block, m_miner_account, 20); + last_block = blk_3; - // Rewind so the miners funds are unlocked for initial transactions. - REWIND_BLOCKS(events, blk_3r, blk_3, m_miner_account); + // Rewind so the miners funds are unlocked for initial transactions, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW blocks + REWIND_BLOCKS_N(events, blk_3r1, last_block, m_miner_account, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - 10); + last_block = blk_3r1; + last_block = rewind_blocks_with_decoys(events, last_block, 10, 1, TEST_DECOY_MINING_COUNTS); - // 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 + MDEBUG("Mining chain generated, height: " << get_block_height(last_block) << ", #events: " << events.size() << ", last block: " << get_block_hash(last_block)); + + // Non-rct transactions Miner -> Alice + // Note that change transaction is spendable after CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE so we batch it to a tx + std::list txs_blk_4; + generate_tx_block(events, txs_blk_4, last_block, m_miner_account, m_alice_account, MK_TCOINS(0.1), 20, 1); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, last_block, m_miner_account, txs_blk_4); + last_block = blk_4; + MDEBUG("Non-rct transactions generated, height: " << get_block_height(last_block) << ", #events: " << events.size() << ", last block: " << get_block_hash(last_block)); // 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)); + MDEBUG("Hardfork " << (int)CUR_HF << " height: " << hardfork_height << ", #events: " << events.size() << " at block: " << get_block_hash(last_block)); // RCT transactions, wallets have to be used, wallet init - m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); - m_wl_bob.reset(new tools::wallet2(m_network_type, 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); - + init_wallets(); 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}); @@ -797,40 +1000,50 @@ bool gen_trezor_base::generate(std::vector& events) 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(50), 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); + // Miner -> Alice, RCT funds + std::list txs_blk_5; + generate_tx_block(events, txs_blk_5, last_block, m_miner_account, m_alice_account, MK_TCOINS(1), 1, CUR_HF); + generate_tx_block(events, txs_blk_5, last_block, m_miner_account, m_alice_account, MK_TCOINS(0.1), heavy_tests() || heavy_test_set() ? 95 : 0, CUR_HF); // 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(10), 10, blk_4); - MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(30), 10, blk_4); - MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(40), 10, blk_4); - MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_5, blk_4r, m_miner_account, txs_blk_5, CUR_HF); + MDEBUG("blk_5 construction - RCT set"); + generate_tx_block(events, txs_blk_5, last_block, m_miner_account, m_alice_account, MK_TCOINS(1), 1, CUR_HF); + generate_tx_block(events, txs_blk_5, last_block, m_miner_account, build_dsts({ + { m_alice_account, false, MK_TCOINS(1) }, + { m_alice_account, false, MK_TCOINS(1) }, + { m_alice_account, false, MK_TCOINS(1) }, + { m_alice2_account, false, MK_TCOINS(1) } + }), 1, CUR_HF); + + // Sub-address destinations + multi out to force use of additional keys + generate_tx_block(events, txs_blk_5, last_block, m_miner_account, build_dsts({ + {addr_alice_sub_0_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_2, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_2, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_3, true, MK_TCOINS(0.1)}, + {m_miner_account, false, MK_TCOINS(0.1)}, + {addr_alice_sub_0_2, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_3, true, MK_TCOINS(0.1)}, + {m_miner_account, false, MK_TCOINS(0.1)}, + {addr_alice_sub_0_2, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_4, true, MK_TCOINS(0.1)}}), 1, CUR_HF); + + // Transfer to other accounts + generate_tx_block(events, txs_blk_5, last_block, m_miner_account, build_dsts({ + {addr_alice_sub_1_0, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_0, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_3, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_2, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_2, true, MK_TCOINS(0.1)}, + {addr_alice_sub_1_1, true, MK_TCOINS(0.1)}, + {addr_alice_sub_0_5, true, MK_TCOINS(0.1)}}), 1, CUR_HF); + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_5, last_block, m_miner_account, txs_blk_5, CUR_HF); + last_block = blk_5; // Simple transaction check bool resx = rct::verRctSemanticsSimple(txs_blk_5.begin()->rct_signatures); @@ -838,23 +1051,34 @@ bool gen_trezor_base::generate(std::vector& events) 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 + // Rewind blocks so there are more spendable coins + last_block = rewind_blocks_with_decoys(events, last_block, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE + 2, CUR_HF, TEST_DECOY_MINING_COUNTS); // 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); + MDEBUG("Post blk_5 construction"); + wallet_tools::process_transactions(vct_wallets(m_wl_alice.get(), m_wl_alice2.get(), m_wl_bob.get()), events, last_block, m_bt); + log_wallets_desc(); // Send Alice -> Bob, manually constructed. Simple TX test, precondition. cryptonote::transaction tx_1; + uint64_t tx_1_amount = MK_TCOINS(0.1); std::vector selected_transfers; std::vector 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"); + bool res = wallet_tools::fill_tx_sources(m_wl_alice.get(), sources, TREZOR_TEST_MIXIN, boost::none, tx_1_amount + TREZOR_TEST_FEE, m_bt, selected_transfers, get_block_height(last_block), 0, 1); + CHECK_AND_ASSERT_THROW_MES(res, "Tx01: 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); + res = construct_tx_to_key(tx_1, m_wl_alice.get(), m_bob_account, tx_1_amount, sources, TREZOR_TEST_FEE, true, rct::RangeProofPaddedBulletproof, 1); + CHECK_AND_ASSERT_THROW_MES(res, "Tx01: tx construction failed"); + + std::list txs_blk_6; + txs_blk_6.push_back(tx_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)); + + generate_tx_block(events, txs_blk_6, last_block, m_miner_account, m_alice_account, MK_TCOINS(1), 1, CUR_HF); + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_6, last_block, m_miner_account, txs_blk_6, CUR_HF); + + last_block = blk_6; + MDEBUG("Post 1st tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(last_block) << ", event: " << events.size()); // Simple transaction check resx = rct::verRctSemanticsSimple(tx_1.rct_signatures); @@ -862,20 +1086,20 @@ bool gen_trezor_base::generate(std::vector& events) CHECK_AND_ASSERT_THROW_MES(resx, "tx_1 semantics failed"); CHECK_AND_ASSERT_THROW_MES(resy, "tx_1 non-semantics failed"); - REWIND_BLOCKS_N_HF(events, blk_6r, blk_6, m_miner_account, 10, CUR_HF); - wallet_tools::process_transactions(m_wl_alice.get(), events, blk_6, m_bt); - wallet_tools::process_transactions(m_wl_bob.get(), events, blk_6, m_bt); - MDEBUG("Available funds on Alice: " << get_available_funds(m_wl_alice.get())); - MDEBUG("Available funds on Bob: " << get_available_funds(m_wl_bob.get())); + last_block = rewind_blocks_with_decoys(events, last_block, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, CUR_HF, TEST_DECOY_MINING_COUNTS); + MDEBUG("Blockchain generation up to " << (int)CUR_HF << " finished at height " << num_blocks(events) << ", #events: " << events.size()); - m_head = blk_6r; + wallet_tools::process_transactions(TREZOR_ALL_WALLET_VCT, events, last_block, m_bt); + log_wallets_desc(); + + m_head = last_block; m_events = events; return true; } void gen_trezor_base::load(std::vector& events) { - init_fields(); + init_accounts(); m_events = events; m_generator.set_events(&m_events); m_generator.set_network_type(m_network_type); @@ -910,37 +1134,31 @@ void gen_trezor_base::load(std::vector& events) } // Setup wallets, synchronize blocks - m_bob_account.set_createtime(m_wallet_ts); - m_eve_account.set_createtime(m_wallet_ts); - - setup_trezor(); - update_client_settings(); - m_alice_account.create_from_device(*m_trezor); - m_alice_account.set_createtime(m_wallet_ts); - - m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); - m_wl_bob.reset(new tools::wallet2(m_network_type, 1, true)); - m_wl_eve.reset(new tools::wallet2(m_network_type, 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); - MDEBUG("Available funds on Alice: " << get_available_funds(m_wl_alice.get())); - MDEBUG("Available funds on Bob: " << get_available_funds(m_wl_bob.get())); + init_wallets(); + wallet_tools::process_transactions(TREZOR_ALL_WALLET_VCT, events, m_head, m_bt); + log_wallets_desc(); } void gen_trezor_base::rewind_blocks(std::vector& events, size_t rewind_n, uint8_t hf) { - auto & generator = m_generator; // macro shortcut - REWIND_BLOCKS_N_HF(events, blk_new, m_head, m_miner_account, rewind_n, hf); - m_head = blk_new; + m_head = rewind_blocks_with_decoys(events, m_head, rewind_n, hf, TEST_DECOY_MINING_COUNTS); m_events = events; - MDEBUG("Blocks rewound: " << rewind_n << ", #blocks: " << num_blocks(events) << ", #events: " << events.size()); +} - 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); +cryptonote::block gen_trezor_base::rewind_blocks_with_decoys(std::vector& events, cryptonote::block & head, size_t rewind_n, uint8_t hf, size_t num_decoys_per_block) +{ + auto & generator = m_generator; // shortcut required by MAKE_NEXT_BLOCK_TX_LIST_HF + cryptonote::block blk_last = head; + for (size_t i = 0; i < rewind_n; ++i) + { + std::list txs_lst; + generate_tx_block(events, txs_lst, blk_last, m_miner_account, m_bob_account, MK_TCOINS(0.0001), num_decoys_per_block, hf); + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk, blk_last, m_miner_account, txs_lst, hf); + blk_last = blk; + } + + MDEBUG("Blocks rewound: " << rewind_n << ", #blocks: " << num_blocks(events) << ", #events: " << events.size()); + return blk_last; } void gen_trezor_base::fix_hf(std::vector& events) @@ -958,8 +1176,15 @@ void gen_trezor_base::fix_hf(std::vector& events) ADD_HARDFORK(m_hard_forks, hf_to_add, hardfork_height); add_top_hfork(events, m_hard_forks); MDEBUG("Hardfork added at height: " << hardfork_height << ", from " << (int)current_hf << " to " << (int)hf_to_add); - rewind_blocks(events, 10, hf_to_add); + + // Wallet2 requires 10 blocks extra to activate HF + rewind_blocks(events, hf_to_add == m_top_hard_fork ? 10 : 1, hf_to_add); + + if (hf_to_add == m_top_hard_fork) { + MDEBUG("Blockchain generation up to " << (int)hf_to_add << " finished at height " << num_blocks(events) << ", #events: " << events.size()); + } } + wallet_tools::process_transactions(TREZOR_ALL_WALLET_VCT, events, m_head, m_bt); } void gen_trezor_base::update_trackers(std::vector& events) @@ -967,25 +1192,25 @@ void gen_trezor_base::update_trackers(std::vector& events) wallet_tools::process_transactions(nullptr, events, m_head, m_bt); } +void gen_trezor_base::init_trezor_account() { + setup_trezor(m_trezor_use_passphrase, m_trezor_pin); + update_client_settings(); + + if (m_trezor_use_alice2) { + m_alice2_account.create_from_device(*m_trezor); + m_alice2_account.set_createtime(m_wallet_ts); + } else { + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + } +} + void gen_trezor_base::test_setup(std::vector& events) { add_shared_events(events); - - setup_trezor(); - update_client_settings(); - - m_alice_account.create_from_device(*m_trezor); - m_alice_account.set_createtime(m_wallet_ts); - - m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); - m_wl_bob.reset(new tools::wallet2(m_network_type, 1, true)); - m_wl_eve.reset(new tools::wallet2(m_network_type, 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); + init_trezor_account(); + init_wallets(); + wallet_tools::process_transactions(TREZOR_ALL_WALLET_VCT, events, m_head, m_bt); } void gen_trezor_base::add_transactions_to_events( @@ -1012,8 +1237,10 @@ void gen_trezor_base::add_transactions_to_events( m_head = blk_new; } -void gen_trezor_base::test_trezor_tx(std::vector& events, std::vector& ptxs, std::vector& dsts_info, test_generator &generator, std::vector wallets, bool is_sweep) +void gen_trezor_base::test_trezor_tx(std::vector& events, std::vector& ptxs, std::vector& dsts_info, test_generator &generator, std::vector wallets, bool is_sweep, size_t sender_idx) { + CHECK_AND_ASSERT_THROW_MES(sender_idx < wallets.size(), "Sender index out of bounds"); + // 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); @@ -1067,6 +1294,7 @@ void gen_trezor_base::test_trezor_tx(std::vector& events, std: // TX receive test uint64_t sum_in = 0; uint64_t sum_out = 0; + const cryptonote::account_base * sender_account = &wallets[sender_idx]->get_account(); for(size_t txid = 0; txid < exported_txs.ptx.size(); ++txid) { auto &c_ptx = exported_txs.ptx[txid]; @@ -1097,7 +1325,7 @@ void gen_trezor_base::test_trezor_tx(std::vector& events, std: 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; + const bool sender = widx == sender_idx; tools::wallet2 *wl = wallets[widx]; wallet_tools::process_transactions(wl, events, m_head, m_bt, boost::make_optional(head_hash)); @@ -1151,8 +1379,8 @@ void gen_trezor_base::test_trezor_tx(std::vector& events, std: 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"); + CHECK_AND_ASSERT_THROW_MES((num_received + m_no_change_in_tested_tx) == num_outs, "Number of received outputs do not match number of outgoing"); + CHECK_AND_ASSERT_THROW_MES((recv_out_idx.size() + m_no_change_in_tested_tx) == 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 @@ -1165,7 +1393,8 @@ void gen_trezor_base::test_trezor_tx(std::vector& events, std: CHECK_AND_ASSERT_THROW_MES(sum_in == sum_out, "Tx amount mismatch"); // Test get_tx_key feature for stored private tx keys - test_get_tx(events, wallets, exported_txs.ptx, aux_data.tx_device_aux); + CHECK_AND_ASSERT_THROW_MES(sender_account != nullptr, "Sender account is null"); + test_get_tx(events, wallets, sender_account, exported_txs.ptx, aux_data.tx_device_aux); } bool gen_trezor_base::verify_tx_key(const ::crypto::secret_key & tx_priv, const ::crypto::public_key & tx_pub, const subaddresses_t & subs) @@ -1187,6 +1416,7 @@ bool gen_trezor_base::verify_tx_key(const ::crypto::secret_key & tx_priv, const void gen_trezor_base::test_get_tx( std::vector& events, std::vector wallets, + const cryptonote::account_base * account, const std::vector &ptxs, const std::vector &aux_tx_info) { @@ -1228,7 +1458,7 @@ void gen_trezor_base::test_get_tx( dev_cold->load_tx_key_data(tx_key_data, aux_tx_info[txid]); CHECK_AND_ASSERT_THROW_MES(std::string(tx_prefix_hash.data, 32) == tx_key_data.tx_prefix_hash, "TX prefix mismatch"); - dev_cold->get_tx_key(tx_keys, tx_key_data, m_alice_account.get_keys().m_view_secret_key); + dev_cold->get_tx_key(tx_keys, tx_key_data, account->get_keys().m_view_secret_key); // alice CHECK_AND_ASSERT_THROW_MES(!tx_keys.empty(), "Empty TX keys"); CHECK_AND_ASSERT_THROW_MES(verify_tx_key(tx_keys[0], tx_pub, all_subs), "Tx pub mismatch"); CHECK_AND_ASSERT_THROW_MES(additional_pub_keys.size() == tx_keys.size() - 1, "Invalid additional keys count"); @@ -1242,11 +1472,16 @@ void gen_trezor_base::test_get_tx( void gen_trezor_base::mine_and_test(std::vector& events) { + if (!m_mining_enabled) { + MDEBUG("Mining test disabled"); + return; + } + cryptonote::core * core = daemon()->core(); const uint64_t height_before_mining = daemon()->get_height(); const auto miner_address = cryptonote::get_account_address_as_str(FAKECHAIN, false, get_address(m_miner_account)); - daemon()->mine_blocks(1, miner_address); + daemon()->mine_blocks(1, miner_address, std::chrono::seconds(m_mining_timeout)); const uint64_t cur_height = daemon()->get_height(); CHECK_AND_ASSERT_THROW_MES(height_before_mining < cur_height, "Mining fail"); @@ -1285,6 +1520,15 @@ void gen_trezor_base::set_hard_fork(uint8_t hf) } } +boost::optional gen_trezor_base::on_pin_request() { + return boost::make_optional(epee::wipeable_string((m_trezor_pin).data(), (m_trezor_pin).size())); +} + +boost::optional gen_trezor_base::on_passphrase_request(bool &on_device) { + on_device = false; + return boost::make_optional(epee::wipeable_string((m_trezor_passphrase).data(), (m_trezor_passphrase).size())); +} + #define TREZOR_TEST_PREFIX() \ test_generator generator(m_generator); \ test_setup(events); \ @@ -1294,7 +1538,7 @@ void gen_trezor_base::set_hard_fork(uint8_t hf) #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())); \ + test_trezor_tx(events, _dsts, _dsts_info, generator, TREZOR_ALL_WALLET_VCT); \ return true #define TREZOR_SKIP_IF_VERSION_LEQ(x) if (m_trezor->get_version() <= x) { MDEBUG("Test skipped"); return true; } @@ -1307,7 +1551,7 @@ tsx_builder * tsx_builder::sources(std::vector & so return this; } -tsx_builder * tsx_builder::compute_sources(boost::optional num_utxo, boost::optional min_amount, ssize_t offset, int step, boost::optional fnc_accept) +tsx_builder * tsx_builder::compute_sources(boost::optional num_utxo, uint64_t extra_amount, ssize_t offset, int step, boost::optional fnc_accept) { CHECK_AND_ASSERT_THROW_MES(m_tester, "m_tester wallet empty"); CHECK_AND_ASSERT_THROW_MES(m_from, "m_from wallet empty"); @@ -1326,13 +1570,21 @@ tsx_builder * tsx_builder::compute_sources(boost::optional num_utxo, boo 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); + const uint64_t needed_amount = m_fee + std::accumulate(m_destinations_orig.begin(), m_destinations_orig.end(), 0, [](int a, const cryptonote::tx_destination_entry& b){return a + b.amount;}); + const uint64_t real_amount = needed_amount + extra_amount; + MDEBUG("Computing sources for tx, |utxos| = " << (num_utxo ? std::to_string(num_utxo.get()) : "*") + << ", sum: " << std::setfill(' ') << std::setw(12) << ((double)real_amount / COIN) + << ", need: " << std::setfill(' ') << std::setw(12) << ((double)needed_amount / COIN) + << ", extra: " << std::setfill(' ') << std::setw(12) << ((double)extra_amount / COIN) + << ", fee: " << std::setfill(' ') << std::setw(12) << ((double)m_fee / COIN)); + + bool res = wallet_tools::fill_tx_sources(m_from, m_sources, m_mixin, num_utxo, real_amount, m_tester->m_bt, m_selected_transfers, m_cur_height, offset, step, fnc_acc, fnc_trezor_default_acceptor_tx_in); + CHECK_AND_ASSERT_THROW_MES(!num_utxo || num_utxo == m_sources.size(), "!!Test error, Number of computed inputs " << m_sources.size() << " does not match desired number " << num_utxo.get()); CHECK_AND_ASSERT_THROW_MES(res, "Tx source fill error"); return this; } -tsx_builder * tsx_builder::compute_sources_to_sub(boost::optional num_utxo, boost::optional min_amount, ssize_t offset, int step, boost::optional fnc_accept) +tsx_builder * tsx_builder::compute_sources_to_sub(boost::optional num_utxo, uint64_t extra_amount, ssize_t offset, int step, boost::optional 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){ @@ -1344,10 +1596,10 @@ tsx_builder * tsx_builder::compute_sources_to_sub(boost::optional num_ut return true; }; - return compute_sources(num_utxo, min_amount, offset, step, fnc); + return compute_sources(num_utxo, extra_amount, offset, step, fnc); } -tsx_builder * tsx_builder::compute_sources_to_sub_acc(boost::optional num_utxo, boost::optional min_amount, ssize_t offset, int step, boost::optional fnc_accept) +tsx_builder * tsx_builder::compute_sources_to_sub_acc(boost::optional num_utxo, uint64_t extra_amount, ssize_t offset, int step, boost::optional 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){ @@ -1359,7 +1611,7 @@ tsx_builder * tsx_builder::compute_sources_to_sub_acc(boost::optional nu return true; }; - return compute_sources(num_utxo, min_amount, offset, step, fnc); + return compute_sources(num_utxo, extra_amount, offset, step, fnc); } tsx_builder * tsx_builder::destinations(std::vector &dsts) @@ -1513,11 +1765,11 @@ device_trezor_test::device_trezor_test(): m_tx_sign_ctr(0), m_compute_key_image_ void device_trezor_test::clear_test_counters(){ m_tx_sign_ctr = 0; m_compute_key_image_ctr = 0; + m_live_synced_ki.clear(); } -void device_trezor_test::setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type){ +void device_trezor_test::setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type, bool use_passphrase, const std::string & pin){ this->clear_test_counters(); - this->set_callback(nullptr); this->set_debug(true); // debugging commands on Trezor (auto-confirm transactions) CHECK_AND_ASSERT_THROW_MES(this->set_name(trezor_path), "Could not set device name " << trezor_path); @@ -1527,7 +1779,7 @@ void device_trezor_test::setup_for_tests(const std::string & trezor_path, const CHECK_AND_ASSERT_THROW_MES(this->init(), "Could not initialize the device " << trezor_path); CHECK_AND_ASSERT_THROW_MES(this->connect(), "Could not connect to the device " << trezor_path); this->wipe_device(); - this->load_device(seed); + this->load_device(seed, pin, use_passphrase); this->release(); this->disconnect(); } @@ -1540,6 +1792,7 @@ bool device_trezor_test::compute_key_image(const ::cryptonote::account_keys &ack bool res = device_trezor::compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki); m_compute_key_image_ctr += res; + m_live_synced_ki.insert(ki); return res; } @@ -1580,8 +1833,13 @@ bool gen_trezor_ki_sync::generate(std::vector& events) CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement test interface"); if (!m_live_refresh_enabled) CHECK_AND_ASSERT_THROW_MES(dev_trezor_test->m_compute_key_image_ctr == 0, "Live refresh should not happen: " << dev_trezor_test->m_compute_key_image_ctr); - else - CHECK_AND_ASSERT_THROW_MES(dev_trezor_test->m_compute_key_image_ctr == ski.size(), "Live refresh counts invalid"); + else { + CHECK_AND_ASSERT_THROW_MES(dev_trezor_test->m_compute_key_image_ctr >= ski.size(), "Live refresh counts invalid"); + for(size_t i = 0; i < transfers.size(); ++i) + { + CHECK_AND_ASSERT_THROW_MES(dev_trezor_test->m_live_synced_ki.count(ski[i].first) > 0, "Key image not foind in live sync: " << ski[i].first); + } + } return true; } @@ -1667,13 +1925,16 @@ bool gen_trezor_live_refresh::generate(std::vector& events) bool gen_trezor_1utxo::generate(std::vector& events) { TREZOR_TEST_PREFIX(); + m_no_change_in_tested_tx = true; + const uint64_t amount = MK_TCOINS(1); + const fnc_accept_tx_source_t acceptor = [] (const tx_source_info_crate_t &info, bool &abort) -> bool { return info.td->amount() == amount && info.td->m_subaddr_index.minor == 0; }; t_builder->cur_height(num_blocks(events) - 1) ->mixin(num_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) + ->add_destination(m_eve_account, false, MK_TCOINS(1) - TREZOR_TEST_FEE) // no change ->rct_config(m_rct_config) + ->compute_sources(boost::none, 0, -1, -1, acceptor) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1682,15 +1943,14 @@ bool gen_trezor_1utxo::generate(std::vector& events) bool gen_trezor_1utxo_paymentid_short::generate(std::vector& 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(num_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) + ->compute_sources(1, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1699,16 +1959,15 @@ bool gen_trezor_1utxo_paymentid_short::generate(std::vector& e bool gen_trezor_1utxo_paymentid_short_integrated::generate(std::vector& 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(num_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) + ->compute_sources(1, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1721,9 +1980,9 @@ bool gen_trezor_4utxo::generate(std::vector& events) ->mixin(num_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) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1736,9 +1995,9 @@ bool gen_trezor_4utxo_acc1::generate(std::vector& events) ->mixin(num_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) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1751,9 +2010,9 @@ bool gen_trezor_4utxo_to_sub::generate(std::vector& events) ->mixin(num_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) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1766,10 +2025,10 @@ bool gen_trezor_4utxo_to_2sub::generate(std::vector& events) ->mixin(num_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) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1782,10 +2041,10 @@ bool gen_trezor_4utxo_to_1norm_2sub::generate(std::vector& eve ->mixin(num_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) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) ->rct_config(m_rct_config) ->build_tx(); @@ -1799,11 +2058,11 @@ bool gen_trezor_2utxo_sub_acc_to_1norm_2sub::generate(std::vectormixin(num_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) + ->compute_sources_to_sub_acc(2, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1816,7 +2075,6 @@ bool gen_trezor_4utxo_to_7outs::generate(std::vector& events) ->mixin(num_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) @@ -1825,6 +2083,7 @@ bool gen_trezor_4utxo_to_7outs::generate(std::vector& events) ->add_destination(m_wl_eve->get_subaddress({0, 4}), true, 1000) ->add_destination(m_wl_eve.get(), false, 1000) ->rct_config(m_rct_config) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1837,7 +2096,6 @@ bool gen_trezor_4utxo_to_15outs::generate(std::vector& events) ->mixin(num_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) @@ -1853,6 +2111,22 @@ bool gen_trezor_4utxo_to_15outs::generate(std::vector& events) ->add_destination(m_wl_eve->get_subaddress({0, 4}), true, 1000) ->add_destination(m_wl_eve.get(), false, 1000) ->rct_config(m_rct_config) + ->compute_sources(4, MK_TCOINS(0.0001), -1, -1) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_16utxo_to_sub::generate(std::vector& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(num_mixin()) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->rct_config(m_rct_config) + ->compute_sources(16, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1865,9 +2139,9 @@ bool gen_trezor_many_utxo::generate(std::vector& events) ->mixin(num_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) + ->compute_sources(110, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); @@ -1880,7 +2154,6 @@ bool gen_trezor_many_utxo_many_txo::generate(std::vector& even ->mixin(num_mixin()) ->fee(TREZOR_TEST_FEE) ->from(m_wl_alice.get(), 0) - ->compute_sources(40, MK_COINS(1), -1, -1) ->add_destination(m_eve_account, false, 1000) ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) @@ -1897,11 +2170,125 @@ bool gen_trezor_many_utxo_many_txo::generate(std::vector& even ->add_destination(m_wl_eve->get_subaddress({2, 4}), true, 1000) ->add_destination(m_wl_eve->get_subaddress({3, 4}), true, 1000) ->rct_config(m_rct_config) + ->compute_sources(40, MK_TCOINS(0.0001), -1, -1) ->build_tx(); TREZOR_TEST_SUFFIX(); } +bool gen_trezor_no_passphrase::generate(std::vector& events) +{ + m_trezor_use_passphrase = false; + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(num_mixin()) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->compute_sources(boost::none, MK_TCOINS(0.0001), -1, -1) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); + + return true; +} + +bool gen_trezor_wallet_passphrase::generate(std::vector& events) +{ + m_wallet_dir = boost::filesystem::unique_path(); + boost::filesystem::create_directories(m_wallet_dir); + + m_trezor_use_alice2 = true; + m_trezor_use_passphrase = true; + m_trezor_passphrase = m_alice2_passphrase; + test_setup(events); + + const auto wallet_path = (m_wallet_dir / "alice2").string(); + const epee::wipeable_string& password = epee::wipeable_string("test-pass"); + m_wl_alice2->store_to(wallet_path, password); + + // Positive load + MDEBUG("Loading wallet with correct passphrase"); + m_wl_alice2->callback(this); + m_wl_alice2->load(wallet_path, password); + + // Negative load + m_trezor_passphrase = "bad-passphrase-0112312312312"; + setup_trezor(m_trezor_use_passphrase, m_trezor_pin); + update_client_settings(); + m_wl_alice2->callback(this); + + MDEBUG("Loading wallet with different passphrase - should fail"); + try { + m_wl_alice2->load(wallet_path, password); + CHECK_AND_ASSERT_THROW_MES(false, "Wallet load should not pass with different passphrase"); + } catch (const tools::error::wallet_internal_error & ex) { + CHECK_AND_ASSERT_THROW_MES(ex.to_string().find("does not match wallet") != std::string::npos, "Unexpected wallet exception"); + } + + return true; +} + +gen_trezor_wallet_passphrase::~gen_trezor_wallet_passphrase() +{ + try + { + if (!m_wallet_dir.empty() && boost::filesystem::exists(m_wallet_dir)) + { + boost::filesystem::remove_all(m_wallet_dir); + } + } + catch(...) {} +} + +boost::optional gen_trezor_wallet_passphrase::on_device_pin_request() { + return on_pin_request(); +} + +boost::optional gen_trezor_wallet_passphrase::on_device_passphrase_request(bool &on_device) { + return on_passphrase_request(on_device); +} + +bool gen_trezor_passphrase::generate(std::vector& events) +{ + m_trezor_use_passphrase = true; + m_trezor_use_alice2 = true; + m_trezor_passphrase = m_alice2_passphrase; + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(num_mixin()) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice2.get(), 0) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->compute_sources(boost::none, MK_TCOINS(0.0001), -1, -1) + ->build_tx(); + + auto _dsts = t_builder->build(); + auto _dsts_info = t_builder->dest_info(); + test_trezor_tx(events, _dsts, _dsts_info, generator, TREZOR_ALL_WALLET_VCT, false, 1); + return true; +} + +bool gen_trezor_pin::generate(std::vector& events) +{ + m_trezor_pin = "0000"; + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(num_mixin()) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->compute_sources(boost::none, MK_TCOINS(0.0001), -1, -1) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); + + return true; +} + void wallet_api_tests::init() { m_wallet_dir = boost::filesystem::unique_path(); @@ -1917,10 +2304,7 @@ wallet_api_tests::~wallet_api_tests() boost::filesystem::remove_all(m_wallet_dir); } } - catch(...) - { - MERROR("Could not remove wallet directory"); - } + catch(...) {} } bool wallet_api_tests::generate(std::vector& events) @@ -1931,23 +2315,30 @@ bool wallet_api_tests::generate(std::vector& events) const auto api_net_type = m_network_type == TESTNET ? Monero::TESTNET : Monero::MAINNET; Monero::WalletManager *wmgr = Monero::WalletManagerFactory::getWalletManager(); - std::unique_ptr w{wmgr->createWalletFromDevice(wallet_path, "", api_net_type, m_trezor_path, 1)}; + std::unique_ptr w{wmgr->createWalletFromDevice(wallet_path, "", api_net_type, m_trezor_path, 1, "", 1, this)}; CHECK_AND_ASSERT_THROW_MES(w->init(daemon()->rpc_addr(), 0), "Wallet init fail"); auto walletImpl = dynamic_cast(w.get()); CHECK_AND_ASSERT_THROW_MES(walletImpl, "Dynamic wallet cast failed"); WalletApiAccessorTest::allow_mismatched_daemon_version(walletImpl, true); + walletImpl->setTrustedDaemon(true); CHECK_AND_ASSERT_THROW_MES(w->refresh(), "Refresh fail"); + + uint64_t spent, unspent; + walletImpl->coldKeyImageSync(spent, unspent); + CHECK_AND_ASSERT_THROW_MES(walletImpl->rescanSpent(), "Rescan spent fail"); + uint64_t balance = w->balance(0); MDEBUG("Balance: " << balance); CHECK_AND_ASSERT_THROW_MES(w->status() == Monero::PendingTransaction::Status_Ok, "Status nok, " << w->errorString()); + const uint64_t tx_amount = MK_TCOINS(0.5); auto addr = get_address(m_eve_account); auto recepient_address = cryptonote::get_account_address_as_str(m_network_type, false, addr); Monero::PendingTransaction * transaction = w->createTransaction(recepient_address, "", - MK_COINS(10), + tx_amount, num_mixin(), Monero::PendingTransaction::Priority_Medium, 0, @@ -1955,14 +2346,23 @@ bool wallet_api_tests::generate(std::vector& events) CHECK_AND_ASSERT_THROW_MES(transaction->status() == Monero::PendingTransaction::Status_Ok, "Status nok: " << transaction->status() << ", msg: " << transaction->errorString()); w->refresh(); - CHECK_AND_ASSERT_THROW_MES(w->balance(0) == balance, "Err"); - CHECK_AND_ASSERT_THROW_MES(transaction->amount() == MK_COINS(10), "Err"); - CHECK_AND_ASSERT_THROW_MES(transaction->commit(), "Err"); - CHECK_AND_ASSERT_THROW_MES(w->balance(0) != balance, "Err"); - CHECK_AND_ASSERT_THROW_MES(wmgr->closeWallet(w.get()), "Err"); + CHECK_AND_ASSERT_THROW_MES(w->balance(0) == balance, "Err balance"); + CHECK_AND_ASSERT_THROW_MES(transaction->amount() == tx_amount, "Err amount"); + CHECK_AND_ASSERT_THROW_MES(transaction->commit(), "Err commit"); + CHECK_AND_ASSERT_THROW_MES(w->balance(0) != balance, "Err balance2"); + CHECK_AND_ASSERT_THROW_MES(wmgr->closeWallet(w.get()), "Err close"); (void)w.release(); mine_and_test(events); return true; } +Monero::optional wallet_api_tests::onDevicePinRequest() { + return Monero::optional(m_trezor_pin); +} + +Monero::optional wallet_api_tests::onDevicePassphraseRequest(bool &on_device) { + on_device = false; + return Monero::optional(m_trezor_passphrase); +} + diff --git a/tests/trezor/trezor_tests.h b/tests/trezor/trezor_tests.h index 0f9ee30ca..16d7641db 100644 --- a/tests/trezor/trezor_tests.h +++ b/tests/trezor/trezor_tests.h @@ -36,16 +36,24 @@ #include "../core_tests/chaingen.h" #include "../core_tests/wallet_tools.h" -#define TREZOR_TEST_FEE 90000000000 #define TREZOR_TEST_CLSAG_MIXIN 11 #define TREZOR_TEST_HF15_MIXIN 16 #define TREZOR_TEST_MIXIN TREZOR_TEST_CLSAG_MIXIN +#define TREZOR_TEST_MIN_HF_DEFAULT HF_VERSION_BULLETPROOF_PLUS +#define TREZOR_TEST_MAX_HF_DEFAULT HF_VERSION_BULLETPROOF_PLUS +#define TREZOR_TEST_KI_SYNC_DEFAULT 1 +#define TREZOR_TEST_MINING_ENABLED_DEFAULT false +#define TREZOR_TEST_MINING_TIMEOUT_DEFAULT 360 + +#define MK_TCOINS(amount) ((unsigned long long) ((amount) * (COIN))) +#define TREZOR_TEST_FEE_FRAC 0.1 +#define TREZOR_TEST_FEE MK_TCOINS(TREZOR_TEST_FEE_FRAC) /************************************************************************/ /* */ /************************************************************************/ class tsx_builder; -class gen_trezor_base : public test_chain_unit_base +class gen_trezor_base : public test_chain_unit_base, public hw::i_device_callback { public: friend class tsx_builder; @@ -54,7 +62,7 @@ public: gen_trezor_base(const gen_trezor_base &other); virtual ~gen_trezor_base() {}; - virtual void setup_args(const std::string & trezor_path, bool heavy_tests=false); + virtual void setup_args(const std::string & trezor_path, bool heavy_tests, bool mining_enabled, long mining_timeout); virtual bool generate(std::vector& events); virtual void load(std::vector& events); // load events, init test obj virtual void fix_hf(std::vector& events); @@ -76,42 +84,58 @@ public: std::vector& dsts_info, test_generator &generator, std::vector wallets, - bool is_sweep=false); + bool is_sweep=false, + size_t sender_idx=0); virtual void test_get_tx( std::vector& events, std::vector wallets, + const cryptonote::account_base * account, const std::vector &ptxs, const std::vector &aux_tx_info); virtual void mine_and_test(std::vector& events); virtual void rewind_blocks(std::vector& events, size_t rewind_n, uint8_t hf); + virtual cryptonote::block rewind_blocks_with_decoys(std::vector& events, cryptonote::block & head, size_t rewind_n, uint8_t hf, size_t num_decoys_per_block); virtual void set_hard_fork(uint8_t hf); crypto::hash head_hash() const { return get_block_hash(m_head); } cryptonote::block head_block() const { return m_head; } bool heavy_tests() const { return m_heavy_tests; } + bool heavy_test_set() const { return m_gen_heavy_test_set; } + void heavy_test_set(bool set) { m_gen_heavy_test_set = set; } void rct_config(rct::RCTConfig rct_config) { m_rct_config = rct_config; } - uint8_t cur_hf() const { return m_hard_forks.size() > 0 ? m_hard_forks.back().first : 0; } + uint8_t cur_hf() const { return !m_hard_forks.empty() ? m_hard_forks.back().first : 0; } size_t num_mixin() const { return m_top_hard_fork >= HF_VERSION_BULLETPROOF_PLUS ? TREZOR_TEST_HF15_MIXIN : TREZOR_TEST_CLSAG_MIXIN; } cryptonote::network_type nettype() const { return m_network_type; } std::shared_ptr daemon() const { return m_daemon; } void daemon(std::shared_ptr daemon){ m_daemon = std::move(daemon); } + boost::optional on_pin_request() override; + boost::optional on_passphrase_request(bool &on_device) override; + // 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_miner_master_seed_str; 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; + static const std::string m_alice2_passphrase; + static const std::string m_alice2_master_seed; + static const std::string m_bob_master_seed; + static const std::string m_eve_master_seed; protected: - virtual void setup_trezor(); - virtual void init_fields(); + virtual void setup_trezor(bool use_passphrase, const std::string & pin); + virtual void init_accounts(); + virtual void init_wallets(); + virtual void init_trezor_account(); + virtual void log_wallets_desc(); virtual void update_client_settings(); virtual bool verify_tx_key(const ::crypto::secret_key & tx_priv, const ::crypto::public_key & tx_pub, const subaddresses_t & subs); @@ -126,17 +150,27 @@ protected: std::vector m_events; std::string m_trezor_path; + std::string m_trezor_pin; + std::string m_trezor_passphrase; + bool m_trezor_use_passphrase = true; + bool m_trezor_use_alice2 = false; bool m_heavy_tests; + bool m_gen_heavy_test_set; bool m_test_get_tx_key; rct::RCTConfig m_rct_config; bool m_live_refresh_enabled; + bool m_mining_enabled = TREZOR_TEST_MINING_ENABLED_DEFAULT; + long m_mining_timeout = TREZOR_TEST_MINING_TIMEOUT_DEFAULT; + bool m_no_change_in_tested_tx = false; cryptonote::account_base m_miner_account; cryptonote::account_base m_bob_account; cryptonote::account_base m_alice_account; cryptonote::account_base m_eve_account; + cryptonote::account_base m_alice2_account; hw::trezor::device_trezor * m_trezor; std::unique_ptr m_wl_alice; + std::unique_ptr m_wl_alice2; std::unique_ptr m_wl_bob; std::unique_ptr m_wl_eve; @@ -165,9 +199,9 @@ public: 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 & sources, std::vector & selected_transfers); - tsx_builder * compute_sources(boost::optional num_utxo=boost::none, boost::optional min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional fnc_accept=boost::none); - tsx_builder * compute_sources_to_sub(boost::optional num_utxo=boost::none, boost::optional min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional fnc_accept=boost::none); - tsx_builder * compute_sources_to_sub_acc(boost::optional num_utxo=boost::none, boost::optional min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional fnc_accept=boost::none); + tsx_builder * compute_sources(boost::optional num_utxo=boost::none, uint64_t extra_amount=0, ssize_t offset=-1, int step=1, boost::optional fnc_accept=boost::none); + tsx_builder * compute_sources_to_sub(boost::optional num_utxo=boost::none, uint64_t extra_amount=0, ssize_t offset=-1, int step=1, boost::optional fnc_accept=boost::none); + tsx_builder * compute_sources_to_sub_acc(boost::optional num_utxo=boost::none, uint64_t extra_amount=0, ssize_t offset=-1, int step=1, boost::optional fnc_accept=boost::none); tsx_builder * destinations(std::vector &dsts); tsx_builder * add_destination(const cryptonote::tx_destination_entry &dst); @@ -208,11 +242,12 @@ class device_trezor_test : public hw::trezor::device_trezor { public: size_t m_tx_sign_ctr; size_t m_compute_key_image_ctr; + std::unordered_set m_live_synced_ki; device_trezor_test(); void clear_test_counters(); - void setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type); + void setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type, bool use_passphrase=false, const std::string & pin = ""); bool compute_key_image(const ::cryptonote::account_keys &ack, const ::crypto::public_key &out_key, const ::crypto::key_derivation &recv_derivation, size_t real_output_index, @@ -315,6 +350,12 @@ public: bool generate(std::vector& events) override; }; +class gen_trezor_16utxo_to_sub : public gen_trezor_base +{ +public: + bool generate(std::vector& events) override; +}; + class gen_trezor_many_utxo : public gen_trezor_base { public: @@ -327,14 +368,52 @@ public: bool generate(std::vector& events) override; }; -// Wallet::API tests -class wallet_api_tests : public gen_trezor_base +class gen_trezor_no_passphrase : public gen_trezor_base { public: - virtual ~wallet_api_tests(); + bool generate(std::vector& events) override; +}; + +class gen_trezor_wallet_passphrase : public gen_trezor_base, public tools::i_wallet2_callback +{ +public: + ~gen_trezor_wallet_passphrase() override; + bool generate(std::vector& events) override; + boost::optional on_device_pin_request() override; + boost::optional on_device_passphrase_request(bool &on_device) override; +protected: + boost::filesystem::path m_wallet_dir; +}; + +class gen_trezor_passphrase : public gen_trezor_base +{ +public: + bool generate(std::vector& events) override; +}; + +class gen_trezor_pin : public gen_trezor_base +{ +public: + bool generate(std::vector& events) override; +}; + +// Wallet::API tests +class wallet_api_tests : public gen_trezor_base, public Monero::WalletListener +{ +public: + ~wallet_api_tests() override; void init(); bool generate(std::vector& events) override; + Monero::optional onDevicePinRequest() override; + Monero::optional onDevicePassphraseRequest(bool &on_device) override; + void moneySpent(const std::string &txId, uint64_t amount) override {}; + void moneyReceived(const std::string &txId, uint64_t amount) override {}; + void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override {}; + void newBlock(uint64_t height) override {}; + void updated() override {}; + void refreshed() override {}; + protected: boost::filesystem::path m_wallet_dir; }; \ No newline at end of file