diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index f5e6d52c..b6d25b23 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -969,3 +969,62 @@ TEST (wallet, password_race_corrupt_seed) } } } + +TEST (wallet, change_seed) +{ + nano::system system (24000, 1); + auto wallet (system.wallet (0)); + wallet->enter_initial_password (); + nano::raw_key seed1; + seed1.data = 1; + nano::public_key pub; + uint32_t index (4); + nano::raw_key prv; + nano::deterministic_key (seed1.data, index, prv.data); + pub = nano::pub_key (prv.data); + wallet->insert_adhoc (nano::test_genesis_key.prv, false); + auto block (wallet->send_action (nano::test_genesis_key.pub, pub, 100)); + ASSERT_NE (nullptr, block); + system.nodes[0]->block_processor.flush (); + { + auto transaction (wallet->wallets.tx_begin_write ()); + wallet->change_seed (transaction, seed1); + nano::raw_key seed2; + wallet->store.seed (seed2, transaction); + ASSERT_EQ (seed1, seed2); + ASSERT_EQ (index + 1, wallet->store.deterministic_index_get (transaction)); + } + ASSERT_TRUE (wallet->exists (pub)); +} + +TEST (wallet, deterministic_restore) +{ + nano::system system (24000, 1); + auto wallet (system.wallet (0)); + wallet->enter_initial_password (); + nano::raw_key seed1; + seed1.data = 1; + nano::public_key pub; + uint32_t index (4); + { + auto transaction (wallet->wallets.tx_begin_write ()); + wallet->change_seed (transaction, seed1); + nano::raw_key seed2; + wallet->store.seed (seed2, transaction); + ASSERT_EQ (seed1, seed2); + ASSERT_EQ (1, wallet->store.deterministic_index_get (transaction)); + nano::raw_key prv; + nano::deterministic_key (seed1.data, index, prv.data); + pub = nano::pub_key (prv.data); + } + wallet->insert_adhoc (nano::test_genesis_key.prv, false); + auto block (wallet->send_action (nano::test_genesis_key.pub, pub, 100)); + ASSERT_NE (nullptr, block); + system.nodes[0]->block_processor.flush (); + { + auto transaction (wallet->wallets.tx_begin_write ()); + wallet->deterministic_restore (transaction); + ASSERT_EQ (index + 1, wallet->store.deterministic_index_get (transaction)); + } + ASSERT_TRUE (wallet->exists (pub)); +} diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 97ac1145..22e06a07 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1221,48 +1221,64 @@ void nano::wallet::init_free_accounts (nano::transaction const & transaction_a) } } +uint32_t nano::wallet::deterministic_check (nano::transaction const & transaction_a, uint32_t index) +{ + auto block_transaction (wallets.node.store.tx_begin_read ()); + for (uint32_t i (index + 1), n (index + 64); i < n; ++i) + { + nano::raw_key prv; + store.deterministic_key (prv, transaction_a, i); + nano::keypair pair (prv.data.to_string ()); + // Check if account received at least 1 block + auto latest (wallets.node.ledger.latest (block_transaction, pair.pub)); + if (!latest.is_zero ()) + { + index = i; + // i + 64 - Check additional 64 accounts + // i/64 - Check additional accounts for large wallets. I.e. 64000/64 = 1000 accounts to check + n = i + 64 + (i / 64); + } + else + { + // Check if there are pending blocks for account + for (auto ii (wallets.node.store.pending_begin (block_transaction, nano::pending_key (pair.pub, 0))); nano::pending_key (ii->first).account == pair.pub; ++ii) + { + index = i; + n = i + 64 + (i / 64); + break; + } + } + } + return index; +} + nano::public_key nano::wallet::change_seed (nano::transaction const & transaction_a, nano::raw_key const & prv_a, uint32_t count) { store.seed_set (transaction_a, prv_a); auto account = deterministic_insert (transaction_a); if (count == 0) { - for (uint32_t i (1), n (64); i < n; ++i) - { - nano::raw_key prv; - store.deterministic_key (prv, transaction_a, i); - nano::keypair pair (prv.data.to_string ()); - // Check if account received at least 1 block - auto block_transaction (wallets.node.store.tx_begin_read ()); - auto latest (wallets.node.ledger.latest (block_transaction, pair.pub)); - if (!latest.is_zero ()) - { - count = i; - // i + 64 - Check additional 64 accounts - // i/64 - Check additional accounts for large wallets. I.e. 64000/64 = 1000 accounts to check - n = i + 64 + (i / 64); - } - else - { - // Check if there are pending blocks for account - for (auto ii (wallets.node.store.pending_begin (block_transaction, nano::pending_key (pair.pub, 0))); nano::pending_key (ii->first).account == pair.pub; ++ii) - { - count = i; - n = i + 64 + (i / 64); - break; - } - } - } + count = deterministic_check (transaction_a, 0); } for (uint32_t i (0); i < count; ++i) { // Disable work generation to prevent weak CPU nodes stuck account = deterministic_insert (transaction_a, false); } - return account; } +void nano::wallet::deterministic_restore (nano::transaction const & transaction_a) +{ + auto index (store.deterministic_index_get (transaction_a)); + auto new_index (deterministic_check (transaction_a, index)); + for (uint32_t i (index); i <= new_index && index != new_index; ++i) + { + // Disable work generation to prevent weak CPU nodes stuck + deterministic_insert (transaction_a, false); + } +} + bool nano::wallet::live () { return store.handle != 0; diff --git a/nano/node/wallet.hpp b/nano/node/wallet.hpp index 6dc01702..8c3061f0 100644 --- a/nano/node/wallet.hpp +++ b/nano/node/wallet.hpp @@ -147,8 +147,10 @@ public: void work_ensure (nano::account const &, nano::block_hash const &); bool search_pending (); void init_free_accounts (nano::transaction const &); + uint32_t deterministic_check (nano::transaction const & transaction_a, uint32_t index); /** Changes the wallet seed and returns the first account */ - nano::public_key change_seed (nano::transaction const & transaction_a, nano::raw_key const & prv_a, uint32_t = 0); + nano::public_key change_seed (nano::transaction const & transaction_a, nano::raw_key const & prv_a, uint32_t count = 0); + void deterministic_restore (nano::transaction const & transaction_a); bool live (); std::unordered_set free_accounts; std::function lock_observer; diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 0f252c79..6611e316 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -396,6 +396,11 @@ wallet (wallet_a) { this->wallet.account = this->wallet.wallet_m->change_seed (transaction, seed_l); successful = true; + // Pending check for accounts to restore if bootstrap is in progress + if (this->wallet.node.bootstrap_initiator.in_progress ()) + { + this->wallet.needs_deterministic_restore = true; + } } else { @@ -999,7 +1004,8 @@ send_count_label (new QLabel ("Amount:")), send_count (new QLineEdit), send_blocks_send (new QPushButton ("Send")), send_blocks_back (new QPushButton ("Back")), -active_status (*this) +active_status (*this), +needs_deterministic_restore (false) { update_connected (); empty_password (); @@ -1353,6 +1359,13 @@ void nano_qt::wallet::start () else { this_l->active_status.erase (nano_qt::status_types::synchronizing); + // Check for accounts to restore + if (this_l->needs_deterministic_restore) + { + this_l->needs_deterministic_restore = false; + auto transaction (this_l->wallet_m->wallets.tx_begin_write ()); + this_l->wallet_m->deterministic_restore (transaction); + } } } })); diff --git a/nano/qt/qt.hpp b/nano/qt/qt.hpp index 282ca653..557f4794 100644 --- a/nano/qt/qt.hpp +++ b/nano/qt/qt.hpp @@ -353,5 +353,6 @@ public: void push_main_stack (QWidget *); void ongoing_refresh (); std::atomic needs_balance_refresh; + std::atomic needs_deterministic_restore; }; }