From f854e0e88be2b9e2b6575d185c3fd9fc070bc5cf Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 28 Feb 2019 17:57:01 +0100 Subject: [PATCH] tests: wallet dependent code moved to wallet_tools --- tests/core_tests/CMakeLists.txt | 6 +- tests/core_tests/chaingen.cpp | 278 +--------------------------- tests/core_tests/chaingen.h | 57 +----- tests/core_tests/wallet_tools.cpp | 289 ++++++++++++++++++++++++++++++ tests/core_tests/wallet_tools.h | 86 +++++++++ tests/trezor/CMakeLists.txt | 6 +- tests/trezor/trezor_tests.cpp | 6 + tests/trezor/trezor_tests.h | 2 + 8 files changed, 395 insertions(+), 335 deletions(-) create mode 100644 tests/core_tests/wallet_tools.cpp create mode 100644 tests/core_tests/wallet_tools.h diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index dc3fa703fa9..2053534164b 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -42,7 +42,8 @@ set(core_tests_sources tx_validation.cpp v2_tests.cpp rct.cpp - bulletproofs.cpp) + bulletproofs.cpp + wallet_tools.cpp) set(core_tests_headers block_reward.h @@ -60,7 +61,8 @@ set(core_tests_headers tx_validation.h v2_tests.h rct.h - bulletproofs.h) + bulletproofs.h + wallet_tools.h) add_executable(core_tests ${core_tests_sources} diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 781c5bdc893..2075cfb7ddf 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -547,7 +547,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){ auto & vct = m_outs[amount]; const size_t n_outs = vct.size(); @@ -675,8 +675,6 @@ cryptonote::account_public_address get_address(const var_addr_t& inp) return boost::get(inp).get_keys().m_account_address; } else if (typeid(cryptonote::tx_destination_entry) == inp.type()){ return boost::get(inp).addr; - } else if (typeid(tools::wallet2*) == inp.type()){ - return boost::get(inp)->get_account().get_keys().m_account_address; } else { throw std::runtime_error("Unexpected type"); } @@ -702,249 +700,6 @@ cryptonote::account_public_address get_address(const cryptonote::tx_destination_ return inp.addr; } -cryptonote::account_public_address get_address(const tools::wallet2 * inp) -{ - return inp->get_account().get_keys().m_account_address; -} - -void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::account_base& account) -{ - wallet->clear(); - wallet->m_account = account; - wallet->m_nettype = MAINNET; - - wallet->m_key_device_type = account.get_device().get_type(); - wallet->m_account_public_address = account.get_keys().m_account_address; - wallet->m_watch_only = false; - wallet->m_multisig = false; - wallet->m_multisig_threshold = 0; - wallet->m_multisig_signers.clear(); - wallet->m_device_name = account.get_device().get_name(); - - wallet->m_subaddress_lookahead_major = 5; - wallet->m_subaddress_lookahead_minor = 20; - - wallet->setup_new_blockchain(); // generates also subadress register -} - -void wallet_accessor_test::process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added) -{ - wallet->process_parsed_blocks(start_height, blocks, parsed_blocks, blocks_added); -} - -void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional& blk_tail) -{ - map_hash2tx_t mtx; - std::vector blockchain; - find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)); - - if (blk_tail){ - trim_block_chain(blockchain, blk_tail.get()); - } - - process_transactions(wallet, blockchain, mtx, bt); -} - -void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) -{ - uint64_t start_height=0, blocks_added=0; - std::vector v_bche; - std::vector v_parsed_block; - - v_bche.reserve(blockchain.size()); - v_parsed_block.reserve(blockchain.size()); - - size_t idx = 0; - for(auto bl : blockchain) - { - idx += 1; - uint64_t height; - v_bche.emplace_back(); - v_parsed_block.emplace_back(); - - wallet_tools::gen_block_data(bt, bl, mtx, v_bche.back(), v_parsed_block.back(), idx == 1 ? start_height : height); - } - - if (wallet) - wallet_accessor_test::process_parsed_blocks(wallet, start_height, v_bche, v_parsed_block, blocks_added); -} - -bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector& 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) -{ - CHECK_AND_ASSERT_THROW_MES(step != 0, "Step is zero"); - sources.clear(); - - auto & transfers = wallet_accessor_test::get_transfers(wallet); - std::unordered_set selected_idx; - std::unordered_set selected_kis; - const size_t ntrans = wallet->get_num_transfer_details(); - size_t roffset = offset >= 0 ? offset : ntrans - offset - 1; - size_t iters = 0; - uint64_t sum = 0; - size_t cur_utxo = 0; - bool abort = false; - unsigned brk_cond = 0; - unsigned brk_thresh = num_utxo && min_amount ? 2 : (num_utxo || min_amount ? 1 : 0); - -#define EVAL_BRK_COND() do { \ - brk_cond = 0; \ - if (num_utxo && num_utxo.get() <= cur_utxo) \ - brk_cond += 1; \ - if (min_amount && min_amount.get() <= sum) \ - brk_cond += 1; \ - } while(0) - - for(ssize_t i = roffset; iters < ntrans && !abort; i += step, ++iters) - { - EVAL_BRK_COND(); - if (brk_cond >= brk_thresh) - break; - - i = i < 0 ? (i + ntrans) : i % ntrans; - auto & td = transfers[i]; - if (td.m_spent) - continue; - if (td.m_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW > cur_height) - continue; - if (selected_idx.find((size_t)i) != selected_idx.end()){ - MERROR("Should not happen (selected_idx not found): " << i); - continue; - } - if (selected_kis.find(td.m_key_image) != selected_kis.end()){ - MERROR("Should not happen (selected KI): " << i << "ki: " << dump_keys(td.m_key_image.data)); - continue; - } - - try { - cryptonote::tx_source_entry src; - wallet_tools::gen_tx_src(mixin, cur_height, td, src, bt); - - // Acceptor function - if (fnc_accept){ - tx_source_info_crate_t c_info{.td=&td, .src=&src, .selected_idx=&selected_idx, .selected_kis=&selected_kis, - .ntrans=ntrans, .iters=iters, .sum=sum, .cur_utxo=cur_utxo}; - - bool take_it = (fnc_accept.get())(c_info, abort); - if (!take_it){ - continue; - } - } - - MINFO("Selected " << i << " from tx: " << dump_keys(td.m_txid.data) - << " ki: " << dump_keys(td.m_key_image.data) - << " amnt: " << td.amount() - << " rct: " << td.is_rct() - << " glob: " << td.m_global_output_index); - - sum += td.amount(); - cur_utxo += 1; - - sources.emplace_back(src); - selected.push_back((size_t)i); - selected_idx.insert((size_t)i); - selected_kis.insert(td.m_key_image); - - } catch(const std::exception &e){ - MTRACE("Output " << i << ", from: " << dump_keys(td.m_txid.data) - << ", amnt: " << td.amount() << ", rct: " << td.is_rct() - << ", glob: " << td.m_global_output_index << " is not applicable: " << e.what()); - } - } - - EVAL_BRK_COND(); - return brk_cond >= brk_thresh; -#undef EVAL_BRK_COND -} - -void wallet_tools::gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt) -{ - src.amount = td.amount(); - src.rct = td.is_rct(); - - std::vector outs; - bt.get_fake_outs(mixin, td.is_rct() ? 0 : td.amount(), td.m_global_output_index, cur_height, outs); - - for (size_t n = 0; n < mixin; ++n) - { - cryptonote::tx_source_entry::output_entry oe; - oe.first = std::get<0>(outs[n]); - oe.second.dest = rct::pk2rct(std::get<1>(outs[n])); - oe.second.mask = std::get<2>(outs[n]); - src.outputs.push_back(oe); - } - - std::uniform_int_distribution<> dis(0, mixin - 1); - size_t real_idx = (size_t)dis(MT); - - cryptonote::tx_source_entry::output_entry &real_oe = src.outputs[real_idx]; - real_oe.first = td.m_global_output_index; - real_oe.second.dest = rct::pk2rct(boost::get(td.m_tx.vout[td.m_internal_output_index].target).key); - real_oe.second.mask = rct::commit(td.amount(), td.m_mask); - - std::sort(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry i0, const cryptonote::tx_source_entry::output_entry i1) { - return i0.first < i1.first; - }); - - for (size_t i = 0; i < src.outputs.size(); ++i){ - if (src.outputs[i].first == td.m_global_output_index){ - src.real_output = i; - break; - } - } - - src.mask = td.m_mask; - src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index); - src.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); - src.real_output_in_tx_index = td.m_internal_output_index; - src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); -} - -void wallet_tools::gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t &mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height) -{ - vector vtx; - vtx.push_back(&(bl->miner_tx)); - height = boost::get(*bl->miner_tx.vin.begin()).height; - - BOOST_FOREACH(const crypto::hash &h, bl->tx_hashes) { - const map_hash2tx_t::const_iterator cit = mtx.find(h); - CHECK_AND_ASSERT_THROW_MES(mtx.end() != cit, "block contains an unknown tx hash @ " << height << ", " << h); - vtx.push_back(cit->second); - } - - bche.block = "NA"; - bche.txs.resize(bl->tx_hashes.size()); - - parsed_block.error = false; - parsed_block.hash = get_block_hash(*bl); - parsed_block.block = *bl; - parsed_block.txes.reserve(bl->tx_hashes.size()); - - auto & o_indices = parsed_block.o_indices.indices; - o_indices.reserve(bl->tx_hashes.size() + 1); - - size_t cur = 0; - BOOST_FOREACH(const transaction *tx, vtx){ - cur += 1; - o_indices.emplace_back(); - bt.process(bl, tx, cur - 1); - bt.global_indices(tx, o_indices.back().indices); - - if (cur > 1) // miner not included - parsed_block.txes.push_back(*tx); - } -} - -void wallet_tools::compute_subaddresses(std::unordered_map &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors) -{ - auto &hwdev = hw::get_device("default"); - const std::vector pkeys = hwdev.get_subaddress_spend_public_keys(creds.get_keys(), account, 0, minors); - - for(uint32_t c = 0; c < pkeys.size(); ++c){ - cryptonote::subaddress_index sidx{(uint32_t)account, c}; - subaddresses[pkeys[c]] = sidx; - } -} - uint64_t sum_amount(const std::vector& destinations) { uint64_t amount = 0; @@ -1163,27 +918,6 @@ bool construct_tx_to_key(cryptonote::transaction& tx, return construct_tx_rct(from.get_keys(), sources, all_destinations, get_address(from), std::vector(), tx, 0, rct, range_proof_type, bp_version); } -bool construct_tx_to_key(cryptonote::transaction& tx, - tools::wallet2 * sender_wallet, const var_addr_t& to, uint64_t amount, - std::vector &sources, - uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) -{ - vector destinations; - fill_tx_destinations(sender_wallet->get_account(), get_address(to), amount, fee, sources, destinations, rct); - return construct_tx_rct(sender_wallet, sources, destinations, get_address(sender_wallet), std::vector(), tx, 0, rct, range_proof_type, bp_version); -} - -bool construct_tx_to_key(cryptonote::transaction& tx, - tools::wallet2 * sender_wallet, - const std::vector& destinations, - std::vector &sources, - uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) -{ - vector all_destinations; - fill_tx_destinations(sender_wallet->get_account(), destinations, fee, sources, all_destinations, rct); - return construct_tx_rct(sender_wallet, sources, all_destinations, get_address(sender_wallet), std::vector(), tx, 0, rct, range_proof_type, bp_version); -} - bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) { std::unordered_map subaddresses; @@ -1195,16 +929,6 @@ bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std:: return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); } -bool construct_tx_rct(tools::wallet2 * sender_wallet, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) -{ - subaddresses_t & subaddresses = wallet_accessor_test::get_subaddresses(sender_wallet); - crypto::secret_key tx_key; - std::vector additional_tx_keys; - std::vector destinations_copy = destinations; - rct::RCTConfig rct_config = {range_proof_type, bp_version}; - return construct_tx_and_get_tx_key(sender_wallet->get_account().get_keys(), subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); -} - transaction construct_tx_with_fee(std::vector& events, const block& blk_head, const account_base& acc_from, const var_addr_t& to, uint64_t amount, uint64_t fee) { diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 9086b10b80d..0e67a7e654f 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -55,7 +55,6 @@ #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "misc_language.h" -#include "wallet/wallet2.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "tests.core" @@ -345,6 +344,7 @@ struct output_index { } }; +typedef std::tuple get_outs_entry; typedef std::pair output_hasher; typedef boost::hash output_hasher_hasher; typedef std::map > map_output_t; @@ -354,7 +354,7 @@ typedef std::unordered_map ma typedef std::unordered_map subaddresses_t; typedef std::pair outloc_t; -typedef boost::variant var_addr_t; +typedef boost::variant var_addr_t; typedef struct { const var_addr_t addr; bool is_subaddr; @@ -377,7 +377,7 @@ class block_tracker 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); std::string dump_data(); void dump_data(const std::string & fname); @@ -400,42 +400,6 @@ cryptonote::account_public_address get_address(const cryptonote::account_public_ cryptonote::account_public_address get_address(const cryptonote::account_keys& inp); cryptonote::account_public_address get_address(const cryptonote::account_base& inp); cryptonote::account_public_address get_address(const cryptonote::tx_destination_entry& inp); -cryptonote::account_public_address get_address(const tools::wallet2 * inp); - -typedef struct { - tools::wallet2::transfer_details * td; - cryptonote::tx_source_entry * src; - - std::unordered_set * selected_idx; - std::unordered_set * selected_kis; - size_t ntrans; - size_t iters; - uint64_t sum; - size_t cur_utxo; -} tx_source_info_crate_t; - -typedef std::function fnc_accept_tx_source_t; - -// Wallet friend, direct access to required fields and private methods -class wallet_accessor_test -{ -public: - static void set_account(tools::wallet2 * wallet, cryptonote::account_base& account); - static tools::wallet2::transfer_container & get_transfers(tools::wallet2 * wallet) { return wallet->m_transfers; } - static subaddresses_t & get_subaddresses(tools::wallet2 * wallet) { return wallet->m_subaddresses; } - static void process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added); -}; - -class wallet_tools -{ -public: - static void gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt); - static void gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t & mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height); - static void compute_subaddresses(std::unordered_map &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(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); -}; inline cryptonote::difficulty_type get_test_difficulty(const boost::optional& hf_ver=boost::none) {return !hf_ver || hf_ver.get() <= 1 ? 1 : 2;} inline uint64_t current_difficulty_window(const boost::optional& hf_ver=boost::none){ return !hf_ver || hf_ver.get() <= 1 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; } @@ -467,14 +431,6 @@ bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_ std::vector &sources, uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); -bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * from_wallet, const var_addr_t& to, uint64_t amount, - std::vector &sources, - uint64_t fee, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); - -bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * sender_wallet, const std::vector& destinations, - std::vector &sources, - uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); - cryptonote::transaction construct_tx_with_fee(std::vector& events, const cryptonote::block& blk_head, const cryptonote::account_base& acc_from, const var_addr_t& to, uint64_t amount, uint64_t fee); @@ -486,13 +442,6 @@ bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::vector extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); -bool construct_tx_rct(tools::wallet2 * sender_wallet, - std::vector& sources, - const std::vector& destinations, - const boost::optional& change_addr, - std::vector extra, cryptonote::transaction& tx, uint64_t unlock_time, - bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); - uint64_t num_blocks(const std::vector& events); cryptonote::block get_head_block(const std::vector& events); diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp new file mode 100644 index 00000000000..b0592658ca1 --- /dev/null +++ b/tests/core_tests/wallet_tools.cpp @@ -0,0 +1,289 @@ +// +// Created by Dusan Klinec on 2019-02-28. +// + +#include "wallet_tools.h" +#include + +using namespace std; +using namespace epee; +using namespace crypto; +using namespace cryptonote; + +// Shared random generator +static std::random_device RD; +static std::mt19937 MT(RD()); + +void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::account_base& account) +{ + wallet->clear(); + wallet->m_account = account; + wallet->m_nettype = MAINNET; + + wallet->m_key_device_type = account.get_device().get_type(); + wallet->m_account_public_address = account.get_keys().m_account_address; + wallet->m_watch_only = false; + wallet->m_multisig = false; + wallet->m_multisig_threshold = 0; + wallet->m_multisig_signers.clear(); + wallet->m_device_name = account.get_device().get_name(); + + wallet->m_subaddress_lookahead_major = 5; + wallet->m_subaddress_lookahead_minor = 20; + + wallet->setup_new_blockchain(); // generates also subadress register +} + +void wallet_accessor_test::process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added) +{ + wallet->process_parsed_blocks(start_height, blocks, parsed_blocks, blocks_added); +} + +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional& blk_tail) +{ + map_hash2tx_t mtx; + std::vector blockchain; + find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)); + + if (blk_tail){ + trim_block_chain(blockchain, blk_tail.get()); + } + + process_transactions(wallet, blockchain, mtx, bt); +} + +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) +{ + uint64_t start_height=0, blocks_added=0; + std::vector v_bche; + std::vector v_parsed_block; + + v_bche.reserve(blockchain.size()); + v_parsed_block.reserve(blockchain.size()); + + size_t idx = 0; + for(auto bl : blockchain) + { + idx += 1; + uint64_t height; + v_bche.emplace_back(); + v_parsed_block.emplace_back(); + + wallet_tools::gen_block_data(bt, bl, mtx, v_bche.back(), v_parsed_block.back(), idx == 1 ? start_height : height); + } + + if (wallet) + wallet_accessor_test::process_parsed_blocks(wallet, start_height, v_bche, v_parsed_block, blocks_added); +} + +bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector& 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) +{ + CHECK_AND_ASSERT_THROW_MES(step != 0, "Step is zero"); + sources.clear(); + + auto & transfers = wallet_accessor_test::get_transfers(wallet); + std::unordered_set selected_idx; + std::unordered_set selected_kis; + const size_t ntrans = wallet->get_num_transfer_details(); + size_t roffset = offset >= 0 ? offset : ntrans - offset - 1; + size_t iters = 0; + uint64_t sum = 0; + size_t cur_utxo = 0; + bool abort = false; + unsigned brk_cond = 0; + unsigned brk_thresh = num_utxo && min_amount ? 2 : (num_utxo || min_amount ? 1 : 0); + +#define EVAL_BRK_COND() do { \ + brk_cond = 0; \ + if (num_utxo && num_utxo.get() <= cur_utxo) \ + brk_cond += 1; \ + if (min_amount && min_amount.get() <= sum) \ + brk_cond += 1; \ + } while(0) + + for(ssize_t i = roffset; iters < ntrans && !abort; i += step, ++iters) + { + EVAL_BRK_COND(); + if (brk_cond >= brk_thresh) + break; + + i = i < 0 ? (i + ntrans) : i % ntrans; + auto & td = transfers[i]; + if (td.m_spent) + continue; + if (td.m_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW > cur_height) + continue; + if (selected_idx.find((size_t)i) != selected_idx.end()){ + MERROR("Should not happen (selected_idx not found): " << i); + continue; + } + if (selected_kis.find(td.m_key_image) != selected_kis.end()){ + MERROR("Should not happen (selected KI): " << i << "ki: " << dump_keys(td.m_key_image.data)); + continue; + } + + try { + cryptonote::tx_source_entry src; + wallet_tools::gen_tx_src(mixin, cur_height, td, src, bt); + + // Acceptor function + if (fnc_accept){ + tx_source_info_crate_t c_info{.td=&td, .src=&src, .selected_idx=&selected_idx, .selected_kis=&selected_kis, + .ntrans=ntrans, .iters=iters, .sum=sum, .cur_utxo=cur_utxo}; + + bool take_it = (fnc_accept.get())(c_info, abort); + if (!take_it){ + continue; + } + } + + MINFO("Selected " << i << " from tx: " << dump_keys(td.m_txid.data) + << " ki: " << dump_keys(td.m_key_image.data) + << " amnt: " << td.amount() + << " rct: " << td.is_rct() + << " glob: " << td.m_global_output_index); + + sum += td.amount(); + cur_utxo += 1; + + sources.emplace_back(src); + selected.push_back((size_t)i); + selected_idx.insert((size_t)i); + selected_kis.insert(td.m_key_image); + + } catch(const std::exception &e){ + MTRACE("Output " << i << ", from: " << dump_keys(td.m_txid.data) + << ", amnt: " << td.amount() << ", rct: " << td.is_rct() + << ", glob: " << td.m_global_output_index << " is not applicable: " << e.what()); + } + } + + EVAL_BRK_COND(); + return brk_cond >= brk_thresh; +#undef EVAL_BRK_COND +} + +void wallet_tools::gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt) +{ + src.amount = td.amount(); + src.rct = td.is_rct(); + + std::vector outs; + bt.get_fake_outs(mixin, td.is_rct() ? 0 : td.amount(), td.m_global_output_index, cur_height, outs); + + for (size_t n = 0; n < mixin; ++n) + { + cryptonote::tx_source_entry::output_entry oe; + oe.first = std::get<0>(outs[n]); + oe.second.dest = rct::pk2rct(std::get<1>(outs[n])); + oe.second.mask = std::get<2>(outs[n]); + src.outputs.push_back(oe); + } + + std::uniform_int_distribution<> dis(0, mixin - 1); + size_t real_idx = (size_t)dis(MT); + + cryptonote::tx_source_entry::output_entry &real_oe = src.outputs[real_idx]; + real_oe.first = td.m_global_output_index; + real_oe.second.dest = rct::pk2rct(boost::get(td.m_tx.vout[td.m_internal_output_index].target).key); + real_oe.second.mask = rct::commit(td.amount(), td.m_mask); + + std::sort(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry i0, const cryptonote::tx_source_entry::output_entry i1) { + return i0.first < i1.first; + }); + + for (size_t i = 0; i < src.outputs.size(); ++i){ + if (src.outputs[i].first == td.m_global_output_index){ + src.real_output = i; + break; + } + } + + src.mask = td.m_mask; + src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index); + src.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + src.real_output_in_tx_index = td.m_internal_output_index; + src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); +} + +void wallet_tools::gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t &mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height) +{ + vector vtx; + vtx.push_back(&(bl->miner_tx)); + height = boost::get(*bl->miner_tx.vin.begin()).height; + + BOOST_FOREACH(const crypto::hash &h, bl->tx_hashes) { + const map_hash2tx_t::const_iterator cit = mtx.find(h); + CHECK_AND_ASSERT_THROW_MES(mtx.end() != cit, "block contains an unknown tx hash @ " << height << ", " << h); + vtx.push_back(cit->second); + } + + bche.block = "NA"; + bche.txs.resize(bl->tx_hashes.size()); + + parsed_block.error = false; + parsed_block.hash = get_block_hash(*bl); + parsed_block.block = *bl; + parsed_block.txes.reserve(bl->tx_hashes.size()); + + auto & o_indices = parsed_block.o_indices.indices; + o_indices.reserve(bl->tx_hashes.size() + 1); + + size_t cur = 0; + BOOST_FOREACH(const transaction *tx, vtx){ + cur += 1; + o_indices.emplace_back(); + bt.process(bl, tx, cur - 1); + bt.global_indices(tx, o_indices.back().indices); + + if (cur > 1) // miner not included + parsed_block.txes.push_back(*tx); + } +} + +void wallet_tools::compute_subaddresses(std::unordered_map &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors) +{ + auto &hwdev = hw::get_device("default"); + const std::vector pkeys = hwdev.get_subaddress_spend_public_keys(creds.get_keys(), account, 0, minors); + + for(uint32_t c = 0; c < pkeys.size(); ++c){ + cryptonote::subaddress_index sidx{(uint32_t)account, c}; + subaddresses[pkeys[c]] = sidx; + } +} + +cryptonote::account_public_address get_address(const tools::wallet2* inp) +{ + return (inp)->get_account().get_keys().m_account_address; +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + tools::wallet2 * sender_wallet, const var_addr_t& to, uint64_t amount, + std::vector &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector destinations; + fill_tx_destinations(sender_wallet->get_account(), get_address(to), amount, fee, sources, destinations, rct); + return construct_tx_rct(sender_wallet, sources, destinations, get_address(sender_wallet), std::vector(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + tools::wallet2 * sender_wallet, + const std::vector& destinations, + std::vector &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector all_destinations; + fill_tx_destinations(sender_wallet->get_account(), destinations, fee, sources, all_destinations, rct); + return construct_tx_rct(sender_wallet, sources, all_destinations, get_address(sender_wallet), std::vector(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_rct(tools::wallet2 * sender_wallet, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + subaddresses_t & subaddresses = wallet_accessor_test::get_subaddresses(sender_wallet); + crypto::secret_key tx_key; + std::vector additional_tx_keys; + std::vector destinations_copy = destinations; + rct::RCTConfig rct_config = {range_proof_type, bp_version}; + return construct_tx_and_get_tx_key(sender_wallet->get_account().get_keys(), subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); +} diff --git a/tests/core_tests/wallet_tools.h b/tests/core_tests/wallet_tools.h new file mode 100644 index 00000000000..03db04c9908 --- /dev/null +++ b/tests/core_tests/wallet_tools.h @@ -0,0 +1,86 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "chaingen.h" +#include "wallet/wallet2.h" + +typedef struct { + tools::wallet2::transfer_details * td; + cryptonote::tx_source_entry * src; + + std::unordered_set * selected_idx; + std::unordered_set * selected_kis; + size_t ntrans; + size_t iters; + uint64_t sum; + size_t cur_utxo; +} tx_source_info_crate_t; + +typedef std::function fnc_accept_tx_source_t; + +// Wallet friend, direct access to required fields and private methods +class wallet_accessor_test +{ +public: + static void set_account(tools::wallet2 * wallet, cryptonote::account_base& account); + static tools::wallet2::transfer_container & get_transfers(tools::wallet2 * wallet) { return wallet->m_transfers; } + static subaddresses_t & get_subaddresses(tools::wallet2 * wallet) { return wallet->m_subaddresses; } + static void process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added); +}; + +class wallet_tools +{ +public: + static void gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt); + static void gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t & mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height); + static void compute_subaddresses(std::unordered_map &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(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); +}; + +cryptonote::account_public_address get_address(const tools::wallet2*); + +bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * from_wallet, const var_addr_t& to, uint64_t amount, + std::vector &sources, + uint64_t fee, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * sender_wallet, const std::vector& destinations, + std::vector &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); + +bool construct_tx_rct(tools::wallet2 * sender_wallet, + std::vector& sources, + const std::vector& destinations, + const boost::optional& change_addr, + std::vector extra, cryptonote::transaction& tx, uint64_t unlock_time, + bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); diff --git a/tests/trezor/CMakeLists.txt b/tests/trezor/CMakeLists.txt index 7b4df40ac85..67c2f8438c6 100644 --- a/tests/trezor/CMakeLists.txt +++ b/tests/trezor/CMakeLists.txt @@ -28,11 +28,13 @@ set(trezor_tests_sources trezor_tests.cpp - ../core_tests/chaingen.cpp) + ../core_tests/chaingen.cpp + ../core_tests/wallet_tools.cpp) set(trezor_tests_headers trezor_tests.h - ../core_tests/chaingen.h) + ../core_tests/chaingen.h + ../core_tests/wallet_tools.h) add_executable(trezor_tests ${trezor_tests_sources} diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp index 1b3a86b8a9b..c2b46f6987e 100644 --- a/tests/trezor/trezor_tests.cpp +++ b/tests/trezor/trezor_tests.cpp @@ -1070,6 +1070,12 @@ tsx_builder * tsx_builder::add_destination(const var_addr_t addr, bool is_subadd return this; } +tsx_builder * tsx_builder::add_destination(const tools::wallet2 * wallet, bool is_subaddr, uint64_t amount) +{ + m_destinations_orig.push_back(build_dst(get_address(wallet), is_subaddr, amount)); + return this; +} + tsx_builder * tsx_builder::set_integrated(size_t idx) { m_integrated.insert(idx); diff --git a/tests/trezor/trezor_tests.h b/tests/trezor/trezor_tests.h index 307368cc304..41db1cce503 100644 --- a/tests/trezor/trezor_tests.h +++ b/tests/trezor/trezor_tests.h @@ -32,6 +32,7 @@ #include #include "../core_tests/chaingen.h" +#include "../core_tests/wallet_tools.h" #define TREZOR_TEST_FEE 90000000000 #define TREZOR_TEST_MIXIN 11 @@ -136,6 +137,7 @@ class tsx_builder { tsx_builder * destinations(std::vector &dsts); tsx_builder * add_destination(const cryptonote::tx_destination_entry &dst); + tsx_builder * add_destination(const tools::wallet2 * wallet, bool is_subaddr=false, uint64_t amount=1000); tsx_builder * add_destination(const var_addr_t addr, bool is_subaddr=false, uint64_t amount=1000); tsx_builder * set_integrated(size_t idx); tsx_builder * rct_config(const rct::RCTConfig & rct_config) {m_rct_config = rct_config; return this; };