void TransfersConsumer::processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs, bool& contains, bool& updated) { TransactionInformation subscribtionTxInfo; contains = sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo); updated = false; if (contains) { if (subscribtionTxInfo.blockHeight == WALLET_UNCONFIRMED_TRANSACTION_HEIGHT && blockInfo.height != WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) { try { // pool->blockchain sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); updated = true; } catch (...) { m_logger(ERROR, BRIGHT_RED) << "markTransactionConfirmed failed, throw MarkTransactionConfirmedException"; throw MarkTransactionConfirmedException(tx.getTransactionHash()); } } else { assert(subscribtionTxInfo.blockHeight == blockInfo.height); } } else { updated = sub.addTransaction(blockInfo, tx, transfers); contains = updated; } }
void TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) { std::vector<TransactionOutputInformationIn> emptyOutputs; std::vector<ITransfersContainer*> transactionContainers; m_logger(TRACE) << "Process transaction, block " << blockInfo.height << ", transaction index " << blockInfo.transactionIndex << ", hash " << tx.getTransactionHash(); bool someContainerUpdated = false; for (auto& kv : m_subscriptions) { auto it = info.outputs.find(kv.first); auto& subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it->second; bool containerContainsTx; bool containerUpdated; processOutputs(blockInfo, *kv.second, tx, subscriptionOutputs, info.globalIdxs, containerContainsTx, containerUpdated); someContainerUpdated = someContainerUpdated || containerUpdated; if (containerContainsTx) { transactionContainers.emplace_back(&kv.second->getContainer()); } } if (someContainerUpdated) { m_logger(TRACE) << "Transaction updated some containers, hash " << tx.getTransactionHash(); m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionUpdated, this, tx.getTransactionHash(), transactionContainers); } else { m_logger(TRACE) << "Transaction doesn't updated any container, hash " << tx.getTransactionHash(); } }
std::error_code createTransfers( const AccountKeys& account, const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const std::vector<uint32_t>& outputs, const std::vector<uint32_t>& globalIdxs, std::vector<TransactionOutputInformationIn>& transfers) { auto txPubKey = tx.getTransactionPublicKey(); for (auto idx : outputs) { if (idx >= tx.getOutputCount()) { return std::make_error_code(std::errc::argument_out_of_domain); } auto outType = tx.getOutputType(size_t(idx)); if ( outType != TransactionTypes::OutputType::Key) { continue; } TransactionOutputInformationIn info; info.type = outType; info.transactionPublicKey = txPubKey; info.outputInTransaction = idx; info.globalOutputIndex = (blockInfo.height == WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx]; if (outType == TransactionTypes::OutputType::Key) { uint64_t amount; KeyOutput out; tx.getOutput(idx, out, amount); CryptoNote::KeyPair in_ephemeral; CryptoNote::generate_key_image_helper( account, txPubKey, idx, in_ephemeral, info.keyImage); assert(out.key == reinterpret_cast<const PublicKey&>(in_ephemeral.publicKey)); info.amount = amount; info.outputKey = out.key; } transfers.push_back(info); } return std::error_code(); }
std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info) { std::unordered_map<PublicKey, std::vector<uint32_t>> outputs; findMyOutputs(tx, m_viewSecret, m_spendKeys, outputs); if (outputs.empty()) { return std::error_code(); } std::error_code errorCode; auto txHash = tx.getTransactionHash(); if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { errorCode = getGlobalIndices(reinterpret_cast<const Hash&>(txHash), info.globalIdxs); if (errorCode) { return errorCode; } } for (const auto& kv : outputs) { auto it = m_subscriptions.find(kv.first); if (it != m_subscriptions.end()) { auto& transfers = info.outputs[kv.first]; errorCode = createTransfers(it->second->getKeys(), blockInfo, tx, kv.second, info.globalIdxs, transfers); if (errorCode) { return errorCode; } } } return std::error_code(); }
cryptonote::Transaction convertTx(ITransactionReader& tx) { auto blob = tx.getTransactionData(); cryptonote::blobdata data(reinterpret_cast<const char*>(blob.data()), blob.size()); cryptonote::Transaction oldTx; cryptonote::parse_and_validate_tx_from_blob(data, oldTx); // ignore return code return oldTx; }
void TransfersSubscription::addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfersList) { bool added = transfers.addTransaction(blockInfo, tx, transfersList); if (added) { m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); } }
void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, std::vector<std::string>&& messages) { bool added = m_transfers.addTransaction(blockInfo, tx, transfers, std::move(messages)); if (added) { m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); } }
std::error_code submitTransaction(INode& node, ITransactionReader& tx) { auto data = tx.getTransactionData(); CryptoNote::Transaction outTx; fromBinaryArray(outTx, data); LOG_DEBUG("Submitting transaction " + Common::toHex(tx.getTransactionHash().data, 32)); std::promise<std::error_code> result; node.relayTransaction(outTx, [&result](std::error_code ec) { result.set_value(ec); }); auto err = result.get_future().get(); if (err) { LOG_DEBUG("Error: " + err.message()); } else { LOG_DEBUG("Submitted successfully"); } return err; }
std::error_code submitTransaction(INode& node, ITransactionReader& tx) { auto data = tx.getTransactionData(); cryptonote::blobdata txblob(data.data(), data.data() + data.size()); cryptonote::Transaction outTx; cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); LOG_DEBUG("Submitting transaction " + bin2str(tx.getTransactionHash())); std::promise<std::error_code> result; node.relayTransaction(outTx, [&result](std::error_code ec) { result.set_value(ec); }); auto err = result.get_future().get(); if (err) { LOG_DEBUG("Error: " + err.message()); } else { LOG_DEBUG("Submitted successfully"); } return err; }
void TransfersConsumer::processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs, bool& contains, bool& updated) { TransactionInformation subscribtionTxInfo; contains = sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo); updated = false; if (contains) { if (subscribtionTxInfo.blockHeight == WALLET_UNCONFIRMED_TRANSACTION_HEIGHT && blockInfo.height != WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) { // pool->blockchain sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); updated = true; } else { assert(subscribtionTxInfo.blockHeight == blockInfo.height); } } else { auto messages = get_messages_from_extra(tx.getExtra(), tx.getTransactionPublicKey(), &sub.getKeys().spendSecretKey); updated = sub.addTransaction(blockInfo, tx, transfers, std::move(messages)); contains = updated; } }
std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs) { if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { TransactionInformation subscribtionTxInfo; int64_t txBalance; if (sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo, txBalance)) { if (subscribtionTxInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { // pool->blockchain sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); return std::error_code(); } else { // - Subscription already has this transaction as confirmed, so why are we here? // - Because, for instance, some another subscription doesn't have this transactions, so it is given to us again. return std::error_code(); } } } sub.addTransaction(blockInfo, tx, transfers); return std::error_code(); }
std::error_code submitTransaction(ITransactionReader& tx) { auto data = tx.getTransactionData(); cryptonote::blobdata txblob(data.data(), data.data() + data.size()); cryptonote::Transaction outTx; cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); std::promise<std::error_code> result; m_node.relayTransaction(outTx, [&result](std::error_code ec) { std::promise<std::error_code> detachedPromise = std::move(result); detachedPromise.set_value(ec); }); return result.get_future().get(); }
bool TransfersSubscription::addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfersList, std::vector<std::string>&& messages) { std::vector<TransactionOutputInformation> unlockedTransfers; bool added = transfers.addTransaction(blockInfo, tx, transfersList, std::move(messages), &unlockedTransfers); if (added) { m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); } if (!unlockedTransfers.empty()) { m_observerManager.notify(&ITransfersObserver::onTransfersUnlocked, this, unlockedTransfers); } return added; }
std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info) { findMyOutputs(tx, m_viewSecret, m_spendKeys, info.outputs); std::error_code errorCode; if (!info.outputs.empty()) { auto txHash = tx.getTransactionHash(); if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { errorCode = getGlobalIndices(reinterpret_cast<const crypto::hash&>(txHash), info.globalIdxs); if (errorCode) { return errorCode; } } } return std::error_code(); }
std::error_code BlockchainSynchronizer::doAddUnconfirmedTransaction(const ITransactionReader& transaction) { std::unique_lock<std::mutex> lk(m_consumersMutex); std::error_code ec; auto addIt = m_consumers.begin(); for (; addIt != m_consumers.end(); ++addIt) { ec = addIt->first->addUnconfirmedTransaction(transaction); if (ec) { break; } } if (ec) { auto transactionHash = transaction.getTransactionHash(); for (auto rollbackIt = m_consumers.begin(); rollbackIt != addIt; ++rollbackIt) { rollbackIt->first->removeUnconfirmedTransaction(transactionHash); } } return ec; }
std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, const std::vector<uint32_t>& outputs, const std::vector<uint64_t>& globalIdxs) { if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { TransactionInformation subscribtionTxInfo; int64_t txBalance; if (sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo, txBalance)) { if (subscribtionTxInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { // pool->blockchain sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); return std::error_code(); } else { // - Subscription already has this transaction as confirmed, so why are we here? // - Because, for instance, some another subscription doesn't have this transactions, so it is given to us again. return std::error_code(); } } } std::vector<TransactionOutputInformationIn> transfers; auto txPubKey = tx.getTransactionPublicKey(); for (auto idx : outputs) { if (idx >= tx.getOutputCount()) { return std::make_error_code(std::errc::argument_out_of_domain); } auto outType = tx.getOutputType(size_t(idx)); if ( outType != TransactionTypes::OutputType::Key && outType != TransactionTypes::OutputType::Multisignature) { continue; } TransactionOutputInformationIn info; info.type = outType; info.transactionPublicKey = txPubKey; info.outputInTransaction = idx; info.globalOutputIndex = (blockInfo.height == UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx]; if (outType == TransactionTypes::OutputType::Key) { TransactionTypes::OutputKey out; tx.getOutput(idx, out); cryptonote::KeyPair in_ephemeral; cryptonote::generate_key_image_helper( reinterpret_cast<const cryptonote::account_keys&>(sub.getKeys()), reinterpret_cast<const crypto::public_key&>(txPubKey), idx, in_ephemeral, reinterpret_cast<crypto::key_image&>(info.keyImage)); assert(out.key == reinterpret_cast<const PublicKey&>(in_ephemeral.pub)); info.amount = out.amount; info.outputKey = out.key; } else if (outType == TransactionTypes::OutputType::Multisignature) { TransactionTypes::OutputMultisignature out; tx.getOutput(idx, out); info.amount = out.amount; info.requiredSignatures = out.requiredSignatures; } transfers.push_back(info); } sub.addTransaction(blockInfo, tx, transfers); return std::error_code(); }