// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "INodeStubs.h" #include "CryptoNoteCore/CryptoNoteFormatUtils.h" #include "CryptoNoteCore/CryptoNoteTools.h" #include "CryptoNoteCore/TransactionApi.h" #include "Wallet/WalletErrors.h" #include #include #include #include #include #include #include "crypto/crypto.h" #include "BlockchainExplorer/BlockchainExplorerDataBuilder.h" using namespace CryptoNote; using namespace Common; namespace { class ContextCounterHolder { public: ContextCounterHolder(WalletAsyncContextCounter& shutdowner) : m_shutdowner(shutdowner) {} ~ContextCounterHolder() { m_shutdowner.delAsyncContext(); } private: WalletAsyncContextCounter& m_shutdowner; }; } void INodeDummyStub::updateObservers() { observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, getLastKnownBlockHeight()); } bool INodeDummyStub::addObserver(INodeObserver* observer) { return observerManager.add(observer); } bool INodeDummyStub::removeObserver(INodeObserver* observer) { return observerManager.remove(observer); } void INodeTrivialRefreshStub::getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::unique_lock lock(m_walletLock); auto blockchain = m_blockchainGenerator.getBlockchainCopy(); lock.unlock(); std::thread task(std::bind(&INodeTrivialRefreshStub::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), std::ref(startHeight), std::move(blockchain), callback)); task.detach(); } void INodeTrivialRefreshStub::waitForAsyncContexts() { m_asyncCounter.waitAsyncContextsFinish(); } void INodeTrivialRefreshStub::doGetNewBlocks(std::vector knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, std::vector blockchain, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); std::vector::iterator start = blockchain.end(); for (const auto& id : knownBlockIds) { start = std::find_if(blockchain.begin(), blockchain.end(), [&id](Block& block) { return get_block_hash(block) == id; }); if (start != blockchain.end()) break; } if (start == blockchain.end()) { lock.unlock(); callback(std::error_code()); return; } m_lastHeight = static_cast(std::distance(blockchain.begin(), start)); startHeight = m_lastHeight; for (; m_lastHeight < blockchain.size(); ++m_lastHeight) { block_complete_entry e; e.block = asString(toBinaryArray(blockchain[m_lastHeight])); for (auto hash : blockchain[m_lastHeight].transactionHashes) { Transaction tx; if (!m_blockchainGenerator.getTransactionByHash(hash, tx)) continue; e.txs.push_back(asString(toBinaryArray(tx))); } newBlocks.push_back(e); if (newBlocks.size() >= m_getMaxBlocks) { break; } } m_lastHeight = startHeight + static_cast(newBlocks.size()); // m_lastHeight = startHeight + blockchain.size() - 1; lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::unique_lock lock(m_walletLock); calls_getTransactionOutsGlobalIndices.push_back(transactionHash); std::thread task(&INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback); task.detach(); } void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); bool success = m_blockchainGenerator.getTransactionGlobalIndexesByHash(transactionHash, outsGlobalIndices); lock.unlock(); if (consumerTests) { outsGlobalIndices.clear(); outsGlobalIndices.resize(20); getGlobalOutsFunctor(transactionHash, outsGlobalIndices); callback(std::error_code()); } else { if (success) { callback(std::error_code()); } else { callback(std::make_error_code(std::errc::invalid_argument)); } } } void INodeTrivialRefreshStub::relayTransaction(const Transaction& transaction, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task(&INodeTrivialRefreshStub::doRelayTransaction, this, transaction, callback); task.detach(); } void INodeTrivialRefreshStub::doRelayTransaction(const Transaction& transaction, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); if (m_nextTxError) { m_nextTxError = false; lock.unlock(); callback(make_error_code(error::INTERNAL_WALLET_ERROR)); return; } if (m_nextTxToPool) { m_nextTxToPool = false; m_blockchainGenerator.putTxToPool(transaction); lock.unlock(); callback(std::error_code()); return; } m_blockchainGenerator.addTxToBlockchain(transaction); lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task(&INodeTrivialRefreshStub::doGetRandomOutsByAmounts, this, amounts, outsCount, std::ref(result), callback); task.detach(); } void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); std::sort(amounts.begin(), amounts.end()); for (uint64_t amount: amounts) { COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount out; out.amount = amount; uint64_t count = std::min(outsCount, m_maxMixin); for (uint32_t i = 0; i < count; ++i) { Crypto::PublicKey key; Crypto::SecretKey sk; generate_keys(key, sk); COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry e; e.global_amount_index = i; e.out_key = key; out.outs.push_back(e); } result.push_back(std::move(out)); } lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) { auto resultHolder = std::make_shared>(); getNewBlocks(std::move(knownBlockIds), *resultHolder, startHeight, [resultHolder, callback, &startHeight, &newBlocks](std::error_code ec) { if (ec) { callback(ec); return; } for (const auto& item : *resultHolder) { BlockShortEntry entry; if (!fromBinaryArray(entry.block, asBinaryArray(item.block))) { callback(std::make_error_code(std::errc::invalid_argument)); return; } entry.hasBlock = true; entry.blockHash = get_block_hash(entry.block); for (const auto& txBlob: item.txs) { Transaction tx; if (!fromBinaryArray(tx, asBinaryArray(txBlob))) { callback(std::make_error_code(std::errc::invalid_argument)); return; } TransactionShortInfo tsi; tsi.txId = getObjectHash(tx); tsi.txPrefix = tx; entry.txsShortInfo.push_back(std::move(tsi)); } newBlocks.push_back(std::move(entry)); } callback(ec); }); } void INodeTrivialRefreshStub::startAlternativeChain(uint32_t height) { m_blockchainGenerator.cutBlockchain(height); m_lastHeight = height; } void INodeTrivialRefreshStub::setNextTransactionError() { m_nextTxError = true; } void INodeTrivialRefreshStub::setNextTransactionToPool() { m_nextTxToPool = true; } void INodeTrivialRefreshStub::cleanTransactionPool() { m_blockchainGenerator.clearTxPool(); } void INodeTrivialRefreshStub::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual, std::vector>& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( [this, known_pool_tx_ids, known_block_id, &is_bc_actual, &new_txs, &deleted_tx_ids, callback] () mutable { this->doGetPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback); } ); task.detach(); } void INodeTrivialRefreshStub::doGetPoolSymmetricDifference(std::vector&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual, std::vector>& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); std::vector txs; m_blockchainGenerator.getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, txs, deleted_tx_ids); lock.unlock(); std::error_code ec; try { for (const auto& tx: txs) { new_txs.push_back(createTransactionPrefix(tx)); } } catch (std::system_error& ex) { ec = ex.code(); } catch (std::exception&) { ec = make_error_code(std::errc::invalid_argument); } callback(ec); } void INodeTrivialRefreshStub::includeTransactionsFromPoolToBlock() { m_blockchainGenerator.putTxPoolToBlockchain(); } INodeTrivialRefreshStub::~INodeTrivialRefreshStub() { m_asyncCounter.waitAsyncContextsFinish(); } void INodeTrivialRefreshStub::setMaxMixinCount(uint64_t maxMixin) { m_maxMixin = maxMixin; } void INodeTrivialRefreshStub::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( std::bind( static_cast< void(INodeTrivialRefreshStub::*)( const std::vector&, std::vector>&, const Callback& ) >(&INodeTrivialRefreshStub::doGetBlocks), this, std::cref(blockHeights), std::ref(blocks), callback ) ); task.detach(); } void INodeTrivialRefreshStub::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); for (const uint32_t& height : blockHeights) { if (m_blockchainGenerator.getBlockchain().size() <= height) { lock.unlock(); callback(std::error_code(EDOM, std::generic_category())); return; } BlockDetails b = BlockDetails(); b.height = height; b.isOrphaned = false; Crypto::Hash hash = get_block_hash(m_blockchainGenerator.getBlockchain()[height]); b.hash = hash; if (!m_blockchainGenerator.getGeneratedTransactionsNumber(height, b.alreadyGeneratedTransactions)) { callback(std::error_code(EDOM, std::generic_category())); return; } std::vector v; v.push_back(b); blocks.push_back(v); } lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( std::bind( static_cast< void(INodeTrivialRefreshStub::*)( const std::vector&, std::vector&, const Callback& ) >(&INodeTrivialRefreshStub::doGetBlocks), this, std::cref(blockHashes), std::ref(blocks), callback ) ); task.detach(); } void INodeTrivialRefreshStub::doGetBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); for (const Crypto::Hash& hash: blockHashes) { auto iter = std::find_if( m_blockchainGenerator.getBlockchain().begin(), m_blockchainGenerator.getBlockchain().end(), [&hash](const Block& block) -> bool { return hash == get_block_hash(block); } ); if (iter == m_blockchainGenerator.getBlockchain().end()) { lock.unlock(); callback(std::error_code(EDOM, std::generic_category())); return; } BlockDetails b = BlockDetails(); Crypto::Hash actualHash = get_block_hash(*iter); b.hash = actualHash; b.isOrphaned = false; blocks.push_back(b); } lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( std::bind( static_cast< void(INodeTrivialRefreshStub::*)( uint64_t, uint64_t, uint32_t, std::vector&, uint32_t&, const Callback& ) >(&INodeTrivialRefreshStub::doGetBlocks), this, timestampBegin, timestampEnd, blocksNumberLimit, std::ref(blocks), std::ref(blocksNumberWithinTimestamps), callback ) ); task.detach(); } void INodeTrivialRefreshStub::doGetBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); std::vector blockHashes; if (!m_blockchainGenerator.getBlockIdsByTimestamp(timestampBegin, timestampEnd, blocksNumberLimit, blockHashes, blocksNumberWithinTimestamps)) { callback(std::error_code(EDOM, std::generic_category())); return; } for (const Crypto::Hash& hash: blockHashes) { auto iter = std::find_if( m_blockchainGenerator.getBlockchain().begin(), m_blockchainGenerator.getBlockchain().end(), [&hash](const Block& block) -> bool { return hash == get_block_hash(block); } ); if (iter == m_blockchainGenerator.getBlockchain().end()) { callback(std::error_code(EDOM, std::generic_category())); return; } BlockDetails b = BlockDetails(); Crypto::Hash actualHash = get_block_hash(*iter); b.hash = actualHash; b.isOrphaned = false; b.timestamp = iter->timestamp; blocks.push_back(b); } callback(std::error_code()); } void INodeTrivialRefreshStub::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( std::bind( static_cast< void(INodeTrivialRefreshStub::*)( const std::vector&, std::vector&, const Callback& ) >(&INodeTrivialRefreshStub::doGetTransactions), this, std::cref(transactionHashes), std::ref(transactions), callback ) ); task.detach(); } void INodeTrivialRefreshStub::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); for (const Crypto::Hash& hash : transactionHashes) { Transaction tx; TransactionDetails txDetails = TransactionDetails(); if (m_blockchainGenerator.getTransactionByHash(hash, tx, false)) { Crypto::Hash actualHash = getObjectHash(tx); txDetails.hash = actualHash; txDetails.inBlockchain = true; } else if (m_blockchainGenerator.getTransactionByHash(hash, tx, true)) { Crypto::Hash actualHash = getObjectHash(tx); txDetails.hash = actualHash; txDetails.inBlockchain = false; } else { lock.unlock(); callback(std::error_code(EDOM, std::generic_category())); return; } transactions.push_back(txDetails); } lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( std::bind( &INodeTrivialRefreshStub::doGetPoolTransactions, this, timestampBegin, timestampEnd, transactionsNumberLimit, std::ref(transactions), std::ref(transactionsNumberWithinTimestamps), callback ) ); task.detach(); } void INodeTrivialRefreshStub::doGetPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); std::vector transactionHashes; if (!m_blockchainGenerator.getPoolTransactionIdsByTimestamp(timestampBegin, timestampEnd, transactionsNumberLimit, transactionHashes, transactionsNumberWithinTimestamps)) { callback(std::error_code(EDOM, std::generic_category())); return; } for (const Crypto::Hash& hash : transactionHashes) { Transaction tx; TransactionDetails txDetails = TransactionDetails(); if (m_blockchainGenerator.getTransactionByHash(hash, tx, false)) { Crypto::Hash actualHash = getObjectHash(tx); txDetails.hash = actualHash; txDetails.inBlockchain = true; } else if (m_blockchainGenerator.getTransactionByHash(hash, tx, true)) { Crypto::Hash actualHash = getObjectHash(tx); txDetails.hash = actualHash; txDetails.inBlockchain = false; } else { callback(std::error_code(EDOM, std::generic_category())); return; } transactions.push_back(txDetails); } callback(std::error_code()); } void INodeTrivialRefreshStub::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( std::bind( &INodeTrivialRefreshStub::doGetTransactionsByPaymentId, this, std::cref(paymentId), std::ref(transactions), callback ) ); task.detach(); } void INodeTrivialRefreshStub::doGetTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); std::vector transactionHashes; if (!m_blockchainGenerator.getTransactionIdsByPaymentId(paymentId, transactionHashes)) { callback(std::error_code(EDOM, std::generic_category())); return; } for (const Crypto::Hash& hash : transactionHashes) { Transaction tx; TransactionDetails txDetails = TransactionDetails(); if (m_blockchainGenerator.getTransactionByHash(hash, tx, false)) { Crypto::Hash actualHash = getObjectHash(tx); txDetails.hash = actualHash; txDetails.inBlockchain = true; Crypto::Hash paymentId; BlockchainExplorerDataBuilder::getPaymentId(tx, paymentId); txDetails.paymentId = paymentId; } else if (m_blockchainGenerator.getTransactionByHash(hash, tx, true)) { Crypto::Hash actualHash = getObjectHash(tx); txDetails.hash =actualHash; txDetails.inBlockchain = false; Crypto::Hash paymentId; BlockchainExplorerDataBuilder::getPaymentId(tx, paymentId); txDetails.paymentId = paymentId; } else { callback(std::error_code(EDOM, std::generic_category())); return; } transactions.push_back(txDetails); } callback(std::error_code()); } void INodeTrivialRefreshStub::isSynchronized(bool& syncStatus, const Callback& callback) { //m_asyncCounter.addAsyncContext(); syncStatus = m_synchronized; callback(std::error_code()); } void INodeTrivialRefreshStub::setSynchronizedStatus(bool status) { m_synchronized = status; if (m_synchronized) { observerManager.notify(&INodeObserver::blockchainSynchronized, getLastLocalBlockHeight()); } } void INodeTrivialRefreshStub::sendPoolChanged() { observerManager.notify(&INodeObserver::poolChanged); } void INodeTrivialRefreshStub::sendLocalBlockchainUpdated(){ observerManager.notify(&INodeObserver::localBlockchainUpdated, getLastLocalBlockHeight()); } void INodeTrivialRefreshStub::getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, CryptoNote::MultisignatureOutput& out, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::unique_lock lock(m_walletLock); std::thread task(&INodeTrivialRefreshStub::doGetOutByMSigGIndex, this, amount, gindex, std::ref(out), callback); task.detach(); } void INodeTrivialRefreshStub::doGetOutByMSigGIndex(uint64_t amount, uint32_t gindex, CryptoNote::MultisignatureOutput& out, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_walletLock); bool success = m_blockchainGenerator.getMultisignatureOutputByGlobalIndex(amount, gindex, out); lock.unlock(); if (success) { callback(std::error_code()); } else { callback(std::make_error_code(std::errc::invalid_argument)); } }