aboutsummaryrefslogtreecommitdiff
path: root/tests/trezor/trezor_tests.cpp
diff options
context:
space:
mode:
authorDusan Klinec <dusan.klinec@gmail.com>2019-02-27 16:55:31 +0100
committerDusan Klinec <dusan.klinec@gmail.com>2019-03-20 21:11:02 +0100
commitc9b13fbbc23d065afca62a499484c283079c0fa5 (patch)
tree5bd43f21891b3a00b0ef7807c9e91f54f761aec1 /tests/trezor/trezor_tests.cpp
parentdevice/trezor: HF10 support added, wallet::API (diff)
downloadmonero-c9b13fbbc23d065afca62a499484c283079c0fa5.tar.xz
tests/trezor: HF9 and HF10 tests
- tests fixes for HF10, builder change, rct_config; fix_chain - get_tx_key test - proper testing after live refresh added - live refresh synthetic test - log available funds for easier test construction - wallet::API tests with mocked daemon
Diffstat (limited to 'tests/trezor/trezor_tests.cpp')
-rw-r--r--tests/trezor/trezor_tests.cpp553
1 files changed, 490 insertions, 63 deletions
diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp
index c2b46f698..310fa45f1 100644
--- a/tests/trezor/trezor_tests.cpp
+++ b/tests/trezor/trezor_tests.cpp
@@ -41,6 +41,7 @@ using namespace cryptonote;
#include "common/util.h"
#include "common/command_line.h"
#include "trezor_tests.h"
+#include "tools.h"
#include "device/device_cold.hpp"
#include "device_trezor/device_trezor.hpp"
@@ -57,7 +58,7 @@ namespace
const command_line::arg_descriptor<bool> arg_fix_chain = {"fix_chain", "If chain_patch is given and file cannot be used, it is ignored and overwriten", false};
}
-
+#define 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) \
rollback_chain(CORE, BASE.head_block()); \
@@ -70,14 +71,17 @@ namespace
#define TREZOR_SETUP_CHAIN(NAME) do { \
++tests_count; \
try { \
- setup_chain(&core, trezor_base, chain_path, fix_chain); \
+ setup_chain(core, trezor_base, chain_path, fix_chain, vm_core); \
} catch (const std::exception& ex) { \
failed_tests.emplace_back("gen_trezor_base " #NAME); \
} \
} while(0)
+
+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);
+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);
int main(int argc, char* argv[])
{
@@ -123,38 +127,76 @@ int main(int argc, char* argv[])
const bool heavy_tests = command_line::get_arg(vm, arg_heavy_tests);
const bool fix_chain = command_line::get_arg(vm, arg_fix_chain);
- hw::trezor::register_all();
+ hw::register_device(HW_TREZOR_NAME, ensure_trezor_test_device());
+ // hw::trezor::register_all(); // We use our shim instead.
// Bootstrapping common chain & accounts
- cryptonote::core * core = nullptr;
+ const uint8_t initial_hf = 9;
+ const uint8_t max_hf = 10;
+
+ cryptonote::core core_obj(nullptr);
+ cryptonote::core * const core = &core_obj;
+ std::shared_ptr<mock_daemon> daemon = nullptr;
gen_trezor_base trezor_base;
trezor_base.setup_args(trezor_path, heavy_tests);
- trezor_base.rct_config({rct::RangeProofPaddedBulletproof, 1}); // HF9 tests
+ trezor_base.set_hard_fork(initial_hf);
- TREZOR_SETUP_CHAIN("HF9");
-
- // Individual test cases using shared pre-generated blockchain.
- TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync, core, trezor_base);
+ // Arguments for core & daemon
+ po::variables_map vm_core;
+ po::options_description desc_params_core("Core");
+ mock_daemon::init_options(desc_params_core);
+ tools::options::build_options(vm_core, desc_params_core);
+ mock_daemon::default_options(vm_core);
// Transaction tests
- TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_short, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_short_integrated, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_long, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_acc1, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_sub, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_2sub, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_1norm_2sub, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_2utxo_sub_acc_to_1norm_2sub, core, trezor_base);
- TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_7outs, core, trezor_base);
+ for(uint8_t hf=initial_hf; hf <= max_hf; ++hf)
+ {
+ MDEBUG("Transaction tests for HF " << (int)hf);
+ if (hf > initial_hf)
+ {
+ daemon->stop_and_deinit();
+ daemon = nullptr;
+ trezor_base.daemon(nullptr);
+ }
+
+ trezor_base.set_hard_fork(hf);
+ TREZOR_SETUP_CHAIN(std::string("HF") + std::to_string((int)hf));
+
+ daemon = std::make_shared<mock_daemon>(core, vm_core);
+ CHECK_AND_ASSERT_THROW_MES(daemon->nettype() == trezor_base.nettype(), "Serialized chain network type does not match");
+
+ daemon->try_init_and_run();
+ trezor_base.daemon(daemon);
+
+ // Hard-fork independent tests
+ if (hf == initial_hf)
+ {
+ TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync_without_refresh, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_live_refresh, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync_with_refresh, 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);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_long, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_acc1, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_sub, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_2sub, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_1norm_2sub, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_2utxo_sub_acc_to_1norm_2sub, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_7outs, core, trezor_base);
+ TREZOR_COMMON_TEST_CASE(wallet_api_tests, core, trezor_base);
+ }
if (trezor_base.heavy_tests())
{
TREZOR_COMMON_TEST_CASE(gen_trezor_many_utxo, core, trezor_base);
}
+ daemon->stop();
core->deinit();
el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error);
MLOG(level, "\nREPORT:");
@@ -243,7 +285,36 @@ static bool serialize_chain_to_file(std::vector<test_event_entry>& events, gen_t
CATCH_ENTRY_L0("serialize_chain_to_file", false);
}
-static void setup_chain(cryptonote::core ** core, gen_trezor_base & trezor_base, std::string chain_path, bool fix_chain)
+template<class t_test_class>
+static bool init_core_replay_events(std::vector<test_event_entry>& events, cryptonote::core * core, const po::variables_map & vm_core)
+{
+ // this test needs for it to be so.
+ get_test_options<t_test_class> gto;
+
+ // Hardforks can be specified in events.
+ v_hardforks_t hardforks;
+ cryptonote::test_options test_options_tmp{};
+ const cryptonote::test_options * test_options_ = &gto.test_options;
+ if (extract_hard_forks(events, hardforks)){
+ hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator
+ test_options_tmp.hard_forks = hardforks.data();
+ test_options_ = &test_options_tmp;
+ }
+
+ core->deinit();
+ CHECK_AND_ASSERT_THROW_MES(core->init(vm_core, test_options_), "Core init failed");
+ core->get_blockchain_storage().get_db().set_batch_transactions(true);
+
+ // start with a clean pool
+ std::vector<crypto::hash> pool_txs;
+ CHECK_AND_ASSERT_THROW_MES(core->get_pool_transaction_hashes(pool_txs), "Failed to flush txpool");
+ core->get_blockchain_storage().flush_txes_from_pool(pool_txs);
+
+ t_test_class validator;
+ return replay_events_through_core<t_test_class>(*core, events, validator);
+}
+
+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)
{
std::vector<test_event_entry> events;
const bool do_serialize = !chain_path.empty();
@@ -272,6 +343,7 @@ static void setup_chain(cryptonote::core ** core, gen_trezor_base & trezor_base,
{
try
{
+ trezor_base.clear();
generated = trezor_base.generate(events);
if (generated && !loaded && do_serialize)
@@ -287,7 +359,7 @@ static void setup_chain(cryptonote::core ** core, gen_trezor_base & trezor_base,
}
trezor_base.fix_hf(events);
- if (generated && do_replay_events_get_core<gen_trezor_base>(events, core))
+ if (generated && init_core_replay_events<gen_trezor_base>(events, core, vm_core))
{
MGINFO_GREEN("#TEST-chain-init# Succeeded ");
}
@@ -298,6 +370,14 @@ static void setup_chain(cryptonote::core ** core, gen_trezor_base & trezor_base,
}
}
+static device_trezor_test *ensure_trezor_test_device(){
+ if (!trezor_device) {
+ trezor_device = new device_trezor_test();
+ trezor_device->set_name(HW_TREZOR_NAME);
+ }
+ return trezor_device;
+}
+
static void add_hforks(std::vector<test_event_entry>& events, const v_hardforks_t& hard_forks)
{
event_replay_settings repl_set;
@@ -483,8 +563,20 @@ static std::vector<tools::wallet2*> vct_wallets(tools::wallet2* w1=nullptr, tool
return res;
}
+static uint64_t get_available_funds(tools::wallet2* wallet, uint32_t account=0)
+{
+ 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;
+ }
+ return sum;
+}
+
// gen_trezor_base
-const uint64_t gen_trezor_base::m_ts_start = 1338224400;
+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_master_seed_str = "14821d0bc5659b24cafbc889dc4fc60785ee08b65d71c525f81eeaba4f3a570f";
@@ -494,12 +586,16 @@ const std::string gen_trezor_base::m_alice_view_private = "a6ccd4ac344a295d1387f
gen_trezor_base::gen_trezor_base(){
m_rct_config = {rct::RangeProofPaddedBulletproof, 1};
+ m_test_get_tx_key = true;
+ m_network_type = cryptonote::TESTNET;
}
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_hard_forks(other.m_hard_forks), m_trezor(other.m_trezor), m_rct_config(other.m_rct_config),
+ 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)
{
}
@@ -513,33 +609,27 @@ void gen_trezor_base::setup_args(const std::string & trezor_path, bool heavy_tes
void gen_trezor_base::setup_trezor()
{
hw::device &hwdev = hw::get_device(m_trezor_path);
- m_trezor = dynamic_cast<hw::trezor::device_trezor *>(&hwdev);
- CHECK_AND_ASSERT_THROW_MES(m_trezor, "Dynamic cast failed");
-
- m_trezor->set_debug(true); // debugging commands on Trezor (auto-confirm transactions)
-
- CHECK_AND_ASSERT_THROW_MES(m_trezor->set_name(m_trezor_path), "Could not set device name " << m_trezor_path);
- m_trezor->set_network_type(MAINNET);
- m_trezor->set_derivation_path(""); // empty derivation path
+ auto trezor = dynamic_cast<device_trezor_test *>(&hwdev);
+ CHECK_AND_ASSERT_THROW_MES(trezor, "Dynamic cast failed");
- CHECK_AND_ASSERT_THROW_MES(m_trezor->init(), "Could not initialize the device " << m_trezor_path);
- CHECK_AND_ASSERT_THROW_MES(m_trezor->connect(), "Could not connect to the device " << m_trezor_path);
- m_trezor->wipe_device();
- m_trezor->load_device(m_device_seed);
- m_trezor->release();
- m_trezor->disconnect();
+ trezor->setup_for_tests(m_trezor_path, m_device_seed, m_network_type);
+ m_trezor = trezor;
}
void gen_trezor_base::fork(gen_trezor_base & other)
{
other.m_generator = m_generator;
other.m_bt = m_bt;
+ other.m_network_type = m_network_type;
+ other.m_daemon = m_daemon;
other.m_events = m_events;
other.m_head = m_head;
other.m_hard_forks = m_hard_forks;
other.m_trezor_path = m_trezor_path;
other.m_heavy_tests = m_heavy_tests;
other.m_rct_config = m_rct_config;
+ other.m_test_get_tx_key = m_test_get_tx_key;
+ other.m_live_refresh_enabled = m_live_refresh_enabled;
other.m_miner_account = m_miner_account;
other.m_bob_account = m_bob_account;
@@ -577,10 +667,22 @@ void gen_trezor_base::init_fields()
m_alice_account.set_createtime(m_wallet_ts);
}
+void gen_trezor_base::update_client_settings()
+{
+ auto dev_trezor = dynamic_cast<::hw::trezor::device_trezor*>(m_trezor);
+ CHECK_AND_ASSERT_THROW_MES(dev_trezor, "Could not cast to device_trezor");
+
+ dev_trezor->set_live_refresh_enabled(m_live_refresh_enabled);
+}
+
bool gen_trezor_base::generate(std::vector<test_event_entry>& 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);
@@ -589,7 +691,7 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
cryptonote::block blk_gen;
std::vector<size_t> block_weights;
- generate_genesis_block(blk_gen, get_config(MAINNET).GENESIS_TX, get_config(MAINNET).GENESIS_NONCE);
+ generate_genesis_block(blk_gen, get_config(m_network_type).GENESIS_TX, get_config(m_network_type).GENESIS_NONCE);
events.push_back(blk_gen);
generator.add_block(blk_gen, 0, block_weights, 0);
@@ -644,8 +746,8 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
MDEBUG("Hardfork height: " << hardfork_height << " at block: " << get_block_hash(blk_4r));
// RCT transactions, wallets have to be used, wallet init
- m_wl_alice.reset(new tools::wallet2(MAINNET, 1, true));
- m_wl_bob.reset(new tools::wallet2(MAINNET, 1, true));
+ 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);
@@ -659,7 +761,7 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
auto addr_alice_sub_1_2 = m_wl_alice->get_subaddress({1, 2});
// Miner -> Bob, RCT funds
- MAKE_TX_LIST_START_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(5), 10, blk_4);
+ 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)
@@ -688,9 +790,9 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
// Simple RCT transactions
MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(7), 10, blk_4);
- MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(1), 10, blk_4);
- MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(3), 10, blk_4);
- MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(4), 10, blk_4);
+ MAKE_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);
// Simple transaction check
@@ -704,6 +806,8 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
// 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("Available funds on Alice: " << get_available_funds(m_wl_alice.get()));
+ MDEBUG("Available funds on Bob: " << get_available_funds(m_wl_bob.get()));
// Send Alice -> Bob, manually constructed. Simple TX test, precondition.
cryptonote::transaction tx_1;
@@ -768,18 +872,21 @@ void gen_trezor_base::load(std::vector<test_event_entry>& events)
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(MAINNET, 1, true));
- m_wl_bob.reset(new tools::wallet2(MAINNET, 1, true));
- m_wl_eve.reset(new tools::wallet2(MAINNET, 1, true));
+ 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()));
}
void gen_trezor_base::fix_hf(std::vector<test_event_entry>& events)
@@ -804,12 +911,14 @@ void gen_trezor_base::test_setup(std::vector<test_event_entry>& 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(MAINNET, 1, true));
- m_wl_bob.reset(new tools::wallet2(MAINNET, 1, true));
- m_wl_eve.reset(new tools::wallet2(MAINNET, 1, true));
+ 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);
@@ -818,13 +927,11 @@ void gen_trezor_base::test_setup(std::vector<test_event_entry>& events)
wallet_tools::process_transactions(m_wl_eve.get(), events, m_head, m_bt);
}
-void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std::vector<tools::wallet2::pending_tx>& ptxs, std::vector<cryptonote::address_parse_info>& dsts_info, test_generator &generator, std::vector<tools::wallet2*> wallets, bool is_sweep)
+void gen_trezor_base::add_transactions_to_events(
+ std::vector<test_event_entry>& events,
+ test_generator &generator,
+ const std::vector<cryptonote::transaction> &txs)
{
- // Construct pending transaction for signature in the Trezor.
- const uint64_t height_pre = num_blocks(events) - 1;
- cryptonote::block head_block = get_head_block(events);
- const crypto::hash head_hash = get_block_hash(head_block);
-
// If current test requires higher hard-fork, move it up
const auto current_hf = m_hard_forks.back().first;
const uint8_t tx_hf = m_rct_config.bp_version == 2 ? 10 : 9;
@@ -832,8 +939,28 @@ void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std:
throw std::runtime_error("Too late for HF change");
}
- tools::wallet2::unsigned_tx_set txs;
std::list<cryptonote::transaction> tx_list;
+ for(const auto & tx : txs)
+ {
+ events.push_back(tx);
+ tx_list.push_back(tx);
+ }
+
+ MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_new, m_head, m_miner_account, tx_list, tx_hf);
+ MDEBUG("New tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_new));
+
+ m_head = blk_new;
+}
+
+void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std::vector<tools::wallet2::pending_tx>& ptxs, std::vector<cryptonote::address_parse_info>& dsts_info, test_generator &generator, std::vector<tools::wallet2*> wallets, bool is_sweep)
+{
+ // Construct pending transaction for signature in the Trezor.
+ const uint64_t height_pre = num_blocks(events) - 1;
+ cryptonote::block head_block = get_head_block(events);
+ const crypto::hash head_hash = get_block_hash(head_block);
+
+ tools::wallet2::unsigned_tx_set txs;
+ std::vector<cryptonote::transaction> tx_list;
for(auto &ptx : ptxs) {
txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(ptx, *m_trezor));
@@ -848,6 +975,7 @@ void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std:
hw::wallet_shim wallet_shim;
setup_shim(&wallet_shim);
aux_data.tx_recipients = dsts_info;
+ aux_data.bp_version = m_rct_config.bp_version;
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
@@ -865,13 +993,11 @@ void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std:
CHECK_AND_ASSERT_THROW_MES(resx, "Trezor tx_1 semantics failed");
CHECK_AND_ASSERT_THROW_MES(resy, "Trezor tx_1 Nonsemantics failed");
- events.push_back(c_ptx.tx);
tx_list.push_back(c_ptx.tx);
MDEBUG("Transaction: " << dump_data(c_ptx.tx));
}
- MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_7, m_head, m_miner_account, tx_list, tx_hf);
- MDEBUG("Trezor tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_7));
+ add_transactions_to_events(events, generator, tx_list);
// TX receive test
uint64_t sum_in = 0;
@@ -909,7 +1035,7 @@ void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std:
const bool sender = widx == 0;
tools::wallet2 *wl = wallets[widx];
- wallet_tools::process_transactions(wl, events, blk_7, m_bt, boost::make_optional(head_hash));
+ wallet_tools::process_transactions(wl, events, m_head, m_bt, boost::make_optional(head_hash));
tools::wallet2::transfer_container m_trans;
tools::wallet2::transfer_container m_trans_txid;
@@ -972,6 +1098,120 @@ void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& 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);
+}
+
+bool gen_trezor_base::verify_tx_key(const ::crypto::secret_key & tx_priv, const ::crypto::public_key & tx_pub, const subaddresses_t & subs)
+{
+ ::crypto::public_key tx_pub_c;
+ ::crypto::secret_key_to_public_key(tx_priv, tx_pub_c);
+ if (tx_pub == tx_pub_c)
+ return true;
+
+ for(const auto & elem : subs)
+ {
+ tx_pub_c = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(elem.first), rct::sk2rct(tx_priv)));
+ if (tx_pub == tx_pub_c)
+ return true;
+ }
+ return false;
+}
+
+void gen_trezor_base::test_get_tx(
+ std::vector<test_event_entry>& events,
+ std::vector<tools::wallet2*> wallets,
+ const std::vector<tools::wallet2::pending_tx> &ptxs,
+ const std::vector<std::string> &aux_tx_info)
+{
+ if (!m_test_get_tx_key)
+ {
+ return;
+ }
+
+ auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor);
+ CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
+
+ if (!dev_cold->is_get_tx_key_supported())
+ {
+ MERROR("Get TX key is not supported by the connected Trezor");
+ return;
+ }
+
+ subaddresses_t all_subs;
+ for(tools::wallet2 * wlt : wallets)
+ {
+ wlt->expand_subaddresses({10, 20});
+
+ const subaddresses_t & cur_sub = wallet_accessor_test::get_subaddresses(wlt);
+ all_subs.insert(cur_sub.begin(), cur_sub.end());
+ }
+
+ for(size_t txid = 0; txid < ptxs.size(); ++txid)
+ {
+ const auto &c_ptx = ptxs[txid];
+ const auto &c_tx = c_ptx.tx;
+ const ::crypto::hash tx_prefix_hash = cryptonote::get_transaction_prefix_hash(c_tx);
+
+ auto tx_pub = cryptonote::get_tx_pub_key_from_extra(c_tx.extra);
+ auto additional_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(c_tx.extra);
+
+ hw::device_cold:: tx_key_data_t tx_key_data;
+ std::vector<::crypto::secret_key> tx_keys;
+
+ 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);
+ 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");
+
+ for(size_t i = 0; i < additional_pub_keys.size(); ++i)
+ {
+ CHECK_AND_ASSERT_THROW_MES(verify_tx_key(tx_keys[i + 1], additional_pub_keys[i], all_subs), "Tx pub mismatch");
+ }
+ }
+}
+
+void gen_trezor_base::mine_and_test(std::vector<test_event_entry>& events)
+{
+ 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);
+
+ const uint64_t cur_height = daemon()->get_height();
+ CHECK_AND_ASSERT_THROW_MES(height_before_mining < cur_height, "Mining fail");
+
+ const crypto::hash top_hash = core->get_blockchain_storage().get_block_id_by_height(height_before_mining);
+ cryptonote::block top_block{};
+ CHECK_AND_ASSERT_THROW_MES(core->get_blockchain_storage().get_block_by_hash(top_hash, top_block), "Block fetch fail");
+ CHECK_AND_ASSERT_THROW_MES(!top_block.tx_hashes.empty(), "Mined block is empty");
+
+ std::vector<cryptonote::transaction> txs_found;
+ std::vector<crypto::hash> txs_missed;
+ bool r = core->get_blockchain_storage().get_transactions(top_block.tx_hashes, txs_found, txs_missed);
+ CHECK_AND_ASSERT_THROW_MES(r, "Transaction lookup fail");
+ CHECK_AND_ASSERT_THROW_MES(!txs_found.empty(), "Transaction lookup fail");
+
+ // Transaction is not expanded, but mining verified it.
+ events.push_back(txs_found[0]);
+ events.push_back(top_block);
+}
+
+void gen_trezor_base::set_hard_fork(uint8_t hf)
+{
+ m_top_hard_fork = hf;
+ if (hf < 9){
+ throw std::runtime_error("Minimal supported Hardfork is 9");
+ } else if (hf == 9){
+ rct_config({rct::RangeProofPaddedBulletproof, 1});
+ } else {
+ rct_config({rct::RangeProofPaddedBulletproof, 2});
+ }
}
#define TREZOR_TEST_PREFIX() \
@@ -1182,6 +1422,48 @@ std::vector<tools::wallet2::pending_tx> tsx_builder::build()
return m_ptxs;
}
+device_trezor_test::device_trezor_test(): m_tx_sign_ctr(0), m_compute_key_image_ctr(0) {}
+
+void device_trezor_test::clear_test_counters(){
+ m_tx_sign_ctr = 0;
+ m_compute_key_image_ctr = 0;
+}
+
+void device_trezor_test::setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type){
+ 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);
+ this->set_network_type(network_type);
+ this->set_derivation_path(""); // empty derivation path
+
+ 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->release();
+ this->disconnect();
+}
+
+bool device_trezor_test::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,
+ const ::cryptonote::subaddress_index &received_index,
+ ::cryptonote::keypair &in_ephemeral, ::crypto::key_image &ki) {
+
+ 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;
+ return res;
+}
+
+void
+device_trezor_test::tx_sign(hw::wallet_shim *wallet, const ::tools::wallet2::unsigned_tx_set &unsigned_tx, size_t idx,
+ hw::tx_aux_data &aux_data, std::shared_ptr<hw::trezor::protocol::tx::Signer> &signer) {
+ m_tx_sign_ctr += 1;
+ device_trezor::tx_sign(wallet, unsigned_tx, idx, aux_data, signer);
+}
+
bool gen_trezor_ki_sync::generate(std::vector<test_event_entry>& events)
{
test_generator generator(m_generator);
@@ -1207,6 +1489,92 @@ bool gen_trezor_ki_sync::generate(std::vector<test_event_entry>& events)
uint64_t spent = 0, unspent = 0;
m_wl_alice->import_key_images(ski, 0, spent, unspent, false);
+
+ auto dev_trezor_test = dynamic_cast<device_trezor_test*>(m_trezor);
+ 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");
+
+ return true;
+}
+
+bool gen_trezor_ki_sync_with_refresh::generate(std::vector<test_event_entry>& events)
+{
+ m_live_refresh_enabled = true;
+ return gen_trezor_ki_sync::generate(events);
+}
+
+bool gen_trezor_ki_sync_without_refresh::generate(std::vector<test_event_entry>& events)
+{
+ m_live_refresh_enabled = false;
+ return gen_trezor_ki_sync::generate(events);
+}
+
+bool gen_trezor_live_refresh::generate(std::vector<test_event_entry>& events)
+{
+ test_generator generator(m_generator);
+ test_setup(events);
+
+ auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor);
+ CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
+
+ if (!dev_cold->is_live_refresh_supported()){
+ MDEBUG("Trezor does not support live refresh");
+ return true;
+ }
+
+ hw::device & sw_device = hw::get_device("default");
+
+ dev_cold->live_refresh_start();
+ for(unsigned i=0; i<50; ++i)
+ {
+ cryptonote::subaddress_index subaddr = {0, i};
+
+ ::crypto::secret_key r;
+ ::crypto::public_key R;
+ ::crypto::key_derivation D;
+ ::crypto::public_key pub_ver;
+ ::crypto::key_image ki;
+
+ ::crypto::random32_unbiased((unsigned char*)r.data);
+ ::crypto::secret_key_to_public_key(r, R);
+ memcpy(D.data, rct::scalarmultKey(rct::pk2rct(R), rct::sk2rct(m_alice_account.get_keys().m_view_secret_key)).bytes, 32);
+
+ ::crypto::secret_key scalar_step1;
+ ::crypto::secret_key scalar_step2;
+ ::crypto::derive_secret_key(D, i, m_alice_account.get_keys().m_spend_secret_key, scalar_step1);
+ if (i == 0)
+ {
+ scalar_step2 = scalar_step1;
+ }
+ else
+ {
+ ::crypto::secret_key subaddr_sk = sw_device.get_subaddress_secret_key(m_alice_account.get_keys().m_view_secret_key, subaddr);
+ sw_device.sc_secret_add(scalar_step2, scalar_step1, subaddr_sk);
+ }
+
+ ::crypto::secret_key_to_public_key(scalar_step2, pub_ver);
+ ::crypto::generate_key_image(pub_ver, scalar_step2, ki);
+
+ cryptonote::keypair in_ephemeral;
+ ::crypto::key_image ki2;
+
+ dev_cold->live_refresh(
+ m_alice_account.get_keys().m_view_secret_key,
+ pub_ver,
+ D,
+ i,
+ subaddr,
+ in_ephemeral,
+ ki2
+ );
+
+ CHECK_AND_ASSERT_THROW_MES(ki == ki2, "Key image inconsistent");
+ }
+
+ dev_cold->live_refresh_finish();
return true;
}
@@ -1407,3 +1775,62 @@ bool gen_trezor_many_utxo::generate(std::vector<test_event_entry>& events)
TREZOR_TEST_SUFFIX();
}
+void wallet_api_tests::init()
+{
+ m_wallet_dir = boost::filesystem::unique_path();
+ boost::filesystem::create_directories(m_wallet_dir);
+}
+
+wallet_api_tests::~wallet_api_tests()
+{
+ try
+ {
+ if (!m_wallet_dir.empty() && boost::filesystem::exists(m_wallet_dir))
+ {
+ boost::filesystem::remove_all(m_wallet_dir);
+ }
+ }
+ catch(...)
+ {
+ MERROR("Could not remove wallet directory");
+ }
+}
+
+bool wallet_api_tests::generate(std::vector<test_event_entry>& events)
+{
+ init();
+ test_setup(events);
+ const std::string wallet_path = (m_wallet_dir / "wallet").string();
+ const auto api_net_type = m_network_type == TESTNET ? Monero::TESTNET : Monero::MAINNET;
+
+ Monero::WalletManager *wmgr = Monero::WalletManagerFactory::getWalletManager();
+ std::unique_ptr<Monero::Wallet> w{wmgr->createWalletFromDevice(wallet_path, "", api_net_type, m_trezor_path, 1)};
+ CHECK_AND_ASSERT_THROW_MES(w->init(daemon()->rpc_addr(), 0), "Wallet init fail");
+ CHECK_AND_ASSERT_THROW_MES(w->refresh(), "Refresh fail");
+ uint64_t balance = w->balance(0);
+ MINFO("Balance: " << balance);
+ CHECK_AND_ASSERT_THROW_MES(w->status() == Monero::PendingTransaction::Status_Ok, "Status nok");
+
+ 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),
+ TREZOR_TEST_MIXIN,
+ Monero::PendingTransaction::Priority_Medium,
+ 0,
+ std::set<uint32_t>{});
+ CHECK_AND_ASSERT_THROW_MES(transaction->status() == Monero::PendingTransaction::Status_Ok, "Status nok");
+ 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");
+ (void)w.release();
+
+ mine_and_test(events);
+ return true;
+}
+