diff --git a/src/Common/SignalHandler.cpp b/src/Common/SignalHandler.cpp index b73735ee..3795df3d 100644 --- a/src/Common/SignalHandler.cpp +++ b/src/Common/SignalHandler.cpp @@ -35,7 +35,10 @@ namespace { void handleSignal() { static std::mutex m_mutex; - std::unique_lock lock(m_mutex); + std::unique_lock lock(m_mutex, std::try_to_lock); + if (!lock.owns_lock()) { + return; + } m_handler(); } @@ -75,6 +78,7 @@ namespace tools { #else signal(SIGINT, posixHandler); signal(SIGTERM, posixHandler); + signal(SIGPIPE, SIG_IGN); m_handler = t; return true; #endif diff --git a/src/Platform/OSX/System/TcpConnection.cpp b/src/Platform/OSX/System/TcpConnection.cpp index dec331ee..62872f63 100755 --- a/src/Platform/OSX/System/TcpConnection.cpp +++ b/src/Platform/OSX/System/TcpConnection.cpp @@ -237,10 +237,6 @@ std::pair TcpConnection::getPeerAddressAndPort() { } TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { - int val = 1; - if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) { - throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, result=" + std::to_string(errno)); - } } } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 283b08d5..f4a66429 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -150,7 +150,8 @@ const CheckpointData CHECKPOINTS[] = { {667000, "a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995"}, {689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"}, {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}, - {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"} + {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, + {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"} }; } // CryptoNote diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 80cdffe9..0faf95d4 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -435,7 +435,11 @@ namespace CryptoNote m_idleTimer.stop(); m_timedSyncTimer.stop(); - logger(INFO) << "Stopping " << m_connections.size() << " connections"; + logger(INFO) << "Stopping " << m_connections.size() + m_raw_connections.size() << " connections"; + + for (auto& conn : m_raw_connections) { + conn.second.connection.stop(); + } for (auto& conn : m_connections) { conn.second.connection.stop(); @@ -671,32 +675,44 @@ namespace CryptoNote ctx.m_is_income = false; ctx.m_started = time(nullptr); - CryptoNote::LevinProtocol proto(ctx.connection); + auto raw = m_raw_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + try { + CryptoNote::LevinProtocol proto(raw->second.connection); - if (!handshake(proto, ctx, just_take_peerlist)) { - logger(WARNING) << "Failed to HANDSHAKE with peer " << na; - return false; + if (!handshake(proto, raw->second, just_take_peerlist)) { + logger(WARNING) << "Failed to HANDSHAKE with peer " << na; + m_raw_connections.erase(raw); + return false; + } + } catch (...) { + m_raw_connections.erase(raw); + throw; } if (just_take_peerlist) { - logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << ctx << "CONNECTION HANDSHAKED OK AND CLOSED."; + logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << raw->second << "CONNECTION HANDSHAKED OK AND CLOSED."; + m_raw_connections.erase(raw); return true; } peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); pe_local.adr = na; - pe_local.id = ctx.peer_id; + pe_local.id = raw->second.peer_id; time(&pe_local.last_seen); m_peerlist.append_with_peer_white(pe_local); if (m_stop) { + m_raw_connections.erase(raw); throw System::InterruptedException(); } - auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + auto iter = m_connections.emplace(raw->first, std::move(raw->second)).first; + m_raw_connections.erase(raw); + const boost::uuids::uuid& connectionId = iter->first; + p2p_connection_context& connectionContext = iter->second; ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, iter)); + m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connectionContext))); return true; } catch (System::InterruptedException&) { @@ -1219,9 +1235,11 @@ namespace CryptoNote ctx.m_remote_port = addressAndPort.second; auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + const boost::uuids::uuid& connectionId = iter->first; + p2p_connection_context& connection = iter->second; ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, iter)); + m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connection))); } } catch (System::InterruptedException&) { } catch (const std::exception& e) { @@ -1274,10 +1292,9 @@ namespace CryptoNote } } - void node_server::connectionHandler(ConnectionIterator connIter) { + void node_server::connectionHandler(const boost::uuids::uuid& connectionId, p2p_connection_context& ctx) { try { - auto& ctx = connIter->second; on_connection_new(ctx); LevinProtocol proto(ctx.connection); @@ -1320,10 +1337,10 @@ namespace CryptoNote logger(WARNING) << "Exception in connectionHandler: " << e.what(); } - connIter->second.writeLatch.wait(); + ctx.writeLatch.wait(); - on_connection_close(connIter->second); - m_connections.erase(connIter); + on_connection_close(ctx); + m_connections.erase(connectionId); if (--m_spawnCount == 0) { m_shutdownCompleteEvent.set(); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index fc6bd13f..f894606c 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -171,10 +171,11 @@ namespace CryptoNote typedef std::unordered_map> ConnectionContainer; typedef ConnectionContainer::iterator ConnectionIterator; + ConnectionContainer m_raw_connections; ConnectionContainer m_connections; void acceptLoop(); - void connectionHandler(ConnectionIterator connIter); + void connectionHandler(const boost::uuids::uuid& connectionId, p2p_connection_context& connection); void onIdle(); void timedSyncLoop(); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 61d095ac..69c01847 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -791,7 +791,7 @@ bool simple_wallet::listTransfers(const std::vector& args) { for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { TransactionInfo txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active) { + if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } diff --git a/src/transfers/BlockchainSynchronizer.h b/src/transfers/BlockchainSynchronizer.h index 2ec1413c..feeb508c 100644 --- a/src/transfers/BlockchainSynchronizer.h +++ b/src/transfers/BlockchainSynchronizer.h @@ -125,7 +125,6 @@ private: INode& m_node; const crypto::hash m_genesisBlockHash; - std::vector knownTxIds; crypto::hash lastBlockId; State m_currentState; diff --git a/src/version.h.in b/src/version.h.in index 117aada2..7a7edafc 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.4" -#define PROJECT_VERSION_BUILD_NO "461" +#define PROJECT_VERSION "1.0.4.1" +#define PROJECT_VERSION_BUILD_NO "466" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index a25dc275..d1ce67ca 100755 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -16,14 +16,15 @@ // along with Bytecoin. If not, see . #include "Wallet.h" -#include "serialization/binary_utils.h" -#include "WalletUtils.h" -#include "WalletSerializer.h" -#include #include +#include +#include "serialization/binary_utils.h" +#include "WalletHelper.h" #include "WalletSerialization.h" +#include "WalletSerializer.h" +#include "WalletUtils.h" namespace { @@ -93,6 +94,7 @@ private: std::promise promise; std::future future; }; + } //namespace namespace CryptoNote { @@ -125,7 +127,6 @@ Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : m_onInitSyncStarter(new SyncStarter(m_blockchainSync)) { addObserver(m_onInitSyncStarter.get()); - m_blockchainSync.addObserver(this); } Wallet::~Wallet() { @@ -139,10 +140,10 @@ Wallet::~Wallet() { } } - m_blockchainSync.removeObserver(this); - m_blockchainSync.stop(); - m_asyncContextCounter.waitAsyncContextsFinish(); - m_sender.release(); + m_blockchainSync.removeObserver(this); + m_blockchainSync.stop(); + m_asyncContextCounter.waitAsyncContextsFinish(); + m_sender.release(); } void Wallet::addObserver(IWalletObserver* observer) { @@ -215,7 +216,7 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { m_password = password; m_state = LOADING; - + m_asyncContextCounter.addAsyncContext(); std::thread loader(&Wallet::doLoad, this, std::ref(source)); loader.detach(); @@ -234,6 +235,8 @@ void Wallet::initSync() { m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.get_keys(), *m_transferDetails)); m_state = INITIALIZED; + + m_blockchainSync.addObserver(this); } void Wallet::doLoad(std::istream& source) { @@ -248,20 +251,18 @@ void Wallet::doLoad(std::istream& source) { initSync(); try { - if (!cache.empty()) { - std::stringstream stream(cache); - m_transfersSync.load(stream); - } + if (!cache.empty()) { + std::stringstream stream(cache); + m_transfersSync.load(stream); + } } catch (const std::exception&) { // ignore cache loading errors - } - } - catch (std::system_error& e) { + } + } catch (std::system_error& e) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); m_observerManager.notify(&IWalletObserver::initCompleted, e.code()); return; - } - catch (std::exception&) { + } catch (std::exception&) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; @@ -295,31 +296,39 @@ void Wallet::shutdown() { std::unique_lock lock(m_cacheMutex); m_isStopping = false; m_state = NOT_INITIALIZED; + + const AccountAddress& accountAddress = reinterpret_cast(m_account.get_keys().m_account_address); + auto subObject = m_transfersSync.getSubscription(accountAddress); + assert(subObject != nullptr); + subObject->removeObserver(this); + m_transfersSync.removeSubscription(accountAddress); + m_transferDetails = nullptr; + + m_transactionsCache.reset(); + m_lastNotifiedActualBalance = 0; + m_lastNotifiedPendingBalance = 0; } } void Wallet::reset() { InitWaiter initWaiter; SaveWaiter saveWaiter; - - addObserver(&initWaiter); - addObserver(&saveWaiter); + WalletHelper::IWalletRemoveObserverGuard initGuarantee(*this, initWaiter); + WalletHelper::IWalletRemoveObserverGuard saveGuarantee(*this, saveWaiter); std::stringstream ss; try { - save(ss, false, false); + save(ss, false, false); auto saveError = saveWaiter.waitSave(); - if (!saveError) { - shutdown(); - initAndLoad(ss, m_password); + if (!saveError) { + shutdown(); + initAndLoad(ss, m_password); initWaiter.waitInit(); + } + } catch (std::exception& e) { + std::cout << "exception in reset: " << e.what() << std::endl; } - } catch (std::exception&) { - } - - removeObserver(&saveWaiter); - removeObserver(&initWaiter); } void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index a96ae36e..44dde435 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -123,5 +123,9 @@ void WalletUnconfirmedTransactions::collectUsedOutputs() { m_usedOutputs = std::move(used); } +void WalletUnconfirmedTransactions::reset() { + m_unconfirmedTxs.clear(); + m_usedOutputs.clear(); +} } /* namespace CryptoNote */ diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index e36b8bbf..a5b71152 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -64,6 +64,7 @@ public: uint64_t countUnconfirmedOutsAmount() const; uint64_t countUnconfirmedTransactionsAmount() const; bool isUsed(const TransactionOutputInformation& out) const; + void reset(); private: diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index 961419ac..a1f0171c 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -293,5 +293,11 @@ void WalletUserTransactionsCache::updateUnconfirmedTransactions() { Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { return m_transfers.at(transferId); } + +void WalletUserTransactionsCache::reset() { + m_transactions.clear(); + m_transfers.clear(); + m_unconfirmedTransactions.reset(); +} } //namespace CryptoNote diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index 259c0b74..1bdf9f9b 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -57,6 +57,7 @@ public: Transfer& getTransfer(TransferId transferId); bool isUsed(const TransactionOutputInformation& out) const; + void reset(); private: diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index 4a5b8297..57cde040 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -24,6 +24,7 @@ #include "EventWaiter.h" #include "INode.h" #include "wallet/Wallet.h" +#include "wallet/WalletHelper.h" #include "cryptonote_core/account.h" #include "cryptonote_core/Currency.h" @@ -285,7 +286,7 @@ void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mix bob->initAndGenerate("pass2"); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); - ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, 0, "")); + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, mixIn, "")); ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); generator.generateEmptyBlocks(10); @@ -1327,3 +1328,344 @@ TEST_F(WalletApi, DISABLED_loadingBrokenCache) { ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); ASSERT_EQ(result.value(), 0); } + +TEST_F(WalletApi, shutdownAllowsInitializeWalletWithTheSameKeys) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::WalletAccountKeys accountKeys; + alice->getAccountKeys(accountKeys); + + alice->shutdown(); + alice->initWithKeys(accountKeys, "pass"); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransactionCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, shutdownAllowsInitializeWalletWithDifferentKeys) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->shutdown(); + alice->initAndGenerate("pass"); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(0, alice->getTransactionCount()); + + alice->shutdown(); +} + +namespace { +class WalletSynchronizationProgressUpdatedObserver : public CryptoNote::IWalletObserver { +public: + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t /*total*/) override { + m_current = current; + } + + uint64_t m_current = 0; +}; +} + +TEST_F(WalletApi, shutdownDoesNotRemoveObservers) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + WalletSynchronizationProgressUpdatedObserver observer; + CryptoNote::WalletHelper::IWalletRemoveObserverGuard observerGuard(*alice, observer); + + alice->shutdown(); + observer.m_current = 0; + alice->initAndGenerate("pass"); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(5, observer.m_current); + + observerGuard.removeObserver(); + alice->shutdown(); +} + +namespace { +class WalletTransactionEventCounter : public CryptoNote::IWalletObserver { +public: + virtual void externalTransactionCreated(CryptoNote::TransactionId /*transactionId*/) override { + ++m_count; + } + + virtual void transactionUpdated(CryptoNote::TransactionId /*transactionId*/) override { + ++m_count; + } + + size_t m_count = 0; +}; +} + +TEST_F(WalletApi, afterShutdownAndInitWalletDoesNotSendNotificationsRelatedToOldAddress) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + std::string aliceAddress1 = alice->getAddress(); + CryptoNote::WalletAccountKeys accountKeys1; + alice->getAccountKeys(accountKeys1); + + alice->shutdown(); + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + std::string aliceAddress2 = alice->getAddress(); + + alice->shutdown(); + alice->initWithKeys(accountKeys1, "pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + WalletTransactionEventCounter observer; + CryptoNote::WalletHelper::IWalletRemoveObserverGuard observerGuard(*alice, observer); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + GetOneBlockReward(*bob); + generator.generateEmptyBlocks(10); + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + std::vector transfers; + transfers.push_back({ aliceAddress1, TEST_BLOCK_REWARD / 10 }); + transfers.push_back({ aliceAddress2, TEST_BLOCK_REWARD / 5 }); + bob->sendTransaction(transfers, m_currency.minimumFee()); + std::error_code sendResult; + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(bobWalletObserver.get(), sendResult)); + + generator.generateEmptyBlocks(1); + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, observer.m_count); + + observerGuard.removeObserver(); + bob->shutdown(); + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotChangeAddress) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + auto expectedAddress = alice->getAddress(); + alice->reset(); + ASSERT_EQ(expectedAddress, alice->getAddress()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotChangeAccountKeys) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::WalletAccountKeys expectedAccountKeys; + alice->getAccountKeys(expectedAccountKeys); + + alice->reset(); + + CryptoNote::WalletAccountKeys actualAccountKeys; + alice->getAccountKeys(actualAccountKeys); + + ASSERT_EQ(expectedAccountKeys.spendPublicKey, actualAccountKeys.spendPublicKey); + ASSERT_EQ(expectedAccountKeys.spendSecretKey, actualAccountKeys.spendSecretKey); + ASSERT_EQ(expectedAccountKeys.viewPublicKey, actualAccountKeys.viewPublicKey); + ASSERT_EQ(expectedAccountKeys.viewSecretKey, actualAccountKeys.viewSecretKey); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotRemoveObservers) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + WalletSynchronizationProgressUpdatedObserver observer; + CryptoNote::WalletHelper::IWalletRemoveObserverGuard observerGuard(*alice, observer); + + alice->reset(); + observer.m_current = 0; + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(5, observer.m_current); + + observerGuard.removeObserver(); + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotChangePassword) { + std::string password = "password"; + std::string newPassword = "new_password"; + + alice->initAndGenerate(password); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + ASSERT_TRUE(static_cast(alice->changePassword(newPassword, password))); + ASSERT_FALSE(static_cast(alice->changePassword(password, newPassword))); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsPendingBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->pendingBalance()); + alice->reset(); + ASSERT_EQ(0, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsActualBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->actualBalance()); + alice->reset(); + ASSERT_EQ(0, alice->actualBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsTransactionHistory) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransactionCount()); + alice->reset(); + ASSERT_EQ(0, alice->getTransactionCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsTransfersHistory) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->sendTransaction({ alice->getAddress(), 100 }, m_currency.minimumFee()); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransferCount()); + alice->reset(); + ASSERT_EQ(0, alice->getTransferCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncRestorePendingBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncRestoreActualBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->actualBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncRestoreTransactions) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransactionCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncDoNotRestoreTransfers) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->sendTransaction({ alice->getAddress(), 100 }, m_currency.minimumFee()); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(0, alice->getTransferCount()); + + alice->shutdown(); +}