diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -488,6 +488,7 @@ minerfund.cpp net.cpp net_processing.cpp + node/coin.cpp node/transaction.cpp noui.cpp outputtype.cpp diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,6 +178,7 @@ netaddress.h \ netbase.h \ netmessagemaker.h \ + node/coin.h \ node/transaction.h \ noui.h \ optional.h \ @@ -299,6 +300,7 @@ minerfund.cpp \ net.cpp \ net_processing.cpp \ + node/coin.cpp \ node/transaction.cpp \ noui.cpp \ outputtype.cpp \ diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -19,6 +19,7 @@ class CBlock; struct CBlockLocator; class CChainParams; +class Coin; class Config; class CRPCCommand; class CScheduler; @@ -176,6 +177,11 @@ int64_t *time = nullptr, int64_t *max_time = nullptr) = 0; + //! 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. + virtual void findCoins(std::map &coins) = 0; + //! Estimate fraction of total transactions verified if blocks up to //! the specified block hash are verified. virtual double guessVerificationProgress(const BlockHash &block_hash) = 0; diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -295,6 +296,9 @@ } return true; } + void findCoins(std::map &coins) override { + return FindCoins(coins); + } double guessVerificationProgress(const BlockHash &block_hash) override { LOCK(cs_main); return GuessVerificationProgress(Params().TxData(), diff --git a/src/node/coin.h b/src/node/coin.h new file mode 100644 --- /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 + +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 &coins); + +#endif // BITCOIN_NODE_COIN_H diff --git a/src/node/coin.cpp b/src/node/coin.cpp new file mode 100644 --- /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 + +#include +#include + +void FindCoins(std::map &coins) { + LOCK2(cs_main, ::g_mempool.cs); + assert(pcoinsTip); + CCoinsViewCache &chain_view = *::pcoinsTip; + CCoinsViewMemPool mempool_view(&chain_view, ::g_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/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -936,28 +937,22 @@ return EncodeHexTx(CTransaction(mergedTx)); } +// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237): +// This function is called from both wallet and node rpcs +// (signrawtransactionwithwallet and signrawtransactionwithkey). It should be +// moved to a util file so wallet code doesn't need to link against node code. +// Also the dependency on interfaces::Chain should be removed, so +// signrawtransactionwithkey doesn't need access to a Chain instance. UniValue SignTransaction(interfaces::Chain &chain, CMutableTransaction &mtx, const UniValue &prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue &hashType) { // Fetch previous transactions (inputs): - CCoinsView viewDummy; - CCoinsViewCache view(&viewDummy); - { - LOCK2(cs_main, g_mempool.cs); - CCoinsViewCache &viewChain = *pcoinsTip; - CCoinsViewMemPool viewMempool(&viewChain, g_mempool); - // Temporarily switch cache backend to db+mempool view. - view.SetBackend(viewMempool); - - for (const CTxIn &txin : mtx.vin) { - // Load entries from viewChain into view; can fail. - view.AccessCoin(txin.prevout); - } - - // Switch back to avoid locking mempool for too long. - view.SetBackend(viewDummy); + std::map coins; + for (const CTxIn &txin : mtx.vin) { + coins[txin.prevout]; // Create empty map entry keyed by prevout. } + chain.findCoins(coins); // Add previous txouts given in the RPC call: if (!prevTxsUnival.isNull()) { @@ -996,11 +991,12 @@ CScript scriptPubKey(pkData.begin(), pkData.end()); { - const Coin &coin = view.AccessCoin(out); - if (!coin.IsSpent() && - coin.GetTxOut().scriptPubKey != scriptPubKey) { + auto coin = coins.find(out); + if (coin != coins.end() && !coin->second.IsSpent() && + coin->second.GetTxOut().scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); - err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) + + err = err + + ScriptToAsmStr(coin->second.GetTxOut().scriptPubKey) + "\nvs:\n" + ScriptToAsmStr(scriptPubKey); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } @@ -1023,8 +1019,7 @@ // eg getbalance returns "3.14152" rather than 3.14152 throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); } - - view.AddCoin(out, Coin(txout, 1, false), true); + coins[out] = Coin(txout, 1, false); } // If redeemScript and private keys were given, add redeemScript to @@ -1059,16 +1054,17 @@ // Sign what we can: for (size_t i = 0; i < mtx.vin.size(); i++) { CTxIn &txin = mtx.vin[i]; - const Coin &coin = view.AccessCoin(txin.prevout); - if (coin.IsSpent()) { + auto coin = coins.find(txin.prevout); + if (coin == coins.end() || coin->second.IsSpent()) { TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); continue; } - const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; - const Amount amount = coin.GetTxOut().nValue; + const CScript &prevPubKey = coin->second.GetTxOut().scriptPubKey; + const Amount amount = coin->second.GetTxOut().nValue; - SignatureData sigdata = DataFromTransaction(mtx, i, coin.GetTxOut()); + SignatureData sigdata = + DataFromTransaction(mtx, i, coin->second.GetTxOut()); // Only sign SIGHASH_SINGLE if there's a corresponding output: if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || (i < mtx.vout.size())) {