diff --git a/include/IWallet.h b/include/IWallet.h index 72f6714b..49f1b3b9 100755 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -31,7 +31,8 @@ const uint32_t WALLET_UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits destinations; + uint64_t fee = 0; + uint64_t mixIn = 0; + std::string extra; + uint64_t unlockTimestamp = 0; + DonationSettings donation; +}; + class IWallet { public: virtual ~IWallet() {} @@ -113,10 +140,11 @@ public: virtual size_t getTransactionTransferCount(size_t transactionIndex) const = 0; virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const = 0; - virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::string& sourceAddress, const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const TransactionParameters& sendingTransaction) = 0; virtual void start() = 0; virtual void stop() = 0; diff --git a/src/BlockchainExplorer/BlockchainExplorer.cpp b/src/BlockchainExplorer/BlockchainExplorer.cpp index 159bf75b..71f4e42e 100755 --- a/src/BlockchainExplorer/BlockchainExplorer.cpp +++ b/src/BlockchainExplorer/BlockchainExplorer.cpp @@ -78,12 +78,72 @@ private: const std::function requestFunc; }; +BlockchainExplorer::PoolUpdateGuard::PoolUpdateGuard() : + m_state(State::NONE) { +} + +bool BlockchainExplorer::PoolUpdateGuard::beginUpdate() { + auto state = m_state.load(); + for (;;) { + switch (state) { + case State::NONE: + return true; + + case State::UPDATING: + if (m_state.compare_exchange_weak(state, State::UPDATE_REQUIRED)) { + return false; + } + + case State::UPDATE_REQUIRED: + return false; + + default: + assert(false); + return false; + } + } +} + +bool BlockchainExplorer::PoolUpdateGuard::endUpdate() { + auto state = m_state.load(); + for (;;) { + assert(state != State::NONE); + + if (m_state.compare_exchange_weak(state, State::NONE)) { + return state == State::UPDATE_REQUIRED; + } + } +} + +class ScopeExitHandler { +public: + ScopeExitHandler(std::function&& handler) : + m_handler(std::move(handler)), + m_cancelled(false) { + } + + ~ScopeExitHandler() { + if (!m_cancelled) { + m_handler(); + } + } + + void reset() { + m_cancelled = true; + } + +private: + std::function m_handler; + bool m_cancelled; +}; + BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) : node(node), logger(logger, "BlockchainExplorer"), state(NOT_INITIALIZED), synchronized(false), - observersCounter(0) {} + observersCounter(0) { +} BlockchainExplorer::~BlockchainExplorer() {} @@ -432,6 +492,12 @@ void BlockchainExplorer::poolChanged() { return; } + if (!poolUpdateGuard.beginUpdate()) { + return; + } + + ScopeExitHandler poolUpdateEndGuard(std::bind(&BlockchainExplorer::poolUpdateEndHandler, this)); + std::unique_lock lock(mutex); std::shared_ptr>> rawNewTransactionsPtr = std::make_shared>>(); @@ -454,8 +520,11 @@ void BlockchainExplorer::poolChanged() { ); } ); + request.performAsync(asyncContextCounter, [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](std::error_code ec) { + ScopeExitHandler poolUpdateEndGuard(std::bind(&BlockchainExplorer::poolUpdateEndHandler, this)); + if (ec) { logger(ERROR) << "Can't send poolChanged notification because can't get pool symmetric difference: " << ec.message(); return; @@ -503,21 +572,34 @@ void BlockchainExplorer::poolChanged() { std::placeholders::_1 ) ); + request.performAsync(asyncContextCounter, [this, newTransactionsHashesPtr, newTransactionsPtr, removedTransactionsHashesPtr](std::error_code ec) { + ScopeExitHandler poolUpdateEndGuard(std::bind(&BlockchainExplorer::poolUpdateEndHandler, this)); + if (ec) { logger(ERROR) << "Can't send poolChanged notification because can't get transactions: " << ec.message(); return; } + if (!newTransactionsPtr->empty() || !removedTransactionsHashesPtr->empty()) { observerManager.notify(&IBlockchainObserver::poolUpdated, *newTransactionsPtr, *removedTransactionsHashesPtr); logger(DEBUGGING) << "poolUpdated notification was successfully sent."; } } ); + + poolUpdateEndGuard.reset(); } ); - + + poolUpdateEndGuard.reset(); +} + +void BlockchainExplorer::poolUpdateEndHandler() { + if (poolUpdateGuard.endUpdate()) { + poolChanged(); + } } void BlockchainExplorer::blockchainSynchronized(uint32_t topHeight) { diff --git a/src/BlockchainExplorer/BlockchainExplorer.h b/src/BlockchainExplorer/BlockchainExplorer.h index 8ab27420..df488160 100755 --- a/src/BlockchainExplorer/BlockchainExplorer.h +++ b/src/BlockchainExplorer/BlockchainExplorer.h @@ -74,6 +74,25 @@ public: typedef WalletAsyncContextCounter AsyncContextCounter; private: + void poolUpdateEndHandler(); + + class PoolUpdateGuard { + public: + PoolUpdateGuard(); + + bool beginUpdate(); + bool endUpdate(); + + private: + enum class State { + NONE, + UPDATING, + UPDATE_REQUIRED + }; + + std::atomic m_state; + }; + enum State { NOT_INITIALIZED, INITIALIZED @@ -94,6 +113,6 @@ private: Logging::LoggerRef logger; AsyncContextCounter asyncContextCounter; - + PoolUpdateGuard poolUpdateGuard; }; } diff --git a/src/Common/StreamTools.cpp b/src/Common/StreamTools.cpp index 821d7cc2..f1b96bd6 100755 --- a/src/Common/StreamTools.cpp +++ b/src/Common/StreamTools.cpp @@ -158,7 +158,7 @@ void readVarint(IInputStream& in, uint64_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); diff --git a/src/CryptoNoteConfig.h b/src/CryptoNoteConfig.h index c89db609..2af37810 100644 --- a/src/CryptoNoteConfig.h +++ b/src/CryptoNoteConfig.h @@ -164,7 +164,8 @@ const CheckpointData CHECKPOINTS[] = { {804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"}, {810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"}, {816000, "32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745"}, - {822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"} + {822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"}, + {841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"} }; } // CryptoNote diff --git a/src/CryptoNoteCore/Core.h b/src/CryptoNoteCore/Core.h index d880fd9b..2afaa5b2 100755 --- a/src/CryptoNoteCore/Core.h +++ b/src/CryptoNoteCore/Core.h @@ -48,18 +48,18 @@ namespace CryptoNote { core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger); ~core(); - bool on_idle(); - virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block); //Deprecated. Should be removed with CryptoNoteProtocolHandler. - bool handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); - virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + bool on_idle() override; + virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) override; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + bool handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) override; + virtual i_cryptonote_protocol* get_protocol() override {return m_pprotocol;} const Currency& currency() const { return m_currency; } //-------------------- IMinerHandler ----------------------- - virtual bool handle_block_found(Block& b); - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce); + virtual bool handle_block_found(Block& b) override; + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) override; - bool addObserver(ICoreObserver* observer); - bool removeObserver(ICoreObserver* observer); + bool addObserver(ICoreObserver* observer) override; + bool removeObserver(ICoreObserver* observer) override; miner& get_miner() { return *m_miner; } static void init_options(boost::program_options::options_description& desc); @@ -93,13 +93,13 @@ namespace CryptoNote { virtual bool removeMessageQueue(MessageQueue& messageQueue) override; uint32_t get_current_blockchain_height(); - bool have_block(const Crypto::Hash& id); + bool have_block(const Crypto::Hash& id) override; std::vector buildSparseChain() override; std::vector buildSparseChain(const Crypto::Hash& startBlockId) override; - void on_synchronized(); + void on_synchronized() override; bool is_ready() override; - virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id); + virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) override; bool get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs); bool get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks); template @@ -129,13 +129,13 @@ namespace CryptoNote { //bool get_outs(uint64_t amount, std::list& pkeys); virtual std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, uint32_t& totalBlockCount, uint32_t& startBlockIndex) override; - bool get_stat_info(core_stat_info& st_inf); + bool get_stat_info(core_stat_info& st_inf) override; - virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs); + virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) override; Crypto::Hash get_tail_id(); - virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); - void pause_mining(); - void update_block_template_and_resume_mining(); + virtual bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) override; + void pause_mining() override; + void update_block_template_and_resume_mining() override; //Blockchain& get_blockchain_storage(){return m_blockchain;} //debug functions void print_blockchain(uint32_t start_index, uint32_t end_index); diff --git a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h index 0c606f24..0b2bb060 100755 --- a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h @@ -50,8 +50,8 @@ namespace CryptoNote CryptoNoteProtocolHandler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, IP2pEndpoint* p_net_layout, Logging::ILogger& log); - virtual bool addObserver(ICryptoNoteProtocolObserver* observer); - virtual bool removeObserver(ICryptoNoteProtocolObserver* observer); + virtual bool addObserver(ICryptoNoteProtocolObserver* observer) override; + virtual bool removeObserver(ICryptoNoteProtocolObserver* observer) override; void set_p2p_endpoint(IP2pEndpoint* p2p); // ICore& get_core() { return m_core; } @@ -68,8 +68,8 @@ namespace CryptoNote bool get_payload_sync_data(CORE_SYNC_DATA& hshd); bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, CryptoNoteConnectionContext& context, bool is_inital); int handleCommand(bool is_notify, int command, const BinaryArray& in_buff, BinaryArray& buff_out, CryptoNoteConnectionContext& context, bool& handled); - virtual size_t getPeerCount() const; - virtual uint32_t getObservedHeight() const; + virtual size_t getPeerCount() const override; + virtual uint32_t getObservedHeight() const override; void requestMissingPoolTransactions(const CryptoNoteConnectionContext& context); private: diff --git a/src/HTTP/HttpResponse.cpp b/src/HTTP/HttpResponse.cpp index df5b665f..a11be614 100755 --- a/src/HTTP/HttpResponse.cpp +++ b/src/HTTP/HttpResponse.cpp @@ -41,7 +41,7 @@ const char* getErrorBody(CryptoNote::HttpResponse::HTTP_STATUS status) { case CryptoNote::HttpResponse::STATUS_404: return "Requested url is not found\n"; case CryptoNote::HttpResponse::STATUS_500: - return "Internal server error is occured\n"; + return "Internal server error is occurred\n"; default: throw std::runtime_error("Error body for given status is not available"); } diff --git a/src/InProcessNode/InProcessNode.h b/src/InProcessNode/InProcessNode.h index 6f805d60..43b9da5a 100644 --- a/src/InProcessNode/InProcessNode.h +++ b/src/InProcessNode/InProcessNode.h @@ -51,9 +51,9 @@ public: virtual bool addObserver(INodeObserver* observer) override; virtual bool removeObserver(INodeObserver* observer) override; - virtual size_t getPeerCount() const; - virtual uint32_t getLastLocalBlockHeight() const; - virtual uint32_t getLastKnownBlockHeight() const; + virtual size_t getPeerCount() const override; + virtual uint32_t getLastLocalBlockHeight() const override; + virtual uint32_t getLastKnownBlockHeight() const override; virtual uint32_t getLocalBlockCount() const override; virtual uint32_t getKnownBlockCount() const override; virtual uint64_t getLastLocalBlockTimestamp() const override; diff --git a/src/NodeRpcProxy/NodeRpcProxy.h b/src/NodeRpcProxy/NodeRpcProxy.h index 2139a15d..20848475 100644 --- a/src/NodeRpcProxy/NodeRpcProxy.h +++ b/src/NodeRpcProxy/NodeRpcProxy.h @@ -48,25 +48,25 @@ public: NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort); virtual ~NodeRpcProxy(); - virtual bool addObserver(CryptoNote::INodeObserver* observer); - virtual bool removeObserver(CryptoNote::INodeObserver* observer); + virtual bool addObserver(CryptoNote::INodeObserver* observer) override; + virtual bool removeObserver(CryptoNote::INodeObserver* observer) override; virtual bool addObserver(CryptoNote::INodeRpcProxyObserver* observer); virtual bool removeObserver(CryptoNote::INodeRpcProxyObserver* observer); - virtual void init(const Callback& callback); - virtual bool shutdown(); + virtual void init(const Callback& callback) override; + virtual bool shutdown() override; - virtual size_t getPeerCount() const; - virtual uint32_t getLastLocalBlockHeight() const; - virtual uint32_t getLastKnownBlockHeight() const; + virtual size_t getPeerCount() const override; + virtual uint32_t getLastLocalBlockHeight() const override; + virtual uint32_t getLastKnownBlockHeight() const override; virtual uint32_t getLocalBlockCount() const override; virtual uint32_t getKnownBlockCount() const override; virtual uint64_t getLastLocalBlockTimestamp() const override; - virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); - virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); - virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback); + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) override; + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, diff --git a/src/P2p/NetNodeConfig.cpp b/src/P2p/NetNodeConfig.cpp index 9dec3b36..7b271153 100755 --- a/src/P2p/NetNodeConfig.cpp +++ b/src/P2p/NetNodeConfig.cpp @@ -80,6 +80,7 @@ NetNodeConfig::NetNodeConfig() { allowLocalIp = false; hideMyPort = false; configFolder = Tools::getDefaultDataDirectory(); + testnet = false; } bool NetNodeConfig::init(const boost::program_options::variables_map& vm) diff --git a/src/P2p/NetNodeConfig.h b/src/P2p/NetNodeConfig.h index 4590ef8f..3d9d9c17 100755 --- a/src/P2p/NetNodeConfig.h +++ b/src/P2p/NetNodeConfig.h @@ -70,7 +70,7 @@ private: bool hideMyPort; std::string configFolder; std::string p2pStateFilename; - bool testnet = false; + bool testnet; }; } //namespace nodetool diff --git a/src/PaymentGate/NodeFactory.cpp b/src/PaymentGate/NodeFactory.cpp index 23c77c86..40464eee 100644 --- a/src/PaymentGate/NodeFactory.cpp +++ b/src/PaymentGate/NodeFactory.cpp @@ -26,13 +26,13 @@ namespace PaymentService { class NodeRpcStub: public CryptoNote::INode { public: virtual ~NodeRpcStub() {} - virtual bool addObserver(CryptoNote::INodeObserver* observer) { return true; } - virtual bool removeObserver(CryptoNote::INodeObserver* observer) { return true; } + virtual bool addObserver(CryptoNote::INodeObserver* observer) override { return true; } + virtual bool removeObserver(CryptoNote::INodeObserver* observer) override { return true; } - virtual void init(const Callback& callback) { } - virtual bool shutdown() { return true; } + virtual void init(const Callback& callback) override { } + virtual bool shutdown() override { return true; } - virtual size_t getPeerCount() const { return 0; } + virtual size_t getPeerCount() const override { return 0; } virtual uint32_t getLastLocalBlockHeight() const override { return 0; } virtual uint32_t getLastKnownBlockHeight() const override { return 0; } virtual uint32_t getLocalBlockCount() const override { return 0; } @@ -56,7 +56,7 @@ public: }; virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, - std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) override { isBcActual = true; callback(std::error_code()); } diff --git a/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp b/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp index 19084665..b7a847b8 100755 --- a/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp +++ b/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp @@ -127,6 +127,7 @@ void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) { serializer(fee, "fee"); serializer(hash, "hash"); serializer(blockHeight, "block_height"); + serializer(state, "state"); serializer(timestamp, "timestamp"); serializer(extra, "extra"); serializer(transfers, "transfers"); @@ -154,6 +155,7 @@ void ListTransactionsResponse::serialize(CryptoNote::ISerializer& serializer) { } void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) { + serializer(type, "type"); serializer(address, "address"); serializer(amount, "amount"); } diff --git a/src/PaymentGate/PaymentServiceJsonRpcMessages.h b/src/PaymentGate/PaymentServiceJsonRpcMessages.h index 44aa9215..35ed95ec 100644 --- a/src/PaymentGate/PaymentServiceJsonRpcMessages.h +++ b/src/PaymentGate/PaymentServiceJsonRpcMessages.h @@ -137,6 +137,7 @@ struct GetTransactionRequest { }; struct TransferRpcInfo { + uint8_t type; std::string address; int64_t amount; @@ -152,6 +153,7 @@ struct TransactionRpcInfo { uint64_t blockHeight; uint64_t timestamp; std::string extra; + uint8_t state; std::vector transfers; void serialize(CryptoNote::ISerializer& serializer); diff --git a/src/PaymentGate/WalletService.cpp b/src/PaymentGate/WalletService.cpp index 9525fda6..ad1202b6 100755 --- a/src/PaymentGate/WalletService.cpp +++ b/src/PaymentGate/WalletService.cpp @@ -240,8 +240,8 @@ void WalletService::loadWallet() { void WalletService::loadPaymentsCacheAndTransferIndices() { size_t txCount = wallet->getTransactionCount(); - transfersIndices.resize(1); - transfersIndices[0] = 0; + transfersIndices.reserve(txCount + 1); + transfersIndices.push_back(0); logger(Logging::DEBUGGING) << "seeking for payments among " << txCount << " transactions"; @@ -273,18 +273,18 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req logger(Logging::DEBUGGING) << "Send transaction request came"; try { - std::vector transfers; - makeTransfers(req.destinations, transfers); + std::vector orders; + makeOrders(req.destinations, orders); std::string extra; if (!req.paymentId.empty()) { addPaymentIdToExtra(req.paymentId, extra); } - size_t txId = wallet->transfer(transfers, req.fee, req.mixin, extra, req.unlockTime); + size_t txId = wallet->transfer(orders, req.fee, req.mixin, extra, req.unlockTime); if (txId == CryptoNote::WALLET_INVALID_TRANSACTION_ID) { logger(Logging::WARNING) << "Unable to send transaction"; - throw std::runtime_error("Error occured while sending transaction"); + throw std::runtime_error("Error occurred while sending transaction"); } resp.transactionId = txId; @@ -296,11 +296,11 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req return std::error_code(); } -void WalletService::makeTransfers(const std::vector& destinations, std::vector& transfers) { - transfers.reserve(destinations.size()); +void WalletService::makeOrders(const std::vector& destinations, std::vector& orders) { + orders.reserve(destinations.size()); for (auto dest: destinations) { - transfers.push_back( { dest.address, static_cast(dest.amount) } ); + orders.push_back( { dest.address, dest.amount } ); } } @@ -429,7 +429,8 @@ std::error_code WalletService::getTransactionByTransferId(size_t transferId, siz } auto nextTxId = std::upper_bound(transfersIndices.begin(), transfersIndices.end(), transferId); - transactionId = (nextTxId - transfersIndices.begin()) - 1; + assert(nextTxId != transfersIndices.begin()); + transactionId = std::distance(transfersIndices.begin(), nextTxId) - 1; return std::error_code(); } @@ -443,9 +444,10 @@ void WalletService::fillTransactionRpcInfo(size_t txId, const CryptoNote::Wallet rpcInfo.timestamp = tx.timestamp; rpcInfo.extra = Common::toHex(tx.extra.data(), tx.extra.size()); rpcInfo.hash = Common::podToHex(tx.hash); + rpcInfo.state = static_cast(tx.state); for (size_t transferId = 0; transferId < rpcInfo.transferCount; ++transferId) { auto transfer = wallet->getTransactionTransfer(txId, transferId); - TransferRpcInfo rpcTransfer{ transfer.address, transfer.amount }; + TransferRpcInfo rpcTransfer{ static_cast(transfer.type), transfer.address, transfer.amount }; rpcInfo.transfers.push_back(rpcTransfer); } } @@ -510,7 +512,9 @@ std::error_code WalletService::getTransfer(size_t globalTransferId, bool& found, logger(Logging::DEBUGGING) << "getTransfer request came"; found = false; try { - size_t txId = (std::upper_bound(transfersIndices.begin(), transfersIndices.end(), globalTransferId) - transfersIndices.begin()) - 1; + auto nextTxIt = std::upper_bound(transfersIndices.begin(), transfersIndices.end(), globalTransferId); + assert(nextTxIt != transfersIndices.begin()); + size_t txId = std::distance(transfersIndices.begin(), nextTxIt) - 1; size_t fakeTxId = transfersIndices.size() - 1; if (txId == fakeTxId) { @@ -520,6 +524,7 @@ std::error_code WalletService::getTransfer(size_t globalTransferId, bool& found, auto transferId = globalTransferId - transfersIndices[txId]; auto transfer = wallet->getTransactionTransfer(txId, transferId); + rpcInfo.type = static_cast(transfer.type); rpcInfo.address = transfer.address; rpcInfo.amount = transfer.amount; found = true; @@ -572,12 +577,22 @@ void WalletService::refresh() { for (;;) { auto event = wallet->getEvent(); if (event.type == CryptoNote::TRANSACTION_CREATED || event.type == CryptoNote::TRANSACTION_UPDATED) { - size_t transactionId; + size_t transactionId = event.transactionCreated.transactionIndex; + size_t transferCount = wallet->getTransactionTransferCount(transactionId); + if (event.type == CryptoNote::TRANSACTION_CREATED) { - transactionId = event.transactionCreated.transactionIndex; - transfersIndices.push_back(transfersIndices[transactionId] + wallet->getTransactionTransferCount(transactionId)); + assert(transactionId == transfersIndices.size() - 1); + transfersIndices.push_back(transfersIndices[transactionId] + transferCount); } else { - transactionId = event.transactionUpdated.transactionIndex; + assert(event.type == CryptoNote::TRANSACTION_UPDATED); + assert(transactionId < transfersIndices.size() - 1); + size_t oldTransferCount = transfersIndices[transactionId + 1] - transfersIndices[transactionId]; + int change = static_cast(transferCount) - static_cast(oldTransferCount); + if (change != 0) { + for (size_t i = transactionId + 1; i < transfersIndices.size(); ++i) { + transfersIndices[i] = static_cast(static_cast(transfersIndices[i]) + change); + } + } } auto tx = wallet->getTransaction(transactionId); diff --git a/src/PaymentGate/WalletService.h b/src/PaymentGate/WalletService.h index 6fe3791c..4bad432a 100755 --- a/src/PaymentGate/WalletService.h +++ b/src/PaymentGate/WalletService.h @@ -83,7 +83,7 @@ private: void insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed); void fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo); - void makeTransfers(const std::vector& destinations, std::vector& transfers); + void makeOrders(const std::vector& destinations, std::vector& transfers); struct PaymentItem { std::string paymentId; diff --git a/src/PaymentGateService/ConfigurationManager.cpp b/src/PaymentGateService/ConfigurationManager.cpp index 4f092d5a..ce545a44 100755 --- a/src/PaymentGateService/ConfigurationManager.cpp +++ b/src/PaymentGateService/ConfigurationManager.cpp @@ -39,13 +39,13 @@ bool ConfigurationManager::init(int argc, char** argv) { po::options_description confGeneralOptions; confGeneralOptions.add(cmdGeneralOptions).add_options() - ("testnet", po::value(), "") - ("local", po::value(), ""); + ("testnet", po::bool_switch(), "") + ("local", po::bool_switch(), ""); cmdGeneralOptions.add_options() ("help,h", "produce this help message and exit") - ("local", "start with local node (remote is default)") - ("testnet", "testnet mode"); + ("local", po::bool_switch(), "start with local node (remote is default)") + ("testnet", po::bool_switch(), "testnet mode"); command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); @@ -90,9 +90,8 @@ bool ConfigurationManager::init(int argc, char** argv) { coreConfig.init(confOptions); remoteNodeConfig.init(confOptions); - if (confOptions.count("local")) { - startInprocess = confOptions["local"].as(); - } + netNodeConfig.setTestnet(confOptions["testnet"].as()); + startInprocess = confOptions["local"].as(); } //command line options should override options from config file @@ -101,7 +100,11 @@ bool ConfigurationManager::init(int argc, char** argv) { coreConfig.init(cmdOptions); remoteNodeConfig.init(cmdOptions); - if (cmdOptions.count("local")) { + if (cmdOptions["testnet"].as()) { + netNodeConfig.setTestnet(true); + } + + if (cmdOptions["local"].as()) { startInprocess = true; } diff --git a/src/PaymentGateService/PaymentGateService.cpp b/src/PaymentGateService/PaymentGateService.cpp index 734bcef7..6b3badd6 100755 --- a/src/PaymentGateService/PaymentGateService.cpp +++ b/src/PaymentGateService/PaymentGateService.cpp @@ -159,7 +159,7 @@ void PaymentGateService::runInProcess(Logging::LoggerRef& log) { node->init([&initPromise, &log](std::error_code ec) { if (ec) { - log(Logging::INFO) << "Failed to init node: " << ec.message(); + log(Logging::WARNING, Logging::YELLOW) << "Failed to init node: " << ec.message(); } else { log(Logging::INFO) << "node is inited successfully"; } @@ -215,7 +215,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, try { service->init(); } catch (std::exception& e) { - Logging::LoggerRef(logger, "run")(Logging::ERROR) << "Failed to init walletService reason: " << e.what(); + Logging::LoggerRef(logger, "run")(Logging::ERROR, Logging::BRIGHT_RED) << "Failed to init walletService reason: " << e.what(); return; } @@ -236,7 +236,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, try { service->saveWallet(); } catch (std::exception& ex) { - Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); + Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING, Logging::YELLOW) << "Couldn't save wallet: " << ex.what(); } } } diff --git a/src/PaymentGateService/PaymentServiceConfiguration.cpp b/src/PaymentGateService/PaymentServiceConfiguration.cpp index 7721ae57..3848f79d 100644 --- a/src/PaymentGateService/PaymentServiceConfiguration.cpp +++ b/src/PaymentGateService/PaymentServiceConfiguration.cpp @@ -75,7 +75,7 @@ void Configuration::init(const boost::program_options::variables_map& options) { throw ConfigurationError("It's impossible to use both \"register-service\" and \"unregister-service\" at the same time"); } - if (options.count("testnet") != 0) { + if (options["testnet"].as()) { testnet = true; } diff --git a/src/PaymentGateService/main.cpp b/src/PaymentGateService/main.cpp index b8078363..493f912d 100644 --- a/src/PaymentGateService/main.cpp +++ b/src/PaymentGateService/main.cpp @@ -59,7 +59,7 @@ std::string GetLastErrorMessage(DWORD errorMessageID) void __stdcall serviceHandler(DWORD fdwControl) { if (fdwControl == SERVICE_CONTROL_STOP) { Logging::LoggerRef log(ppg->getLogger(), "serviceHandler"); - log(Logging::INFO) << "Stop signal caught"; + log(Logging::INFO, Logging::BRIGHT_YELLOW) << "Stop signal caught"; SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_STOP_PENDING, 0, NO_ERROR, 0, 0, 0 }; SetServiceStatus(serviceStatusHandle, &serviceStatus); @@ -73,26 +73,26 @@ void __stdcall serviceMain(DWORD dwArgc, char **lpszArgv) { serviceStatusHandle = RegisterServiceCtrlHandler("PaymentGate", serviceHandler); if (serviceStatusHandle == NULL) { - logRef(Logging::FATAL) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError()); return; } SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, 0, NO_ERROR, 0, 1, 3000 }; if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { - logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); return; } serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_RUNNING, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 }; if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { - logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); return; } try { ppg->run(); } catch (std::exception& ex) { - logRef(Logging::FATAL) << "Error occured: " << ex.what(); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Error occurred: " << ex.what(); } serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_STOPPED, 0, NO_ERROR, 0, 0, 0 }; @@ -138,10 +138,14 @@ int runDaemon() { { NULL, NULL } }; + Logging::LoggerRef logRef(ppg->getLogger(), "RunService"); + if (StartServiceCtrlDispatcher(serviceTable) != TRUE) { + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "Couldn't start service: " << GetLastErrorMessage(GetLastError()); return 1; } + logRef(Logging::INFO) << "Service stopped"; return 0; #else @@ -151,7 +155,7 @@ int runDaemon() { //parent return 0; } else if (daemonResult < 0) { - //error occured + //error occurred return 1; } @@ -174,7 +178,7 @@ int registerService() { for (;;) { if (GetModuleFileName(NULL, pathBuff, ARRAYSIZE(pathBuff)) == 0) { - logRef(Logging::FATAL) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError()); ret = 1; break; } @@ -186,7 +190,7 @@ int registerService() { scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); if (scManager == NULL) { - logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); ret = 1; break; } @@ -195,7 +199,7 @@ int registerService() { SERVICE_ERROR_NORMAL, modulePath.c_str(), NULL, NULL, NULL, NULL, NULL); if (scService == NULL) { - logRef(Logging::FATAL) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError()); ret = 1; break; } @@ -231,14 +235,14 @@ int unregisterService() { for (;;) { scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (scManager == NULL) { - logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); ret = 1; break; } scService = OpenService(scManager, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE); if (scService == NULL) { - logRef(Logging::FATAL) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError()); ret = 1; break; } @@ -260,12 +264,12 @@ int unregisterService() { if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { logRef(Logging::INFO) << SERVICE_NAME << " is stopped"; } else { - logRef(Logging::FATAL) << SERVICE_NAME << " failed to stop" << std::endl; + logRef(Logging::FATAL, Logging::BRIGHT_RED) << SERVICE_NAME << " failed to stop" << std::endl; } } if (!DeleteService(scService)) { - logRef(Logging::FATAL) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError()); + logRef(Logging::FATAL, Logging::BRIGHT_RED) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError()); ret = 1; break; } diff --git a/src/Serialization/BinaryInputStreamSerializer.h b/src/Serialization/BinaryInputStreamSerializer.h index 2bf2327c..2bef3e9b 100644 --- a/src/Serialization/BinaryInputStreamSerializer.h +++ b/src/Serialization/BinaryInputStreamSerializer.h @@ -28,7 +28,7 @@ public: BinaryInputStreamSerializer(Common::IInputStream& strm) : stream(strm) {} virtual ~BinaryInputStreamSerializer() {} - virtual ISerializer::SerializerType type() const; + virtual ISerializer::SerializerType type() const override; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; diff --git a/src/Serialization/BinaryOutputStreamSerializer.h b/src/Serialization/BinaryOutputStreamSerializer.h index 5d585c77..d93dc407 100644 --- a/src/Serialization/BinaryOutputStreamSerializer.h +++ b/src/Serialization/BinaryOutputStreamSerializer.h @@ -28,7 +28,7 @@ public: BinaryOutputStreamSerializer(Common::IOutputStream& strm) : stream(strm) {} virtual ~BinaryOutputStreamSerializer() {} - virtual ISerializer::SerializerType type() const; + virtual ISerializer::SerializerType type() const override; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; diff --git a/src/Serialization/JsonInputValueSerializer.h b/src/Serialization/JsonInputValueSerializer.h index 232e4f81..f3fc82ae 100644 --- a/src/Serialization/JsonInputValueSerializer.h +++ b/src/Serialization/JsonInputValueSerializer.h @@ -29,7 +29,7 @@ public: JsonInputValueSerializer(Common::JsonValue&& value); virtual ~JsonInputValueSerializer(); - SerializerType type() const; + SerializerType type() const override; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; diff --git a/src/Serialization/JsonOutputStreamSerializer.h b/src/Serialization/JsonOutputStreamSerializer.h index c86e8d67..6eae195e 100644 --- a/src/Serialization/JsonOutputStreamSerializer.h +++ b/src/Serialization/JsonOutputStreamSerializer.h @@ -28,7 +28,7 @@ public: JsonOutputStreamSerializer(); virtual ~JsonOutputStreamSerializer(); - SerializerType type() const; + SerializerType type() const override; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; diff --git a/src/Serialization/KVBinaryOutputStreamSerializer.h b/src/Serialization/KVBinaryOutputStreamSerializer.h index 87ab6f6d..8c859a83 100644 --- a/src/Serialization/KVBinaryOutputStreamSerializer.h +++ b/src/Serialization/KVBinaryOutputStreamSerializer.h @@ -32,7 +32,7 @@ public: void dump(Common::IOutputStream& target); - virtual ISerializer::SerializerType type() const; + virtual ISerializer::SerializerType type() const override; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; diff --git a/src/SimpleWallet/SimpleWallet.cpp b/src/SimpleWallet/SimpleWallet.cpp index 8217c46e..593c9ce5 100644 --- a/src/SimpleWallet/SimpleWallet.cpp +++ b/src/SimpleWallet/SimpleWallet.cpp @@ -464,7 +464,8 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C logManager(log), logger(log, "simplewallet"), m_refresh_progress_reporter(*this), - m_initResultPromise(nullptr) { + m_initResultPromise(nullptr), + m_walletSynchronized(false) { m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); //m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); @@ -734,8 +735,21 @@ bool simple_wallet::save(const std::vector &args) } bool simple_wallet::reset(const std::vector &args) { + { + std::unique_lock lock(m_walletSynchronizedMutex); + m_walletSynchronized = false; + } + m_wallet->reset(); - success_msg_writer(true) << "Reset is complete successfully"; + success_msg_writer(true) << "Reset completed successfully."; + + std::unique_lock lock(m_walletSynchronizedMutex); + while (!m_walletSynchronized) { + m_walletSynchronizedCV.wait(lock); + } + + std::cout << std::endl; + return true; } @@ -814,10 +828,6 @@ void simple_wallet::initCompleted(std::error_code result) { } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::localBlockchainUpdated(uint32_t height) { - m_refresh_progress_reporter.update(height, false); -} -//---------------------------------------------------------------------------------------------------- void simple_wallet::connectionStatusUpdated(bool connected) { if (connected) { logger(INFO, GREEN) << "Wallet connected to daemon."; @@ -854,6 +864,19 @@ void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transac } } //---------------------------------------------------------------------------------------------------- +void simple_wallet::synchronizationCompleted(std::error_code result) { + std::unique_lock lock(m_walletSynchronizedMutex); + m_walletSynchronized = true; + m_walletSynchronizedCV.notify_one(); +} + +void simple_wallet::synchronizationProgressUpdated(uint32_t current, uint32_t total) { + std::unique_lock lock(m_walletSynchronizedMutex); + if (!m_walletSynchronized) { + m_refresh_progress_reporter.update(current, false); + } +} + bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) { success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) << ", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance()); @@ -1010,6 +1033,15 @@ bool simple_wallet::transfer(const std::vector &args) { } //---------------------------------------------------------------------------------------------------- bool simple_wallet::run() { + { + std::unique_lock lock(m_walletSynchronizedMutex); + while (!m_walletSynchronized) { + m_walletSynchronizedCV.wait(lock); + } + } + + std::cout << std::endl; + std::string addr_start = m_wallet->getAddress().substr(0, 6); m_consoleHandler.start(false, "[wallet " + addr_start + "]: ", Common::Console::Color::BrightYellow); return true; diff --git a/src/SimpleWallet/SimpleWallet.h b/src/SimpleWallet/SimpleWallet.h index 6bbe0541..cc9db868 100755 --- a/src/SimpleWallet/SimpleWallet.h +++ b/src/SimpleWallet/SimpleWallet.h @@ -17,8 +17,10 @@ #pragma once -#include +#include #include +#include +#include #include @@ -95,13 +97,11 @@ namespace CryptoNote void printConnectionError() const; - //---------------- IWalletObserver ------------------------- + //---------------- IWalletLegacyObserver ------------------------- virtual void initCompleted(std::error_code result) override; virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override; - //---------------------------------------------------------- - - //----------------- INodeObserver -------------------------- - virtual void localBlockchainUpdated(uint32_t height) override; + virtual void synchronizationCompleted(std::error_code result) override; + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override; //---------------------------------------------------------- //----------------- INodeRpcProxyObserver -------------------------- @@ -130,8 +130,7 @@ namespace CryptoNote m_blockchain_height = (std::max)(m_blockchain_height, height); } - if (std::chrono::milliseconds(1) < current_time - m_print_time || force) - { + if (std::chrono::milliseconds(1) < current_time - m_print_time || force) { std::cout << "Height " << height << " of " << m_blockchain_height << '\r'; m_print_time = current_time; } @@ -140,17 +139,9 @@ namespace CryptoNote private: void update_blockchain_height() { - std::string err; uint64_t blockchain_height = m_simple_wallet.m_node->getLastLocalBlockHeight(); - if (err.empty()) - { - m_blockchain_height = blockchain_height; - m_blockchain_height_update_time = std::chrono::system_clock::now(); - } - else - { - std::cerr << "Failed to get current blockchain height: " << err; - } + m_blockchain_height = blockchain_height; + m_blockchain_height_update_time = std::chrono::system_clock::now(); } private: @@ -182,5 +173,9 @@ namespace CryptoNote std::unique_ptr m_node; std::unique_ptr m_wallet; refresh_progress_reporter_t m_refresh_progress_reporter; + + bool m_walletSynchronized; + std::mutex m_walletSynchronizedMutex; + std::condition_variable m_walletSynchronizedCV; }; } diff --git a/src/Transfers/BlockchainSynchronizer.cpp b/src/Transfers/BlockchainSynchronizer.cpp index 08f36149..6517af22 100755 --- a/src/Transfers/BlockchainSynchronizer.cpp +++ b/src/Transfers/BlockchainSynchronizer.cpp @@ -372,7 +372,7 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { lk.unlock(); switch (result) { - case UpdateConsumersResult::errorOccured: + case UpdateConsumersResult::errorOccurred: if (setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { return futureState != State::stopped; @@ -435,7 +435,7 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons static_cast(interval.blocks.size()) - startOffset); smthChanged = true; } else { - return UpdateConsumersResult::errorOccured; + return UpdateConsumersResult::errorOccurred; } } } diff --git a/src/Transfers/BlockchainSynchronizer.h b/src/Transfers/BlockchainSynchronizer.h index 10591524..272f6016 100755 --- a/src/Transfers/BlockchainSynchronizer.h +++ b/src/Transfers/BlockchainSynchronizer.h @@ -92,7 +92,7 @@ private: enum class UpdateConsumersResult { nothingChanged = 0, addedNewBlocks = 1, - errorOccured = 2 + errorOccurred = 2 }; //void startSync(); diff --git a/src/Wallet/WalletErrors.h b/src/Wallet/WalletErrors.h index a0e55413..f8eaa5d3 100644 --- a/src/Wallet/WalletErrors.h +++ b/src/Wallet/WalletErrors.h @@ -45,7 +45,8 @@ enum WalletErrorCodes { KEY_GENERATION_ERROR, INDEX_OUT_OF_RANGE, ADDRESS_ALREADY_EXISTS, - TRACKING_MODE + TRACKING_MODE, + WRONG_PARAMETERS }; // custom category: @@ -66,7 +67,7 @@ public: case NOT_INITIALIZED: return "Object was not initialized"; case WRONG_PASSWORD: return "The password is wrong"; case ALREADY_INITIALIZED: return "The object is already initialized"; - case INTERNAL_WALLET_ERROR: return "Internal error occured"; + case INTERNAL_WALLET_ERROR: return "Internal error occurred"; case MIXIN_COUNT_TOO_BIG: return "MixIn count is too big"; case BAD_ADDRESS: return "Bad address"; case TRANSACTION_SIZE_TOO_BIG: return "Transaction size is too big"; @@ -83,6 +84,7 @@ public: case INDEX_OUT_OF_RANGE: return "Index is out of range"; case ADDRESS_ALREADY_EXISTS: return "Address already exists"; case TRACKING_MODE: return "The wallet is in tracking mode"; + case WRONG_PARAMETERS: return "Wrong parameters passed"; default: return "Unknown error"; } } diff --git a/src/Wallet/WalletGreen.cpp b/src/Wallet/WalletGreen.cpp index a83b2e87..a0d6347e 100755 --- a/src/Wallet/WalletGreen.cpp +++ b/src/Wallet/WalletGreen.cpp @@ -153,6 +153,64 @@ size_t getTransactionSize(const ITransactionReader& transaction) { return transaction.getTransactionData().size(); } +std::vector convertOrdersToTransfer(const std::vector& orders) { + std::vector transfers; + transfers.reserve(orders.size()); + + for (const auto& order: orders) { + WalletTransfer transfer; + + if (order.amount > std::numeric_limits::max()) { + throw std::system_error(make_error_code(CryptoNote::error::WRONG_AMOUNT), + "Order amount must not exceed " + std::to_string(std::numeric_limits::max())); + } + + transfer.type = WalletTransferType::USUAL; + transfer.address = order.address; + transfer.amount = static_cast(order.amount); + + transfers.emplace_back(std::move(transfer)); + } + + return transfers; +} + +uint64_t calculateDonationAmount(uint64_t freeAmount, uint64_t donationThreshold, uint64_t dustThreshold) { + std::vector decomposedAmounts; + decomposeAmount(freeAmount, dustThreshold, decomposedAmounts); + + std::sort(decomposedAmounts.begin(), decomposedAmounts.end(), std::greater()); + + uint64_t donationAmount = 0; + for (auto amount: decomposedAmounts) { + if (amount > donationThreshold - donationAmount) { + continue; + } + + donationAmount += amount; + } + + assert(donationAmount <= freeAmount); + return donationAmount; +} + +uint64_t pushDonationTransferIfPossible(const DonationSettings& donation, uint64_t freeAmount, uint64_t dustThreshold, std::vector& destinations) { + uint64_t donationAmount = 0; + if (!donation.address.empty() && donation.threshold != 0) { + if (donation.threshold > std::numeric_limits::max()) { + throw std::system_error(make_error_code(error::WRONG_AMOUNT), + "Donation threshold must not exceed " + std::to_string(std::numeric_limits::max())); + } + + donationAmount = calculateDonationAmount(freeAmount, donation.threshold, dustThreshold); + if (donationAmount != 0) { + destinations.emplace_back(WalletTransfer {WalletTransferType::DONATION, donation.address, static_cast(donationAmount)}); + } + } + + return donationAmount; +} + } namespace CryptoNote { @@ -163,7 +221,7 @@ WalletGreen::WalletGreen(System::Dispatcher& dispatcher, const Currency& currenc m_node(node), m_blockchainSynchronizer(node, currency.genesisBlockHash()), m_synchronizer(currency, m_blockchainSynchronizer, node), - m_eventOccured(m_dispatcher), + m_eventOccurred(m_dispatcher), m_readyEvent(m_dispatcher), m_transactionSoftLockTime(transactionSoftLockTime) { @@ -549,9 +607,7 @@ WalletTransfer WalletGreen::getTransactionTransfer(size_t transactionIndex, size throw std::system_error(std::make_error_code(std::errc::invalid_argument)); } - auto it = bounds.first; - std::advance(it, transferIndex); - return it->second; + return std::next(bounds.first, transferIndex)->second; } std::pair WalletGreen::getTransactionTransfers( @@ -566,73 +622,111 @@ std::pair Wall return bounds; } -size_t WalletGreen::transfer(const WalletTransfer& destination, - uint64_t fee, - uint64_t mixIn, - std::string const& extra, - uint64_t unlockTimestamp) -{ - std::vector destinations { destination }; - return transfer(destinations, fee, mixIn, extra, unlockTimestamp); -} - -size_t WalletGreen::transfer( - const std::vector& destinations, +size_t WalletGreen::transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { - System::EventLock lk(m_readyEvent); + TransactionParameters transactionParameters; + transactionParameters.destinations.emplace_back(destination); + transactionParameters.fee = fee; + transactionParameters.mixIn = mixIn; + transactionParameters.extra = extra; + transactionParameters.unlockTimestamp = unlockTimestamp; - throwIfNotInitialized(); - throwIfStopped(); - throwIfTrackingMode(); + return transfer(transactionParameters); +} - return doTransfer(pickWalletsWithMoney(), destinations, fee, mixIn, extra, unlockTimestamp); +size_t WalletGreen::transfer( + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + + TransactionParameters transactionParameters; + transactionParameters.destinations = destinations; + transactionParameters.fee = fee; + transactionParameters.mixIn = mixIn; + transactionParameters.extra = extra; + transactionParameters.unlockTimestamp = unlockTimestamp; + + return transfer(transactionParameters); } size_t WalletGreen::transfer( const std::string& sourceAddress, - const WalletTransfer& destination, + const WalletOrder& destination, uint64_t fee, uint64_t mixIn, std::string const& extra, uint64_t unlockTimestamp) { - std::vector destinations { destination }; - return transfer(sourceAddress, destinations, fee, mixIn, extra, unlockTimestamp); + + TransactionParameters transactionParameters; + transactionParameters.sourceAddress = sourceAddress; + transactionParameters.destinations.emplace_back(destination); + transactionParameters.fee = fee; + transactionParameters.mixIn = mixIn; + transactionParameters.extra = extra; + transactionParameters.unlockTimestamp = unlockTimestamp; + + return transfer(transactionParameters); } size_t WalletGreen::transfer( const std::string& sourceAddress, - const std::vector& destinations, + const std::vector& destinations, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { + + TransactionParameters transactionParameters; + transactionParameters.sourceAddress = sourceAddress; + transactionParameters.destinations = destinations; + transactionParameters.fee = fee; + transactionParameters.mixIn = mixIn; + transactionParameters.extra = extra; + transactionParameters.unlockTimestamp = unlockTimestamp; + + return transfer(transactionParameters); +} + +size_t WalletGreen::transfer(const TransactionParameters& transactionParameters) { System::EventLock lk(m_readyEvent); throwIfNotInitialized(); - throwIfStopped(); throwIfTrackingMode(); + throwIfStopped(); - WalletOuts wallet = pickWallet(sourceAddress); std::vector wallets; - - if (!wallet.outs.empty()) { - wallets.push_back(wallet); + if (!transactionParameters.sourceAddress.empty()) { + WalletOuts wallet = pickWallet(transactionParameters.sourceAddress); + if (!wallet.outs.empty()) { + wallets.emplace_back(std::move(wallet)); + } + } else { + wallets = pickWalletsWithMoney(); } - return doTransfer(std::move(wallets), destinations, fee, mixIn, extra, unlockTimestamp); + return doTransfer(std::move(wallets), + transactionParameters.destinations, + transactionParameters.fee, + transactionParameters.mixIn, + transactionParameters.extra, + transactionParameters.unlockTimestamp, + transactionParameters.donation); } size_t WalletGreen::doTransfer(std::vector&& wallets, - const std::vector& destinations, + const std::vector& orders, uint64_t fee, uint64_t mixIn, const std::string& extra, - uint64_t unlockTimestamp) { - if (destinations.empty()) { + uint64_t unlockTimestamp, + const DonationSettings& donation) { + if (orders.empty()) { throw std::system_error(make_error_code(error::ZERO_DESTINATION)); } @@ -640,6 +734,11 @@ size_t WalletGreen::doTransfer(std::vector&& wallets, throw std::system_error(make_error_code(error::FEE_TOO_SMALL)); } + if (donation.address.empty() ^ (donation.threshold == 0)) { + throw std::system_error(make_error_code(error::WRONG_PARAMETERS), "DonationSettings must have both address and threshold parameters filled"); + } + + std::vector destinations = convertOrdersToTransfer(orders); validateAddresses(destinations, m_currency); uint64_t neededMoney = countNeededMoney(destinations, fee); @@ -661,16 +760,19 @@ size_t WalletGreen::doTransfer(std::vector&& wallets, std::vector keysInfo; prepareInputs(selectedTransfers, mixinResult, mixIn, keysInfo); - WalletTransfer changeDestination; - changeDestination.address = m_currency.accountAddressAsString({ m_walletsContainer.get()[0].spendPublicKey, m_viewPublicKey }); - changeDestination.amount = foundMoney - neededMoney; + uint64_t donationAmount = pushDonationTransferIfPossible(donation, foundMoney - neededMoney, m_currency.defaultDustThreshold(), destinations); + uint64_t changeAmount = foundMoney - neededMoney - donationAmount; - std::vector decomposedOutputs; - splitDestinations(destinations, changeDestination, m_currency.defaultDustThreshold(), m_currency, decomposedOutputs); + std::vector decomposedOutputs = splitDestinations(destinations, m_currency.defaultDustThreshold(), m_currency); + if (changeAmount != 0) { + CryptoNote::AccountPublicAddress changeDestination = { m_walletsContainer.get()[0].spendPublicKey, m_viewPublicKey }; + auto splittedChange = splitAmount(changeAmount, changeDestination, m_currency.defaultDustThreshold()); + decomposedOutputs.emplace_back(std::move(splittedChange)); + } std::unique_ptr tx = makeTransaction(decomposedOutputs, keysInfo, extra, unlockTimestamp); - size_t txId = insertOutgoingTransaction(tx->getTransactionHash(), -static_cast(neededMoney), fee, tx->getExtra(), unlockTimestamp); + size_t txId = insertOutgoingTransactionAndPushEvent(tx->getTransactionHash(), -static_cast(neededMoney), fee, tx->getExtra(), unlockTimestamp); pushBackOutgoingTransfers(txId, destinations); m_fusionTxsCache.emplace(txId, false); @@ -679,34 +781,34 @@ size_t WalletGreen::doTransfer(std::vector&& wallets, try { sendTransaction(tx.get()); } catch (std::exception&) { + updateTransactionStateAndPushEvent(txId, WalletTransactionState::FAILED); deleteSpentOutputs(tx->getTransactionHash()); - pushEvent(makeTransactionCreatedEvent(txId)); throw; } - auto txIt = m_transactions.get().begin(); - std::advance(txIt, txId); - m_transactions.get().modify(txIt, - [] (WalletTransaction& tx) { tx.state = WalletTransactionState::SUCCEEDED; }); - - m_change[tx->getTransactionHash()] = changeDestination.amount; + m_change[tx->getTransactionHash()] = changeAmount; + updateTransactionStateAndPushEvent(txId, WalletTransactionState::SUCCEEDED); updateUsedWalletsBalances(selectedTransfers); - pushEvent(makeTransactionCreatedEvent(txId)); - return txId; } -void WalletGreen::pushBackOutgoingTransfers(size_t txId, const std::vector &destinations) { +void WalletGreen::pushBackOutgoingTransfers(size_t txId, const std::vector& destinations) { for (const auto& dest: destinations) { - WalletTransfer d { dest.address, -dest.amount }; + WalletTransfer d; + d.type = dest.type; + d.address = dest.address; + d.amount = -dest.amount; + m_transfers.push_back(std::make_pair(txId, d)); } } -size_t WalletGreen::insertOutgoingTransaction(const Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp) { +size_t WalletGreen::insertOutgoingTransactionAndPushEvent(const Hash& transactionHash, + int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp) { + WalletTransaction insertTx; - insertTx.state = WalletTransactionState::FAILED; + insertTx.state = WalletTransactionState::CREATED; insertTx.creationTime = static_cast(time(nullptr)); insertTx.unlockTime = unlockTimestamp; insertTx.blockHeight = CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT; @@ -720,59 +822,67 @@ size_t WalletGreen::insertOutgoingTransaction(const Hash& transactionHash, int64 size_t txId = m_transactions.get().size(); m_transactions.get().push_back(std::move(insertTx)); + pushEvent(makeTransactionCreatedEvent(txId)); + return txId; } -bool WalletGreen::transactionExists(const Hash& hash) { - auto& hashIndex = m_transactions.get(); - auto it = hashIndex.find(hash); - return it != hashIndex.end(); -} +void WalletGreen::updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state) { + auto it = std::next(m_transactions.get().begin(), transactionId); -bool WalletGreen::updateWalletTransactionInfo(const Hash& hash, const CryptoNote::TransactionInformation& info) { - auto& hashIndex = m_transactions.get(); - - bool updated = false; - auto it = hashIndex.find(hash); - if (it != hashIndex.end()) { - bool r = hashIndex.modify(it, [&info, &updated] (WalletTransaction& transaction) { - if (transaction.blockHeight != info.blockHeight) { - transaction.blockHeight = info.blockHeight; - updated = true; - } - - if (transaction.timestamp != info.timestamp) { - transaction.timestamp = info.timestamp; - updated = true; - } - - if (transaction.state != WalletTransactionState::SUCCEEDED) { - //transaction may be deleted first then added again - transaction.state = WalletTransactionState::SUCCEEDED; - updated = true; - } - - // Fix LegacyWallet error. Some old versions didn't fill extra field - if (transaction.extra.empty() && !info.extra.empty()) { - transaction.extra = Common::asString(info.extra); - updated = true; - } - - bool isBase = info.totalAmountIn == 0; - if (transaction.isBase != isBase) { - transaction.isBase = isBase; - updated = true; - } + if (it->state != state) { + m_transactions.get().modify(it, [state](WalletTransaction& tx) { + tx.state = state; }); - assert(r); - return updated; + pushEvent(makeTransactionUpdatedEvent(transactionId)); } - - throw std::system_error(make_error_code(std::errc::invalid_argument)); } -size_t WalletGreen::insertBlockchainTransaction(const TransactionInformation& info, int64_t txBalance) { +void WalletGreen::updateWalletTransactionInfoAndPushEvent(size_t transactionId, const CryptoNote::TransactionInformation& info, bool transfersUpdated) { + auto& txIdIndex = m_transactions.get(); + assert(transactionId < txIdIndex.size()); + auto it = std::next(txIdIndex.begin(), transactionId); + + bool updated = false; + bool r = txIdIndex.modify(it, [&info, &updated](WalletTransaction& transaction) { + if (transaction.blockHeight != info.blockHeight) { + transaction.blockHeight = info.blockHeight; + updated = true; + } + + if (transaction.timestamp != info.timestamp) { + transaction.timestamp = info.timestamp; + updated = true; + } + + if (transaction.state != WalletTransactionState::SUCCEEDED) { + //transaction may be deleted first then added again + transaction.state = WalletTransactionState::SUCCEEDED; + updated = true; + } + + // Fix LegacyWallet error. Some old versions didn't fill extra field + if (transaction.extra.empty() && !info.extra.empty()) { + transaction.extra = Common::asString(info.extra); + updated = true; + } + + bool isBase = info.totalAmountIn == 0; + if (transaction.isBase != isBase) { + transaction.isBase = isBase; + updated = true; + } + }); + + assert(r); + + if (updated || transfersUpdated) { + pushEvent(makeTransactionUpdatedEvent(transactionId)); + } +} + +size_t WalletGreen::insertBlockchainTransactionAndPushEvent(const TransactionInformation& info, int64_t txBalance) { auto& index = m_transactions.get(); WalletTransaction tx; @@ -787,8 +897,12 @@ size_t WalletGreen::insertBlockchainTransaction(const TransactionInformation& in tx.creationTime = info.timestamp; tx.isBase = info.totalAmountIn == 0; + size_t txId = index.size(); index.push_back(std::move(tx)); - return index.size() - 1; + + pushEvent(makeTransactionCreatedEvent(txId)); + + return txId; } void WalletGreen::insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount) { @@ -796,7 +910,7 @@ void WalletGreen::insertIncomingTransfer(size_t txId, const std::string& address return val < a.first; }); - WalletTransfer tr { address, amount }; + WalletTransfer tr { WalletTransferType::USUAL, address, amount }; m_transfers.insert(it, std::make_pair(txId, std::move(tr))); } @@ -805,12 +919,23 @@ std::unique_ptr WalletGreen::makeTransaction(const std std::unique_ptr tx = createTransaction(); + typedef std::pair AmountToAddress; + std::vector amountsToAddresses; for (const auto& output: decomposedOutputs) { for (auto amount: output.amounts) { - tx->addOutput(amount, output.receiver); + amountsToAddresses.emplace_back(AmountToAddress{&output.receiver, amount}); } } + std::shuffle(amountsToAddresses.begin(), amountsToAddresses.end(), std::default_random_engine{Crypto::rand()}); + std::sort(amountsToAddresses.begin(), amountsToAddresses.end(), [] (const AmountToAddress& left, const AmountToAddress& right) { + return left.second < right.second; + }); + + for (const auto& amountToAddress: amountsToAddresses) { + tx->addOutput(amountToAddress.second, *amountToAddress.first); + } + tx->setUnlockTime(unlockTimestamp); tx->appendExtra(Common::asBinaryArray(extra)); @@ -984,26 +1109,30 @@ WalletGreen::WalletOuts WalletGreen::pickWallet(const std::string& address) { return outs; } -void WalletGreen::splitDestinations(const std::vector& destinations, - const CryptoNote::WalletTransfer& changeDestination, +std::vector WalletGreen::splitDestinations(const std::vector& destinations, uint64_t dustThreshold, - const CryptoNote::Currency& currency, - std::vector& decomposedOutputs) { + const CryptoNote::Currency& currency) { + std::vector decomposedOutputs; for (const auto& destination: destinations) { - ReceiverAmounts receiverAmounts; - - parseAddressString(destination.address, currency, receiverAmounts.receiver); - decomposeAmount(destination.amount, dustThreshold, receiverAmounts.amounts); - - decomposedOutputs.push_back(std::move(receiverAmounts)); + AccountPublicAddress address; + parseAddressString(destination.address, currency, address); + decomposedOutputs.push_back(splitAmount(destination.amount, address, dustThreshold)); } - ReceiverAmounts changeAmounts; - parseAddressString(changeDestination.address, currency, changeAmounts.receiver); - decomposeAmount(changeDestination.amount, dustThreshold, changeAmounts.amounts); + return decomposedOutputs; +} - decomposedOutputs.push_back(std::move(changeAmounts)); +CryptoNote::WalletGreen::ReceiverAmounts WalletGreen::splitAmount( + uint64_t amount, + const AccountPublicAddress& destination, + uint64_t dustThreshold) { + + ReceiverAmounts receiverAmounts; + + receiverAmounts.receiver = destination; + decomposeAmount(amount, dustThreshold, receiverAmounts.amounts); + return receiverAmounts; } void WalletGreen::prepareInputs( @@ -1067,7 +1196,7 @@ void WalletGreen::start() { void WalletGreen::stop() { m_stopped = true; - m_eventOccured.set(); + m_eventOccurred.set(); } WalletEvent WalletGreen::getEvent() { @@ -1075,8 +1204,8 @@ WalletEvent WalletGreen::getEvent() { throwIfStopped(); while(m_events.empty()) { - m_eventOccured.wait(); - m_eventOccured.clear(); + m_eventOccurred.wait(); + m_eventOccurred.clear(); throwIfStopped(); } @@ -1161,25 +1290,23 @@ void WalletGreen::transactionUpdated(ITransfersSubscription* object, const Hash& int64_t txBalance; bool found = container->getTransactionInformation(transactionHash, info, txBalance); assert(found); + bool addTransfer = txBalance > 0; - if (transactionExists(info.transactionHash)) { - bool updated = updateWalletTransactionInfo(info.transactionHash, info); + auto& hashIndex = m_transactions.get(); + auto it = hashIndex.find(info.transactionHash); - auto id = getTransactionId(info.transactionHash); - - if (updated) { - pushEvent(makeTransactionUpdatedEvent(id)); - } + size_t transactionId; + if (it != hashIndex.end()) { + transactionId = std::distance(m_transactions.get().begin(), m_transactions.project(it)); + updateWalletTransactionInfoAndPushEvent(transactionId, info, addTransfer); } else { - auto id = insertBlockchainTransaction(info, txBalance); - if (txBalance > 0) { - AccountPublicAddress walletAddress{ getWalletRecord(container).spendPublicKey, m_viewPublicKey }; - insertIncomingTransfer(id, m_currency.accountAddressAsString(walletAddress), txBalance); - } + transactionId = insertBlockchainTransactionAndPushEvent(info, txBalance); + m_fusionTxsCache.emplace(transactionId, isFusionTransaction(m_transactions.get()[transactionId])); + } - m_fusionTxsCache.emplace(id, isFusionTransaction(m_transactions.get()[id])); - - pushEvent(makeTransactionCreatedEvent(id)); + if (addTransfer) { + AccountPublicAddress walletAddress{ getWalletRecord(container).spendPublicKey, m_viewPublicKey }; + insertIncomingTransfer(transactionId, m_currency.accountAddressAsString(walletAddress), txBalance); } m_change.erase(transactionHash); @@ -1194,7 +1321,7 @@ void WalletGreen::transactionUpdated(ITransfersSubscription* object, const Hash& void WalletGreen::pushEvent(const WalletEvent& event) { m_events.push(event); - m_eventOccured.set(); + m_eventOccurred.set(); } size_t WalletGreen::getTransactionId(const Hash& transactionHash) const { @@ -1393,9 +1520,9 @@ size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin) System::EventLock lk(m_readyEvent); throwIfNotInitialized(); + throwIfTrackingMode(); throwIfStopped(); - //TODO: check if wallet is not in tracking mode const size_t MAX_FUSION_OUTPUT_COUNT = 4; if (threshold <= m_currency.defaultDustThreshold()) { @@ -1455,11 +1582,11 @@ size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin) throw std::runtime_error("Unable to create fusion transaction"); } - size_t transactionId = insertOutgoingTransaction(fusionTransaction->getTransactionHash(), 0, 0, fusionTransaction->getExtra(), 0); + size_t transactionId = insertOutgoingTransactionAndPushEvent(fusionTransaction->getTransactionHash(), 0, 0, fusionTransaction->getExtra(), 0); m_fusionTxsCache.emplace(transactionId, true); - WalletTransfer destination = {m_currency.accountAddressAsString({m_walletsContainer.get().begin()->spendPublicKey, - m_viewPublicKey }), 0}; + std::string address = m_currency.accountAddressAsString({m_walletsContainer.get().begin()->spendPublicKey, m_viewPublicKey }); + WalletTransfer destination = {WalletTransferType::USUAL, address, 0}; pushBackOutgoingTransfers(transactionId, std::vector {destination}); markOutputsSpent(fusionTransaction->getTransactionHash(), fusionInputs); @@ -1467,22 +1594,15 @@ size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin) try { sendTransaction(fusionTransaction.get()); } catch (std::exception&) { + updateTransactionStateAndPushEvent(transactionId, WalletTransactionState::FAILED); deleteSpentOutputs(fusionTransaction->getTransactionHash()); - pushEvent(makeTransactionCreatedEvent(transactionId)); throw; } - auto txIt = m_transactions.get().begin(); - std::advance(txIt, transactionId); - m_transactions.get().modify(txIt, - [] (WalletTransaction& tx) { tx.state = WalletTransactionState::SUCCEEDED; }); - m_change[fusionTransaction->getTransactionHash()] = transactionAmount; - + updateTransactionStateAndPushEvent(transactionId, WalletTransactionState::SUCCEEDED); updateUsedWalletsBalances(fusionInputs); - pushEvent(makeTransactionCreatedEvent(transactionId)); - return transactionId; } diff --git a/src/Wallet/WalletGreen.h b/src/Wallet/WalletGreen.h index 1fdcf7f7..5d3cbfd4 100755 --- a/src/Wallet/WalletGreen.h +++ b/src/Wallet/WalletGreen.h @@ -66,10 +66,11 @@ public: virtual size_t getTransactionTransferCount(size_t transactionIndex) const override; virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const override; - virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; - virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; - virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; - virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::string& sourceAddress, const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const TransactionParameters& sendingTransaction) override; virtual void start() override; virtual void stop() override; @@ -145,11 +146,12 @@ protected: bool isFusionTransaction(const WalletTransaction& walletTx) const; size_t doTransfer(std::vector&& wallets, - const std::vector& destinations, + const std::vector& orders, uint64_t fee, uint64_t mixIn, const std::string& extra, - uint64_t unlockTimestamp); + uint64_t unlockTimestamp, + const DonationSettings& donation); void requestMixinOuts(const std::vector& selectedTransfers, uint64_t mixIn, @@ -166,18 +168,20 @@ protected: std::vector&& wallets, std::vector& selectedTransfers); - void splitDestinations(const std::vector& destinations, const WalletTransfer& changeDestination, - uint64_t dustThreshold, const Currency& currency, std::vector& decomposedOutputs); + std::vector splitDestinations(const std::vector& destinations, + uint64_t dustThreshold, const Currency& currency); + ReceiverAmounts splitAmount(uint64_t amount, const AccountPublicAddress& destination, uint64_t dustThreshold); std::unique_ptr makeTransaction(const std::vector& decomposedOutputs, std::vector& keysInfo, const std::string& extra, uint64_t unlockTimestamp); void sendTransaction(ITransaction* tx); - size_t insertOutgoingTransaction(const Crypto::Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp); - bool transactionExists(const Crypto::Hash& hash); - bool updateWalletTransactionInfo(const Crypto::Hash& hash, const CryptoNote::TransactionInformation& info); - size_t insertBlockchainTransaction(const TransactionInformation& info, int64_t txBalance); + size_t insertBlockchainTransactionAndPushEvent(const TransactionInformation& info, int64_t txBalance); + size_t insertOutgoingTransactionAndPushEvent(const Crypto::Hash& transactionHash, + int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp); + void updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state); + void updateWalletTransactionInfoAndPushEvent(size_t transactionId, const CryptoNote::TransactionInformation& info, bool transfersUpdated); void insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount); void pushBackOutgoingTransfers(size_t txId, const std::vector &destinations); void insertUnlockTransactionJob(const Crypto::Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container); @@ -219,7 +223,7 @@ protected: BlockchainSynchronizer m_blockchainSynchronizer; TransfersSyncronizer m_synchronizer; - System::Event m_eventOccured; + System::Event m_eventOccurred; std::queue m_events; mutable System::Event m_readyEvent; diff --git a/src/Wallet/WalletRpcServerCommandsDefinitions.h b/src/Wallet/WalletRpcServerCommandsDefinitions.h index e89554ac..2e0fc322 100755 --- a/src/Wallet/WalletRpcServerCommandsDefinitions.h +++ b/src/Wallet/WalletRpcServerCommandsDefinitions.h @@ -48,7 +48,7 @@ using CryptoNote::ISerializer; }; }; - struct trnsfer_destination + struct transfer_destination { uint64_t amount; std::string address; @@ -63,7 +63,7 @@ using CryptoNote::ISerializer; { struct request { - std::list destinations; + std::list destinations; uint64_t fee; uint64_t mixin; uint64_t unlock_time; diff --git a/src/Wallet/WalletSerialization.cpp b/src/Wallet/WalletSerialization.cpp index e03dba4b..1b923d38 100755 --- a/src/Wallet/WalletSerialization.cpp +++ b/src/Wallet/WalletSerialization.cpp @@ -99,14 +99,18 @@ struct WalletTransactionDto { //DO NOT CHANGE IT struct WalletTransferDto { - WalletTransferDto() {} - WalletTransferDto(const CryptoNote::WalletTransfer& tr) { + WalletTransferDto(uint32_t version) : version(version) {} + WalletTransferDto(const CryptoNote::WalletTransfer& tr, uint32_t version) : WalletTransferDto(version) { address = tr.address; amount = tr.amount; + type = static_cast(tr.type); } std::string address; uint64_t amount; + uint8_t type; + + uint32_t version; }; void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) { @@ -156,8 +160,11 @@ void serialize(WalletTransactionDto& value, CryptoNote::ISerializer& serializer) void serialize(WalletTransferDto& value, CryptoNote::ISerializer& serializer) { serializer(value.address, "address"); serializer(value.amount, "amount"); -} + if (value.version > 2) { + serializer(value.type, "type"); + } +} template std::string serialize(Object& obj, const std::string& name) { @@ -266,7 +273,7 @@ CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) { namespace CryptoNote { -const uint32_t WalletSerializer::SERIALIZATION_VERSION = 2; +const uint32_t WalletSerializer::SERIALIZATION_VERSION = 3; void CryptoContext::incIv() { uint64_t * i = reinterpret_cast(&iv.data[0]); @@ -503,7 +510,7 @@ void WalletSerializer::saveTransfers(Common::IOutputStream& destination, CryptoC for (const auto& kv: m_transfers) { uint64_t txId = kv.first; - WalletTransferDto tr(kv.second); + WalletTransferDto tr(kv.second, SERIALIZATION_VERSION); serializeEncrypted(txId, "transaction_id", cryptoContext, destination); cryptoContext.incIv(); @@ -519,18 +526,18 @@ void WalletSerializer::load(const std::string& password, Common::IInputStream& s uint32_t version = loadVersion(source); - if (version == SERIALIZATION_VERSION) { - loadCurrentVersion(source, password); - } else if (version == 1) { - loadWalletV1(source, password); - } else { + if (version > SERIALIZATION_VERSION) { throw std::system_error(make_error_code(error::WRONG_VERSION)); + } else if (version != 1) { + loadWallet(source, password, version); + } else { + loadWalletV1(source, password); } s.endObject(); } -void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const std::string& password) { +void WalletSerializer::loadWallet(Common::IInputStream& source, const std::string& password, uint32_t version) { CryptoNote::CryptoContext cryptoContext; bool details = false; @@ -549,7 +556,7 @@ void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const st if (details) { loadTransactions(source, cryptoContext); - loadTransfers(source, cryptoContext); + loadTransfers(source, cryptoContext, version); } if (cache) { @@ -869,7 +876,7 @@ void WalletSerializer::loadTransactions(Common::IInputStream& source, CryptoCont } } -void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext) { +void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version) { uint64_t count = 0; deserializeEncrypted(count, "transfers_count", cryptoContext, source); cryptoContext.incIv(); @@ -881,7 +888,7 @@ void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext deserializeEncrypted(txId, "transaction_id", cryptoContext, source); cryptoContext.incIv(); - WalletTransferDto dto; + WalletTransferDto dto(version); deserializeEncrypted(dto, "transfer", cryptoContext, source); cryptoContext.incIv(); @@ -889,6 +896,12 @@ void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext tr.address = dto.address; tr.amount = dto.amount; + if (version > 2) { + tr.type = static_cast(dto.type); + } else { + tr.type = WalletTransferType::USUAL; + } + m_transfers.push_back(std::make_pair(txId, tr)); } } diff --git a/src/Wallet/WalletSerialization.h b/src/Wallet/WalletSerialization.h index 999b7bab..655d977e 100755 --- a/src/Wallet/WalletSerialization.h +++ b/src/Wallet/WalletSerialization.h @@ -59,7 +59,7 @@ public: private: static const uint32_t SERIALIZATION_VERSION; - void loadCurrentVersion(Common::IInputStream& source, const std::string& password); + void loadWallet(Common::IInputStream& source, const std::string& password, uint32_t version); void loadWalletV1(Common::IInputStream& source, const std::string& password); CryptoContext generateCryptoContext(const std::string& password); @@ -95,7 +95,7 @@ private: void loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext); void loadChange(Common::IInputStream& source, CryptoContext& cryptoContext); void loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext); - void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version); void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer); void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer); diff --git a/src/WalletLegacy/WalletLegacy.h b/src/WalletLegacy/WalletLegacy.h index 2ad7117d..169e30db 100755 --- a/src/WalletLegacy/WalletLegacy.h +++ b/src/WalletLegacy/WalletLegacy.h @@ -53,37 +53,37 @@ public: WalletLegacy(const CryptoNote::Currency& currency, INode& node); virtual ~WalletLegacy(); - virtual void addObserver(IWalletLegacyObserver* observer); - virtual void removeObserver(IWalletLegacyObserver* observer); + virtual void addObserver(IWalletLegacyObserver* observer) override; + virtual void removeObserver(IWalletLegacyObserver* observer) override; - virtual void initAndGenerate(const std::string& password); - virtual void initAndLoad(std::istream& source, const std::string& password); - virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password); - virtual void shutdown(); - virtual void reset(); + virtual void initAndGenerate(const std::string& password) override; + virtual void initAndLoad(std::istream& source, const std::string& password) override; + virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password) override; + virtual void shutdown() override; + virtual void reset() override; - virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true); + virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) override; - virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword); + virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) override; - virtual std::string getAddress(); + virtual std::string getAddress() override; - virtual uint64_t actualBalance(); - virtual uint64_t pendingBalance(); + virtual uint64_t actualBalance() override; + virtual uint64_t pendingBalance() override; - virtual size_t getTransactionCount(); - virtual size_t getTransferCount(); + virtual size_t getTransactionCount() override; + virtual size_t getTransferCount() override; - virtual TransactionId findTransactionByTransferId(TransferId transferId); + virtual TransactionId findTransactionByTransferId(TransferId transferId) override; - virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction); - virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer); + virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) override; + virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) override; - virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); - virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); - virtual std::error_code cancelTransaction(size_t transactionId); + virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) override; + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) override; + virtual std::error_code cancelTransaction(size_t transactionId) override; - virtual void getAccountKeys(AccountKeys& keys); + virtual void getAccountKeys(AccountKeys& keys) override; private: diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c index 76bcaeb7..f398bfa8 100644 --- a/src/crypto/oaes_lib.c +++ b/src/crypto/oaes_lib.c @@ -34,7 +34,11 @@ static const char _NR[] = { #include #include #include +#ifdef __APPLE__ +#include +#else #include +#endif #include #include #include diff --git a/src/crypto/slow-hash.inl b/src/crypto/slow-hash.inl index 57c86e06..e0d15e73 100644 --- a/src/crypto/slow-hash.inl +++ b/src/crypto/slow-hash.inl @@ -24,7 +24,7 @@ cn_slow_hash_noaesni (void *restrict context, const void *restrict data, size_t length, void *restrict hash) { #define ctx ((struct cn_ctx *) context) - uint8_t ExpandedKey[256]; + ALIGNED_DECL(uint8_t ExpandedKey[256], 16); size_t i; __m128i *longoutput, *expkey, *xmminput, b_x; ALIGNED_DECL(uint64_t a[2], 16); diff --git a/src/version.h.in b/src/version.h.in index 18cb97e7..0bc423bc 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.8.1" -#define PROJECT_VERSION_BUILD_NO "614" +#define PROJECT_VERSION "1.0.8.2" +#define PROJECT_VERSION_BUILD_NO "625" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/tests/NodeRpcProxyTests/NodeRpcProxyTests.cpp b/tests/NodeRpcProxyTests/NodeRpcProxyTests.cpp index 775367dd..1865e9e6 100755 --- a/tests/NodeRpcProxyTests/NodeRpcProxyTests.cpp +++ b/tests/NodeRpcProxyTests/NodeRpcProxyTests.cpp @@ -39,7 +39,7 @@ public: virtual ~NodeObserver() { } - virtual void peerCountUpdated(size_t count) { + virtual void peerCountUpdated(size_t count) override { logger(INFO) << '[' << m_name << "] peerCountUpdated " << count << " = " << m_nodeProxy.getPeerCount(); } diff --git a/tests/UnitTests/ICoreStub.h b/tests/UnitTests/ICoreStub.h index bf07b436..e9295f16 100644 --- a/tests/UnitTests/ICoreStub.h +++ b/tests/UnitTests/ICoreStub.h @@ -31,16 +31,16 @@ public: ICoreStub(); ICoreStub(const CryptoNote::Block& genesisBlock); - virtual bool addObserver(CryptoNote::ICoreObserver* observer); - virtual bool removeObserver(CryptoNote::ICoreObserver* observer); - virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id); + virtual bool addObserver(CryptoNote::ICoreObserver* observer) override; + virtual bool removeObserver(CryptoNote::ICoreObserver* observer) override; + virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) override; virtual std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, uint32_t& totalBlockCount, uint32_t& startBlockIndex) override; virtual bool get_random_outs_for_amounts(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, - CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); - virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs); - virtual CryptoNote::i_cryptonote_protocol* get_protocol(); - virtual bool handle_incoming_tx(CryptoNote::BinaryArray const& tx_blob, CryptoNote::tx_verification_context& tvc, bool keeped_by_block); + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) override; + virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) override; + virtual CryptoNote::i_cryptonote_protocol* get_protocol() override; + virtual bool handle_incoming_tx(CryptoNote::BinaryArray const& tx_blob, CryptoNote::tx_verification_context& tvc, bool keeped_by_block) override; virtual std::vector getPoolTransactions() override; virtual bool getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, std::vector& addedTxs, std::vector& deletedTxsIds) override; diff --git a/tests/UnitTests/INodeStubs.h b/tests/UnitTests/INodeStubs.h index 6f2c194e..79c91270 100644 --- a/tests/UnitTests/INodeStubs.h +++ b/tests/UnitTests/INodeStubs.h @@ -34,10 +34,10 @@ public: virtual bool addObserver(CryptoNote::INodeObserver* observer) override; virtual bool removeObserver(CryptoNote::INodeObserver* observer) override; - virtual void init(const CryptoNote::INode::Callback& callback) {callback(std::error_code());}; - virtual bool shutdown() { return true; }; + virtual void init(const CryptoNote::INode::Callback& callback) override {callback(std::error_code());}; + virtual bool shutdown() override { return true; }; - virtual size_t getPeerCount() const { return 0; }; + virtual size_t getPeerCount() const override { return 0; }; virtual uint32_t getLastLocalBlockHeight() const override { return 0; }; virtual uint32_t getLastKnownBlockHeight() const override { return 0; }; virtual uint32_t getLocalBlockCount() const override { return 0; }; @@ -56,13 +56,13 @@ public: virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override { callback(std::error_code()); }; - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { callback(std::error_code()); }; - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { callback(std::error_code()); }; - virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { callback(std::error_code()); }; - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { callback(std::error_code()); }; - virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { callback(std::error_code()); }; - virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { callback(std::error_code()); }; - virtual void isSynchronized(bool& syncStatus, const Callback& callback) { callback(std::error_code()); }; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override { callback(std::error_code()); }; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override { callback(std::error_code()); }; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override { callback(std::error_code()); }; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override { callback(std::error_code()); }; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) override { callback(std::error_code()); }; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) override { callback(std::error_code()); }; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override { callback(std::error_code()); }; virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, CryptoNote::MultisignatureOutput& out, const Callback& callback) override { callback(std::error_code()); } void updateObservers(); @@ -79,16 +79,16 @@ public: void setGetNewBlocksLimit(size_t maxBlocks) { m_getMaxBlocks = maxBlocks; } - virtual uint32_t getLastLocalBlockHeight() const { return static_cast(m_blockchainGenerator.getBlockchain().size() - 1); } - virtual uint32_t getLastKnownBlockHeight() const { return static_cast(m_blockchainGenerator.getBlockchain().size() - 1); } + virtual uint32_t getLastLocalBlockHeight() const override { return static_cast(m_blockchainGenerator.getBlockchain().size() - 1); } + virtual uint32_t getLastKnownBlockHeight() const override { return static_cast(m_blockchainGenerator.getBlockchain().size() - 1); } virtual uint32_t getLocalBlockCount() const override { return static_cast(m_blockchainGenerator.getBlockchain().size()); } virtual uint32_t getKnownBlockCount() const override { return static_cast(m_blockchainGenerator.getBlockchain().size()); } - virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback); + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; - virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); - virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) override; virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual, diff --git a/tests/UnitTests/TestBcS.cpp b/tests/UnitTests/TestBcS.cpp index a5319a5e..33ff2f0f 100755 --- a/tests/UnitTests/TestBcS.cpp +++ b/tests/UnitTests/TestBcS.cpp @@ -1049,7 +1049,7 @@ TEST_F(BcSTest, checkBlocksRequesting) { }); - uint32_t blocksExpected = 20; + size_t blocksExpected = 20; generator.generateEmptyBlocks(blocksExpected - 1); //-1 for genesis m_node.setGetNewBlocksLimit(3); @@ -1085,7 +1085,7 @@ TEST_F(BcSTest, checkConsumerHeightReceived) { uint32_t firstlySnchronizedHeight = 20; - generator.generateEmptyBlocks(firstlySnchronizedHeight - 1);//-1 for genesis + generator.generateEmptyBlocks(static_cast(firstlySnchronizedHeight - 1));//-1 for genesis m_node.setGetNewBlocksLimit(50); c.onNewBlocksFunctor = [&](const CompleteBlock*, uint32_t startHeight, size_t) -> bool { diff --git a/tests/UnitTests/TestBlockchainExplorer.cpp b/tests/UnitTests/TestBlockchainExplorer.cpp index f840c1e1..1b099e75 100755 --- a/tests/UnitTests/TestBlockchainExplorer.cpp +++ b/tests/UnitTests/TestBlockchainExplorer.cpp @@ -1103,7 +1103,7 @@ TEST_F(BlockchainExplorerTests, getBlocksByTimestampNotInited) { } TEST_F(BlockchainExplorerTests, generatedTransactions) { - const uint32_t NUMBER_OF_BLOCKS = 10; + const size_t NUMBER_OF_BLOCKS = 10; const size_t POOL_TX_NUMBER = 10; std::vector blockHeights; for (uint32_t i = 0; i < NUMBER_OF_BLOCKS + 3; ++i) { diff --git a/tests/UnitTests/TestBlockchainGenerator.cpp b/tests/UnitTests/TestBlockchainGenerator.cpp index becccc70..523d030c 100644 --- a/tests/UnitTests/TestBlockchainGenerator.cpp +++ b/tests/UnitTests/TestBlockchainGenerator.cpp @@ -134,11 +134,11 @@ void TestBlockchainGenerator::addMiningBlock() { m_generatedTransactionsIndex.add(block); } -void TestBlockchainGenerator::generateEmptyBlocks(uint32_t count) +void TestBlockchainGenerator::generateEmptyBlocks(size_t count) { std::unique_lock lock(m_mutex); - for (uint32_t i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) { CryptoNote::Block& prev_block = m_blockchain.back(); CryptoNote::Block block; @@ -359,11 +359,11 @@ void TestBlockchainGenerator::addTx(const CryptoNote::Transaction& tx) { const auto& out = tx.outputs[outIndex]; if (out.target.type() == typeid(KeyOutput)) { auto& keyOutsContainer = keyOutsIndex[out.amount]; - globalIndexes.push_back(keyOutsContainer.size()); + globalIndexes.push_back(static_cast(keyOutsContainer.size())); keyOutsContainer.push_back({ txHash, outIndex }); } else if (out.target.type() == typeid(MultisignatureOutput)) { auto& msigOutsContainer = multisignatureOutsIndex[out.amount]; - globalIndexes.push_back(msigOutsContainer.size()); + globalIndexes.push_back(static_cast(msigOutsContainer.size())); msigOutsContainer.push_back({ txHash, outIndex }); } } diff --git a/tests/UnitTests/TestBlockchainGenerator.h b/tests/UnitTests/TestBlockchainGenerator.h index 4b829251..935aa21a 100644 --- a/tests/UnitTests/TestBlockchainGenerator.h +++ b/tests/UnitTests/TestBlockchainGenerator.h @@ -36,7 +36,7 @@ public: //TODO: get rid of this method std::vector& getBlockchain(); std::vector getBlockchainCopy(); - void generateEmptyBlocks(uint32_t count); + void generateEmptyBlocks(size_t count); bool getBlockRewardForAddress(const CryptoNote::AccountPublicAddress& address); bool generateTransactionsInOneBlock(const CryptoNote::AccountPublicAddress& address, size_t n); bool getSingleOutputTransaction(const CryptoNote::AccountPublicAddress& address, uint64_t amount); diff --git a/tests/UnitTests/TestInprocessNode.cpp b/tests/UnitTests/TestInprocessNode.cpp index a163fa1e..3c75b0e7 100644 --- a/tests/UnitTests/TestInprocessNode.cpp +++ b/tests/UnitTests/TestInprocessNode.cpp @@ -267,7 +267,7 @@ TEST_F(InProcessNodeTests, getBlocksByHeightEmpty) { } TEST_F(InProcessNodeTests, getBlocksByHeightMany) { - const uint32_t NUMBER_OF_BLOCKS = 10; + const size_t NUMBER_OF_BLOCKS = 10; std::vector blockHeights; std::vector> actualBlocks; @@ -311,7 +311,7 @@ TEST_F(InProcessNodeTests, getBlocksByHeightMany) { } TEST_F(InProcessNodeTests, getBlocksByHeightFail) { - const uint32_t NUMBER_OF_BLOCKS = 10; + const size_t NUMBER_OF_BLOCKS = 10; std::vector blockHeights; std::vector> actualBlocks; @@ -366,7 +366,7 @@ TEST_F(InProcessNodeTests, getBlocksByHashEmpty) { } TEST_F(InProcessNodeTests, getBlocksByHashMany) { - const uint32_t NUMBER_OF_BLOCKS = 10; + const size_t NUMBER_OF_BLOCKS = 10; std::vector blockHashes; std::vector actualBlocks; @@ -407,7 +407,7 @@ TEST_F(InProcessNodeTests, getBlocksByHashMany) { } TEST_F(InProcessNodeTests, getBlocksByHashFail) { - const uint32_t NUMBER_OF_BLOCKS = 10; + const size_t NUMBER_OF_BLOCKS = 10; std::vector blockHashes; std::vector actualBlocks; diff --git a/tests/UnitTests/TestTransfersConsumer.cpp b/tests/UnitTests/TestTransfersConsumer.cpp index 6f1c6699..042b7470 100755 --- a/tests/UnitTests/TestTransfersConsumer.cpp +++ b/tests/UnitTests/TestTransfersConsumer.cpp @@ -437,7 +437,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesError) block.transactions.push_back(tx); consumer.addSubscription(subscription); - ASSERT_FALSE(consumer.onNewBlocks(&block, subscription.syncStart.height, 1)); + ASSERT_FALSE(consumer.onNewBlocks(&block, static_cast(subscription.syncStart.height), 1)); } TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) { @@ -457,7 +457,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) { block.block->timestamp = subscription.syncStart.timestamp; block.transactions.push_back(tx); - ASSERT_TRUE(m_consumer.onNewBlocks(&block, subscription.syncStart.height, 1)); + ASSERT_TRUE(m_consumer.onNewBlocks(&block, static_cast(subscription.syncStart.height), 1)); ASSERT_EQ(900, container.balance(ITransfersContainer::IncludeAllLocked)); std::unique_ptr blocks(new CompleteBlock[subscription.transactionSpendableAge]); @@ -468,7 +468,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) { addTestKeyOutput(*tr, 100, i + 1, generateAccountKeys()); } - ASSERT_TRUE(m_consumer.onNewBlocks(blocks.get(), subscription.syncStart.height + 1, subscription.transactionSpendableAge)); + ASSERT_TRUE(m_consumer.onNewBlocks(blocks.get(), static_cast(subscription.syncStart.height + 1), static_cast(subscription.transactionSpendableAge))); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); ASSERT_EQ(900, container.balance(ITransfersContainer::IncludeAllUnlocked)); } @@ -792,7 +792,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_manyBlocks) { } } - ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size())); + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, static_cast(blocks.size()))); ASSERT_EQ(expectedTransactions, container.transactionsCount()); ASSERT_EQ(expectedAmount, container.balance(ITransfersContainer::IncludeAll)); @@ -1031,7 +1031,7 @@ TEST_F(TransfersConsumerPerformanceTest, DISABLED_memory) { { AutoPrintTimer t; - ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size())); + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, static_cast(blocks.size()))); } blocks.clear(); @@ -1056,7 +1056,7 @@ TEST_F(TransfersConsumerPerformanceTest, DISABLED_performanceTest) { std::cout << "Calling onNewBlocks" << std::endl; - ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size())); + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, static_cast(blocks.size()))); auto end = std::chrono::steady_clock::now(); std::chrono::duration dur = end - start; diff --git a/tests/UnitTests/TestWallet.cpp b/tests/UnitTests/TestWallet.cpp index 31c86513..fedc5b81 100755 --- a/tests/UnitTests/TestWallet.cpp +++ b/tests/UnitTests/TestWallet.cpp @@ -54,9 +54,13 @@ namespace CryptoNote { case WalletTransactionState::SUCCEEDED: o << "SUCCEEDED"; break; + case WalletTransactionState::CREATED: + o << "CREATED"; + break; } return o; } + std::ostream& operator<<(std::ostream& o, const WalletTransaction& tx) { o << "WalletTransaction{state=" << tx.state << ", timestamp=" << tx.timestamp << ", blockHeight=" << tx.blockHeight << ", hash=" << tx.hash @@ -123,6 +127,10 @@ namespace CryptoNote { return false; } + if (lhs.type != rhs.type) { + return false; + } + return true; } @@ -185,8 +193,10 @@ protected: size_t sendMoneyToRandomAddressFrom(const std::string& address, uint64_t amount, uint64_t fee); size_t sendMoneyToRandomAddressFrom(const std::string& address); - size_t sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0); - size_t sendMoney(const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0); + size_t sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0); + size_t sendMoney(const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0); + size_t sendMoneyWithDonation(const std::string& to, uint64_t amount, uint64_t fee, + const std::string& donationAddress, uint64_t donationAmount, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0); void fillWalletWithDetailsCache(); @@ -401,11 +411,11 @@ void WalletApi::generateAddressesWithPendingMoney(size_t count) { } size_t WalletApi::sendMoneyToRandomAddressFrom(const std::string& address, uint64_t amount, uint64_t fee) { - CryptoNote::WalletTransfer transfer; - transfer.address = RANDOM_ADDRESS; - transfer.amount = amount; + CryptoNote::WalletOrder order; + order.address = RANDOM_ADDRESS; + order.amount = amount; - return alice.transfer(address, transfer, fee, 0); + return alice.transfer(address, order, fee, 0); } size_t WalletApi::sendMoneyToRandomAddressFrom(const std::string& address) { @@ -429,18 +439,33 @@ void WalletApi::fillWalletWithDetailsCache() { } } -size_t WalletApi::sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { - CryptoNote::WalletTransfer transfer; - transfer.address = to; - transfer.amount = amount; +size_t WalletApi::sendMoney(CryptoNote::WalletGreen& wallet, const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { + CryptoNote::WalletOrder order; + order.address = to; + order.amount = amount; - return wallet.transfer(transfer, fee, mixIn, extra, unlockTimestamp); + return wallet.transfer(order, fee, mixIn, extra, unlockTimestamp); } -size_t WalletApi::sendMoney(const std::string& to, int64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { +size_t WalletApi::sendMoney(const std::string& to, uint64_t amount, uint64_t fee, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { return sendMoney(alice, to, amount, fee, mixIn, extra, unlockTimestamp); } +size_t WalletApi::sendMoneyWithDonation(const std::string& to, uint64_t amount, uint64_t fee, + const std::string& donationAddress, uint64_t donationAmount, uint64_t mixIn, const std::string& extra, uint64_t unlockTimestamp) { + + TransactionParameters params; + params.destinations.push_back({to, amount}); + params.fee = fee; + params.donation.address = donationAddress; + params.donation.threshold = donationAmount; + params.mixIn = mixIn; + params.extra = extra; + params.unlockTimestamp = unlockTimestamp; + + return alice.transfer(params); +} + void WalletApi::wait(uint64_t milliseconds) { System::Timer timer(dispatcher); timer.sleep(std::chrono::nanoseconds(milliseconds * 1000000)); @@ -510,7 +535,7 @@ TEST_F(WalletApi, pendingBalanceUpdatedAfterTransactionGotInBlock) { auto prevPending = alice.getPendingBalance(); - generator.generateEmptyBlocks(TRANSACTION_SOFTLOCK_TIME); + generator.generateEmptyBlocks(static_cast(TRANSACTION_SOFTLOCK_TIME)); node.updateObservers(); waitPendingBalanceUpdated(prevPending); @@ -524,7 +549,7 @@ TEST_F(WalletApi, moneyLockedIfTransactionIsSoftLocked) { bob.initialize("pass2"); sendMoney(bob.createAddress(), SENT, FEE); - generator.generateEmptyBlocks(TRANSACTION_SOFTLOCK_TIME - 1); + generator.generateEmptyBlocks(static_cast(TRANSACTION_SOFTLOCK_TIME - 1)); node.updateObservers(); waitPendingBalanceUpdated(bob, 0); @@ -598,7 +623,11 @@ TEST_F(WalletApi, transferFromTwoAddresses) { } TEST_F(WalletApi, transferTooBigTransaction) { - CryptoNote::Currency cur = CryptoNote::CurrencyBuilder(logger).blockGrantedFullRewardZone(5).minerTxBlobReservedSize(2).currency(); + const size_t testBlockGrantedFullRewardZone = 2000; + const size_t outputSize = 32 + 1; + const size_t bigTxOutputCount = 2 * testBlockGrantedFullRewardZone / outputSize; + + CryptoNote::Currency cur = CryptoNote::CurrencyBuilder(logger).blockGrantedFullRewardZone(testBlockGrantedFullRewardZone).currency(); TestBlockchainGenerator gen(cur); INodeTrivialRefreshStub n(gen); @@ -613,11 +642,12 @@ TEST_F(WalletApi, transferTooBigTransaction) { n.updateObservers(); waitActualBalanceUpdated(wallet, prev); - CryptoNote::WalletTransfer transfer; - transfer.address = RANDOM_ADDRESS; - transfer.amount = SENT; + std::vector destinations; + for (size_t i = 0; i < bigTxOutputCount; ++i) { + destinations.push_back({ RANDOM_ADDRESS, 1 }); + } - ASSERT_ANY_THROW(wallet.transfer(transfer, FEE)); + ASSERT_ANY_THROW(wallet.transfer(destinations, FEE)); } TEST_F(WalletApi, balanceAfterTransfer) { @@ -1272,26 +1302,91 @@ TEST_F(WalletApi, incomingTxTransfer) { wait(100); } +TEST_F(WalletApi, walletSendsTransactionUpdatedEventAfterAddingTransfer) { + generateAndUnlockMoney(); + + CryptoNote::WalletGreen bob(dispatcher, currency, node, TRANSACTION_SOFTLOCK_TIME); + bob.initialize("pass2"); + bob.createAddress(); + bob.createAddress(); + bob.createAddress(); + + std::vector orders; + orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(0), SENT }); + orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(1), SENT }); + orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(2), SENT }); + alice.transfer(orders, FEE, 0, "", 0); + + node.updateObservers(); + ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_CREATED, std::chrono::seconds(5))); + ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_UPDATED, std::chrono::seconds(5))); + ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_UPDATED, std::chrono::seconds(5))); + + bob.shutdown(); + wait(100); +} + +TEST_F(WalletApi, walletCreatesTransferForEachTransactionFunding) { + generateAndUnlockMoney(); + + CryptoNote::WalletGreen bob(dispatcher, currency, node, TRANSACTION_SOFTLOCK_TIME); + bob.initialize("pass2"); + bob.createAddress(); + bob.createAddress(); + + std::vector orders; + orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(0), SENT }); + orders.emplace_back(CryptoNote::WalletOrder{ bob.getAddress(1), 2 * SENT }); + alice.transfer(orders, FEE, 0, "", 0); + + node.updateObservers(); + ASSERT_TRUE(waitForWalletEvent(bob, CryptoNote::WalletEventType::TRANSACTION_UPDATED, std::chrono::seconds(5))); + + ASSERT_EQ(2, bob.getTransactionTransferCount(0)); + auto tr1 = bob.getTransactionTransfer(0, 0); + auto tr2 = bob.getTransactionTransfer(0, 1); + ASSERT_TRUE(tr1.address == bob.getAddress(0) || tr1.address == bob.getAddress(1)); + ASSERT_TRUE(tr2.address == bob.getAddress(0) || tr2.address == bob.getAddress(1)); + ASSERT_NE(tr1.address, tr2.address); + + bob.shutdown(); + wait(100); +} + +size_t getTransactionUsualTransferCount(WalletGreen& wallet, size_t transactionIndex) { + size_t transfersCount = wallet.getTransactionTransferCount(transactionIndex); + size_t usualTransfersCount = 0; + for (size_t i = 0; i < transfersCount; ++i) { + if (wallet.getTransactionTransfer(transactionIndex, i).type == WalletTransferType::USUAL) { + ++usualTransfersCount; + } + } + + return usualTransfersCount; +} + TEST_F(WalletApi, hybridTxTransfer) { generateAndUnlockMoney(); alice.createAddress(); alice.createAddress(); - CryptoNote::WalletTransfer tr1 { alice.getAddress(1), static_cast(SENT) }; - CryptoNote::WalletTransfer tr2 { alice.getAddress(2), static_cast(2 * SENT) }; + CryptoNote::WalletOrder tr1 { alice.getAddress(1), SENT }; + CryptoNote::WalletOrder tr2 { alice.getAddress(2), 2 * SENT }; alice.transfer({tr1, tr2}, FEE); node.updateObservers(); dispatcher.yield(); - ASSERT_EQ(2, alice.getTransactionTransferCount(1)); + ASSERT_EQ(2, getTransactionUsualTransferCount(alice, 1)); EXPECT_EQ(tr1.address, alice.getTransactionTransfer(1, 0).address); - EXPECT_EQ(-tr1.amount, alice.getTransactionTransfer(1, 0).amount); + EXPECT_EQ(-static_cast(tr1.amount), alice.getTransactionTransfer(1, 0).amount); + EXPECT_EQ(WalletTransferType::USUAL, alice.getTransactionTransfer(1, 0).type); EXPECT_EQ(tr2.address, alice.getTransactionTransfer(1, 1).address); - EXPECT_EQ(-tr2.amount, alice.getTransactionTransfer(1, 1).amount); + EXPECT_EQ(-static_cast(tr2.amount), alice.getTransactionTransfer(1, 1).amount); + EXPECT_EQ(WalletTransferType::USUAL, alice.getTransactionTransfer(1, 1).type); } TEST_F(WalletApi, doubleSpendJustSentOut) { @@ -1384,7 +1479,7 @@ TEST_F(WalletApi, DISABLED_loadTest) { steady_clock::time_point transferStart = steady_clock::now(); for (size_t i = 0; i < TRANSACTIONS_COUNT; ++i) { - CryptoNote::WalletTransfer tr; + CryptoNote::WalletOrder tr; tr.amount = SENT; tr.address = RANDOM_ADDRESS; wallet.transfer(tr, FEE); @@ -1839,10 +1934,10 @@ TEST_F(WalletApi, fusionManagerIsFusionTransactionSpent) { ASSERT_NE(WALLET_INVALID_TRANSACTION_ID, id); unlockMoney(); - CryptoNote::WalletTransfer transfer; - transfer.address = wallet.getAddress(0); - transfer.amount = alice.getActualBalance() - currency.minimumFee(); - alice.transfer(aliceAddress, transfer, currency.minimumFee(), 0); + CryptoNote::WalletOrder order; + order.address = wallet.getAddress(0); + order.amount = alice.getActualBalance() - currency.minimumFee(); + alice.transfer(aliceAddress, order, currency.minimumFee(), 0); auto pending = wallet.getPendingBalance(); node.updateObservers(); @@ -1851,3 +1946,123 @@ TEST_F(WalletApi, fusionManagerIsFusionTransactionSpent) { ASSERT_TRUE(alice.isFusionTransaction(id)); } +size_t findDonationTransferId(const WalletGreen& wallet, size_t transactionId) { + for (size_t i = 0; i < wallet.getTransactionTransferCount(transactionId); ++i) { + if (wallet.getTransactionTransfer(transactionId, i).type == WalletTransferType::DONATION) { + return i; + } + } + + return WALLET_INVALID_TRANSFER_ID; +} + +TEST_F(WalletApi, donationTransferPresents) { + const uint64_t DONATION_THRESHOLD = 1000000; + + generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD); + unlockMoney(); + + auto transactionId = sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD); + + ASSERT_NE(WALLET_INVALID_TRANSACTION_ID, transactionId); + + auto donationTransferId = findDonationTransferId(alice, transactionId); + ASSERT_NE(WALLET_INVALID_TRANSFER_ID, donationTransferId); + + auto donationTransfer = alice.getTransactionTransfer(transactionId, donationTransferId); + ASSERT_EQ(WalletTransferType::DONATION, donationTransfer.type); + ASSERT_EQ(-static_cast(DONATION_THRESHOLD), donationTransfer.amount); + ASSERT_EQ(RANDOM_ADDRESS, donationTransfer.address); +} + +TEST_F(WalletApi, donationDidntHappenIfNotEnoughMoney) { + const uint64_t DONATION_THRESHOLD = 1000000; + + generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE); + unlockMoney(); + + auto transactionId = sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD); + ASSERT_NE(WALLET_INVALID_TRANSACTION_ID, transactionId); + ASSERT_EQ(WALLET_INVALID_TRANSFER_ID, findDonationTransferId(alice, transactionId)); +} + +TEST_F(WalletApi, donationThrowsIfAddressEmpty) { + const uint64_t DONATION_THRESHOLD = 1000000; + + generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD); + unlockMoney(); + + TransactionParameters params; + params.destinations.push_back({RANDOM_ADDRESS, SENT}); + params.fee = FEE; + params.donation.threshold = DONATION_THRESHOLD; + + ASSERT_ANY_THROW(alice.transfer(params)); +} + +TEST_F(WalletApi, donationThrowsIfThresholdZero) { + const uint64_t DONATION_THRESHOLD = 1000000; + + generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD); + unlockMoney(); + + TransactionParameters params; + params.destinations.push_back({RANDOM_ADDRESS, SENT}); + params.fee = FEE; + params.donation.address = RANDOM_ADDRESS; + params.donation.threshold = 0; + + ASSERT_ANY_THROW(alice.transfer(params)); +} + +TEST_F(WalletApi, donationTransactionHaveCorrectFee) { + CatchTransactionNodeStub catchNode(generator); + CryptoNote::WalletGreen wallet(dispatcher, currency, catchNode); + wallet.initialize("pass"); + wallet.createAddress(); + + const uint64_t DONATION_THRESHOLD = 1000000; + + generator.getSingleOutputTransaction(parseAddress(wallet.getAddress(0)), SENT + FEE + DONATION_THRESHOLD); + unlockMoney(wallet, catchNode); + + TransactionParameters params; + params.destinations.push_back({RANDOM_ADDRESS, SENT}); + params.fee = FEE; + params.donation.address = RANDOM_ADDRESS; + params.donation.threshold = DONATION_THRESHOLD; + + wallet.transfer(params); + + ASSERT_TRUE(catchNode.caught); + ASSERT_EQ(FEE, getInputAmount(catchNode.transaction) - getOutputAmount(catchNode.transaction)); + + wallet.shutdown(); +} + +TEST_F(WalletApi, donationSerialization) { + const uint64_t DONATION_THRESHOLD = 1000000; + + generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE + DONATION_THRESHOLD); + unlockMoney(); + + sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD); + + std::stringstream data; + alice.save(data, true, true); + + WalletGreen bob(dispatcher, currency, node, TRANSACTION_SOFTLOCK_TIME); + bob.load(data, "pass"); + + compareWalletsTransactionTransfers(alice, bob); + bob.shutdown(); +} + +TEST_F(WalletApi, transferThrowsIfDonationThresholdTooBig) { + const uint64_t DONATION_THRESHOLD = static_cast(std::numeric_limits::max()) + 1; + + generator.getSingleOutputTransaction(parseAddress(aliceAddress), SENT + FEE); + unlockMoney(); + + ASSERT_ANY_THROW(sendMoneyWithDonation(RANDOM_ADDRESS, SENT, FEE, RANDOM_ADDRESS, DONATION_THRESHOLD)); +} diff --git a/tests/UnitTests/TestWalletLegacy.cpp b/tests/UnitTests/TestWalletLegacy.cpp index bdc9d56a..b5c04c0f 100644 --- a/tests/UnitTests/TestWalletLegacy.cpp +++ b/tests/UnitTests/TestWalletLegacy.cpp @@ -1392,7 +1392,7 @@ TEST_F(WalletLegacyApi, outcommingExternalTransactionTotalAmount) { wallet.initAndLoad(walletData, "pass"); WaitWalletSync(&walletObserver); - ASSERT_EQ(-(sent + fee), externalTransactionObserver.totalAmount); + ASSERT_EQ(-static_cast(sent + fee), externalTransactionObserver.totalAmount); wallet.shutdown(); } diff --git a/tests/UnitTests/TransactionPool.cpp b/tests/UnitTests/TransactionPool.cpp index fa89195e..d698357a 100755 --- a/tests/UnitTests/TransactionPool.cpp +++ b/tests/UnitTests/TransactionPool.cpp @@ -701,7 +701,8 @@ const size_t TEST_TX_COUNT_UP_TO_MEDIAN = 10; const size_t TEST_MAX_TX_COUNT_PER_BLOCK = TEST_TX_COUNT_UP_TO_MEDIAN * 125 / 100; const size_t TEST_TRANSACTION_SIZE = 2000; const size_t TEST_FUSION_TX_MAX_SIZE = TEST_FUSION_TX_COUNT_PER_BLOCK * TEST_TRANSACTION_SIZE; -const size_t TEST_MEDIAN_SIZE = TEST_TX_COUNT_UP_TO_MEDIAN * TEST_TRANSACTION_SIZE; +const size_t TEST_MINER_TX_BLOB_RESERVED_SIZE = 600; +const size_t TEST_MEDIAN_SIZE = TEST_TX_COUNT_UP_TO_MEDIAN * TEST_TRANSACTION_SIZE + TEST_MINER_TX_BLOB_RESERVED_SIZE; Transaction createTestFusionTransaction(const Currency& currency) { FusionTransactionBuilder builder(currency, 30 * currency.defaultDustThreshold());