diff options
Diffstat (limited to 'src/node')
-rw-r--r-- | src/node/README.md | 22 | ||||
-rw-r--r-- | src/node/coin.cpp | 22 | ||||
-rw-r--r-- | src/node/coin.h | 22 | ||||
-rw-r--r-- | src/node/psbt.cpp | 134 | ||||
-rw-r--r-- | src/node/psbt.h | 43 | ||||
-rw-r--r-- | src/node/transaction.cpp | 78 | ||||
-rw-r--r-- | src/node/transaction.h | 24 |
7 files changed, 345 insertions, 0 deletions
diff --git a/src/node/README.md b/src/node/README.md new file mode 100644 index 0000000000..e99a717534 --- /dev/null +++ b/src/node/README.md @@ -0,0 +1,22 @@ +# src/node/ + +The [`src/node/`](./) directory contains code that needs to access node state +(state in `CChain`, `CBlockIndex`, `CCoinsView`, `CTxMemPool`, and similar +classes). + +Code in [`src/node/`](./) is meant to be segregated from code in +[`src/wallet/`](../wallet/) and [`src/qt/`](../qt/), to ensure wallet and GUI +code changes don't interfere with node operation, to allow wallet and GUI code +to run in separate processes, and to perhaps eventually allow wallet and GUI +code to be maintained in separate source repositories. + +As a rule of thumb, code in one of the [`src/node/`](./), +[`src/wallet/`](../wallet/), or [`src/qt/`](../qt/) directories should avoid +calling code in the other directories directly, and only invoke it indirectly +through the more limited [`src/interfaces/`](../interfaces/) classes. + +The [`src/node/`](./) directory is a new directory introduced in +[#14978](https://github.com/bitcoin/bitcoin/pull/14978) and at the moment is +sparsely populated. Eventually more substantial files like +[`src/validation.cpp`](../validation.cpp) and +[`src/txmempool.cpp`](../txmempool.cpp) might be moved there. diff --git a/src/node/coin.cpp b/src/node/coin.cpp new file mode 100644 index 0000000000..bb98e63f3a --- /dev/null +++ b/src/node/coin.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <node/coin.h> + +#include <txmempool.h> +#include <validation.h> + +void FindCoins(std::map<COutPoint, Coin>& coins) +{ + LOCK2(cs_main, ::mempool.cs); + assert(pcoinsTip); + CCoinsViewCache& chain_view = *::pcoinsTip; + CCoinsViewMemPool mempool_view(&chain_view, ::mempool); + for (auto& coin : coins) { + if (!mempool_view.GetCoin(coin.first, coin.second)) { + // Either the coin is not in the CCoinsViewCache or is spent. Clear it. + coin.second.Clear(); + } + } +} diff --git a/src/node/coin.h b/src/node/coin.h new file mode 100644 index 0000000000..eb95b75cfb --- /dev/null +++ b/src/node/coin.h @@ -0,0 +1,22 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_COIN_H +#define BITCOIN_NODE_COIN_H + +#include <map> + +class COutPoint; +class Coin; + +/** + * Look up unspent output information. Returns coins in the mempool and in the + * current chain UTXO set. Iterates through all the keys in the map and + * populates the values. + * + * @param[in,out] coins map to fill + */ +void FindCoins(std::map<COutPoint, Coin>& coins); + +#endif // BITCOIN_NODE_COIN_H diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp new file mode 100644 index 0000000000..12559c5a5f --- /dev/null +++ b/src/node/psbt.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <coins.h> +#include <consensus/tx_verify.h> +#include <node/psbt.h> +#include <policy/policy.h> +#include <policy/settings.h> + +#include <numeric> + +PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) +{ + // Go through each input and build status + PSBTAnalysis result; + + bool calc_fee = true; + bool all_final = true; + bool only_missing_sigs = true; + bool only_missing_final = false; + CAmount in_amt = 0; + + result.inputs.resize(psbtx.tx->vin.size()); + + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + PSBTInput& input = psbtx.inputs[i]; + PSBTInputAnalysis& input_analysis = result.inputs[i]; + + // Check for a UTXO + CTxOut utxo; + if (psbtx.GetInputUTXO(utxo, i)) { + in_amt += utxo.nValue; + input_analysis.has_utxo = true; + } else { + input_analysis.has_utxo = false; + input_analysis.is_final = false; + input_analysis.next = PSBTRole::UPDATER; + calc_fee = false; + } + + // Check if it is final + if (!utxo.IsNull() && !PSBTInputSigned(input)) { + input_analysis.is_final = false; + all_final = false; + + // Figure out what is missing + SignatureData outdata; + bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata); + + // Things are missing + if (!complete) { + input_analysis.missing_pubkeys = outdata.missing_pubkeys; + input_analysis.missing_redeem_script = outdata.missing_redeem_script; + input_analysis.missing_witness_script = outdata.missing_witness_script; + input_analysis.missing_sigs = outdata.missing_sigs; + + // If we are only missing signatures and nothing else, then next is signer + if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) { + input_analysis.next = PSBTRole::SIGNER; + } else { + only_missing_sigs = false; + input_analysis.next = PSBTRole::UPDATER; + } + } else { + only_missing_final = true; + input_analysis.next = PSBTRole::FINALIZER; + } + } else if (!utxo.IsNull()){ + input_analysis.is_final = true; + } + } + + if (all_final) { + only_missing_sigs = false; + result.next = PSBTRole::EXTRACTOR; + } + if (calc_fee) { + // Get the output amount + CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0), + [](CAmount a, const CTxOut& b) { + return a += b.nValue; + } + ); + + // Get the fee + CAmount fee = in_amt - out_amt; + result.fee = fee; + + // Estimate the size + CMutableTransaction mtx(*psbtx.tx); + CCoinsView view_dummy; + CCoinsViewCache view(&view_dummy); + bool success = true; + + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + PSBTInput& input = psbtx.inputs[i]; + Coin newcoin; + + if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) { + success = false; + break; + } else { + mtx.vin[i].scriptSig = input.final_script_sig; + mtx.vin[i].scriptWitness = input.final_script_witness; + newcoin.nHeight = 1; + view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true); + } + } + + if (success) { + CTransaction ctx = CTransaction(mtx); + size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS)); + result.estimated_vsize = size; + // Estimate fee rate + CFeeRate feerate(fee, size); + result.estimated_feerate = feerate; + } + + if (only_missing_sigs) { + result.next = PSBTRole::SIGNER; + } else if (only_missing_final) { + result.next = PSBTRole::FINALIZER; + } else if (all_final) { + result.next = PSBTRole::EXTRACTOR; + } else { + result.next = PSBTRole::UPDATER; + } + } else { + result.next = PSBTRole::UPDATER; + } + + return result; +} diff --git a/src/node/psbt.h b/src/node/psbt.h new file mode 100644 index 0000000000..e04366a20f --- /dev/null +++ b/src/node/psbt.h @@ -0,0 +1,43 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_PSBT_H +#define BITCOIN_NODE_PSBT_H + +#include <psbt.h> + +/** + * Holds an analysis of one input from a PSBT + */ +struct PSBTInputAnalysis { + bool has_utxo; //!< Whether we have UTXO information for this input + bool is_final; //!< Whether the input has all required information including signatures + PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next + + std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing + std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing + uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing + uint256 missing_witness_script; //!< SHA256 of witness script, if missing +}; + +/** + * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT) + */ +struct PSBTAnalysis { + Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction + Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction + Optional<CAmount> fee; //!< Amount of fee being paid by the transaction + std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction + PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next +}; + +/** + * Provides helpful miscellaneous information about where a PSBT is in the signing workflow. + * + * @param[in] psbtx the PSBT to analyze + * @return A PSBTAnalysis with information about the provided PSBT. + */ +PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx); + +#endif // BITCOIN_NODE_PSBT_H diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp new file mode 100644 index 0000000000..5ffb15ed3c --- /dev/null +++ b/src/node/transaction.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <net.h> +#include <txmempool.h> +#include <util/validation.h> +#include <validation.h> +#include <validationinterface.h> +#include <node/transaction.h> + +#include <future> + +TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee) +{ + std::promise<void> promise; + hashTx = tx->GetHash(); + + { // cs_main scope + LOCK(cs_main); + CCoinsViewCache &view = *pcoinsTip; + bool fHaveChain = false; + for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { + const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o)); + fHaveChain = !existingCoin.IsSpent(); + } + bool fHaveMempool = mempool.exists(hashTx); + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, + nullptr /* plTxnReplaced */, false /* bypass_limits */, highfee)) { + if (state.IsInvalid()) { + err_string = FormatStateMessage(state); + return TransactionError::MEMPOOL_REJECTED; + } else { + if (fMissingInputs) { + return TransactionError::MISSING_INPUTS; + } + err_string = FormatStateMessage(state); + return TransactionError::MEMPOOL_ERROR; + } + } else { + // If wallet is enabled, ensure that the wallet has been made aware + // of the new transaction prior to returning. This prevents a race + // where a user might call sendrawtransaction with a transaction + // to/from their wallet, immediately call some wallet RPC, and get + // a stale result because callbacks have not yet been processed. + CallFunctionInValidationInterfaceQueue([&promise] { + promise.set_value(); + }); + } + } else if (fHaveChain) { + return TransactionError::ALREADY_IN_CHAIN; + } else { + // Make sure we don't block forever if re-sending + // a transaction already in mempool. + promise.set_value(); + } + + } // cs_main + + promise.get_future().wait(); + + if (!g_connman) { + return TransactionError::P2P_DISABLED; + } + + CInv inv(MSG_TX, hashTx); + g_connman->ForEachNode([&inv](CNode* pnode) { + pnode->PushInventory(inv); + }); + + return TransactionError::OK; +} diff --git a/src/node/transaction.h b/src/node/transaction.h new file mode 100644 index 0000000000..51033f94e5 --- /dev/null +++ b/src/node/transaction.h @@ -0,0 +1,24 @@ +// Copyright (c) 2017-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_TRANSACTION_H +#define BITCOIN_NODE_TRANSACTION_H + +#include <attributes.h> +#include <primitives/transaction.h> +#include <uint256.h> +#include <util/error.h> + +/** + * Broadcast a transaction + * + * @param[in] tx the transaction to broadcast + * @param[out] &txid the txid of the transaction, if successfully broadcast + * @param[out] &err_string reference to std::string to fill with error string if available + * @param[in] highfee Reject txs with fees higher than this (if 0, accept any fee) + * return error + */ +NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, uint256& txid, std::string& err_string, const CAmount& highfee); + +#endif // BITCOIN_NODE_TRANSACTION_H |