// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "gtest/gtest.h" #include #include "../IntegrationTestLib/BaseFunctionalTests.h" #include "../IntegrationTestLib/NodeObserver.h" #include "WalletLegacy/WalletLegacy.h" #include "WalletLegacyObserver.h" using namespace CryptoNote; using namespace Logging; extern Tests::Common::BaseFunctionalTestsConfig baseCfg; // extern System::Dispatcher globalDispatcher; struct TotalWalletBalance { TotalWalletBalance(uint64_t actual_ = 0, uint64_t pending_ = 0) : actual(actual_), pending(pending_) {} TotalWalletBalance(IWalletLegacy& wallet) : TotalWalletBalance(wallet.actualBalance(), wallet.pendingBalance()) {} uint64_t actual = 0; uint64_t pending = 0; uint64_t total() const { return actual + pending; } }; class IntegrationTest : public Tests::Common::BaseFunctionalTests, public ::testing::Test { public: IntegrationTest() : currency(CryptoNote::CurrencyBuilder(log).testnet(true).currency()), BaseFunctionalTests(currency, dispatcher, baseCfg), logger(log, "IntegrationTest") { } ~IntegrationTest() { wallets.clear(); inodes.clear(); stopTestnet(); } void makeINodes() { for (auto& n : nodeDaemons) { std::unique_ptr node; n->makeINode(node); inodes.push_back(std::move(node)); } } void makeWallets() { for (auto& n: inodes) { std::unique_ptr wallet(new CryptoNote::WalletLegacy(m_currency, *n)); std::unique_ptr observer(new WalletLegacyObserver()); wallet->initAndGenerate(walletPassword); wallet->addObserver(observer.get()); wallets.push_back(std::move(wallet)); walletObservers.push_back(std::move(observer)); } } void mineBlocksFor(size_t node, const std::string& address, size_t blockCount) { auto prevHeight = nodeDaemons[node]->getLocalHeight(); nodeDaemons[node]->startMining(1, address); do { std::this_thread::sleep_for(std::chrono::seconds(1)); } while (prevHeight + blockCount < nodeDaemons[node]->getLocalHeight()); nodeDaemons[node]->stopMining(); } void printWalletBalances() { for (auto& w: wallets) { logger(INFO) << "Wallet " << w->getAddress().substr(0, 6); logger(INFO) << " " << currency.formatAmount(w->actualBalance()) << " actual / " << currency.formatAmount(w->pendingBalance()) << " pending"; } } void mineEmptyBlocks(size_t nodeNum, size_t blocksCount) { } void mineMoneyForWallet(size_t nodeNum, size_t walletNum) { auto& wallet = *wallets[walletNum]; auto& node = *nodeDaemons[nodeNum]; node.startMining(1, wallet.getAddress()); walletObservers[walletNum]->waitActualBalanceChange(); node.stopMining(); while (node.getLocalHeight() > walletObservers[walletNum]->getCurrentHeight()) { std::this_thread::sleep_for(std::chrono::seconds(1)); } } std::error_code transferMoney(size_t srcWallet, size_t dstWallet, uint64_t amount, uint64_t fee) { logger(INFO) << "Transferring from " << wallets[srcWallet]->getAddress().substr(0, 6) << " to " << wallets[dstWallet]->getAddress().substr(0, 6) << " " << currency.formatAmount(amount); CryptoNote::WalletLegacyTransfer tr; tr.address = wallets[dstWallet]->getAddress(); tr.amount = amount; std::error_code result; auto txId = wallets[srcWallet]->sendTransaction(tr, fee); logger(DEBUGGING) << "Transaction id = " << txId; return walletObservers[srcWallet]->waitSendResult(txId); } void checkIncomingTransfer(size_t dstWallet, uint64_t amount) { startMining(1); auto txId = walletObservers[dstWallet]->waitExternalTransaction(); stopMining(); WalletLegacyTransaction txInfo; ASSERT_TRUE(wallets[dstWallet]->getTransaction(txId, txInfo)); ASSERT_EQ(txInfo.totalAmount, amount); } System::Dispatcher dispatcher; std::string walletPassword = "pass"; CryptoNote::Currency currency; Logging::ConsoleLogger log; Logging::LoggerRef logger; std::vector> inodes; std::vector> wallets; std::vector> walletObservers; }; TEST_F(IntegrationTest, Wallet2Wallet) { const uint64_t FEE = 1000000; launchTestnet(2); logger(INFO) << "Testnet launched"; makeINodes(); makeWallets(); logger(INFO) << "Created wallets"; mineMoneyForWallet(0, 0); logger(INFO) << "Mined money"; printWalletBalances(); TotalWalletBalance w0pre(*wallets[0]); TotalWalletBalance w1pre(*wallets[1]); auto sendAmount = w0pre.actual / 2; ASSERT_TRUE(!transferMoney(0, 1, sendAmount, currency.minimumFee())); ASSERT_NO_FATAL_FAILURE(checkIncomingTransfer(1, sendAmount)); printWalletBalances(); TotalWalletBalance w0after(*wallets[0]); TotalWalletBalance w1after(*wallets[1]); // check total ASSERT_EQ(w0pre.total() + w1pre.total() - currency.minimumFee(), w0after.total() + w1after.total()); // check diff ASSERT_EQ(sendAmount, w1after.total() - w1pre.total()); } TEST_F(IntegrationTest, BlockPropagationSpeed) { launchTestnet(3, Line); makeINodes(); { std::unique_ptr& localNode = inodes.front(); std::unique_ptr& remoteNode = inodes.back(); std::unique_ptr wallet; makeWallet(wallet, localNode); NodeObserver localObserver(*localNode); NodeObserver remoteObserver(*remoteNode); const size_t BLOCKS_COUNT = 10; nodeDaemons.front()->startMining(1, wallet->getAddress()); for (size_t blockNumber = 0; blockNumber < BLOCKS_COUNT; ++blockNumber) { uint32_t localHeight = localObserver.waitLastKnownBlockHeightUpdated(); uint32_t remoteHeight = 0; while (remoteHeight != localHeight) { ASSERT_TRUE(remoteObserver.waitLastKnownBlockHeightUpdated(std::chrono::milliseconds(5000), remoteHeight)); } logger(INFO) << "Iteration " << blockNumber + 1 << ": " << "height = " << localHeight; } nodeDaemons.front()->stopMining(); } }