Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/rawtransaction.cpp
// Copyright (c) 2010 Satoshi Nakamoto | // Copyright (c) 2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <chain.h> | #include <chain.h> | ||||
#include <chainparams.h> | #include <chainparams.h> | ||||
#include <coins.h> | #include <coins.h> | ||||
#include <compat/byteswap.h> | #include <compat/byteswap.h> | ||||
#include <config.h> | #include <config.h> | ||||
#include <consensus/validation.h> | #include <consensus/validation.h> | ||||
#include <core_io.h> | #include <core_io.h> | ||||
#include <index/txindex.h> | #include <index/txindex.h> | ||||
#include <init.h> | #include <init.h> | ||||
#include <interfaces/chain.h> | |||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <keystore.h> | #include <keystore.h> | ||||
#include <merkleblock.h> | #include <merkleblock.h> | ||||
#include <node/transaction.h> | #include <node/transaction.h> | ||||
#include <policy/policy.h> | #include <policy/policy.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <psbt.h> | #include <psbt.h> | ||||
#include <rpc/rawtransaction.h> | #include <rpc/rawtransaction.h> | ||||
▲ Show 20 Lines • Show All 908 Lines • ▼ Show 20 Lines | for (size_t i = 0; i < mergedTx.vin.size(); i++) { | ||||
txout.scriptPubKey, sigdata); | txout.scriptPubKey, sigdata); | ||||
UpdateInput(txin, sigdata); | UpdateInput(txin, sigdata); | ||||
} | } | ||||
return EncodeHexTx(CTransaction(mergedTx)); | 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, | UniValue SignTransaction(interfaces::Chain &chain, CMutableTransaction &mtx, | ||||
const UniValue &prevTxsUnival, | const UniValue &prevTxsUnival, | ||||
CBasicKeyStore *keystore, bool is_temp_keystore, | CBasicKeyStore *keystore, bool is_temp_keystore, | ||||
const UniValue &hashType) { | const UniValue &hashType) { | ||||
// Fetch previous transactions (inputs): | // Fetch previous transactions (inputs): | ||||
CCoinsView viewDummy; | std::map<COutPoint, Coin> coins; | ||||
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) { | for (const CTxIn &txin : mtx.vin) { | ||||
// Load entries from viewChain into view; can fail. | coins[txin.prevout]; // Create empty map entry keyed by prevout. | ||||
view.AccessCoin(txin.prevout); | |||||
} | |||||
// Switch back to avoid locking mempool for too long. | |||||
view.SetBackend(viewDummy); | |||||
} | } | ||||
chain.findCoins(coins); | |||||
// Add previous txouts given in the RPC call: | // Add previous txouts given in the RPC call: | ||||
if (!prevTxsUnival.isNull()) { | if (!prevTxsUnival.isNull()) { | ||||
UniValue prevTxs = prevTxsUnival.get_array(); | UniValue prevTxs = prevTxsUnival.get_array(); | ||||
for (size_t idx = 0; idx < prevTxs.size(); ++idx) { | for (size_t idx = 0; idx < prevTxs.size(); ++idx) { | ||||
const UniValue &p = prevTxs[idx]; | const UniValue &p = prevTxs[idx]; | ||||
if (!p.isObject()) { | if (!p.isObject()) { | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | ||||
Show All 22 Lines | if (!prevTxsUnival.isNull()) { | ||||
"vout must be positive"); | "vout must be positive"); | ||||
} | } | ||||
COutPoint out(txid, nOut); | COutPoint out(txid, nOut); | ||||
std::vector<uint8_t> pkData(ParseHexO(prevOut, "scriptPubKey")); | std::vector<uint8_t> pkData(ParseHexO(prevOut, "scriptPubKey")); | ||||
CScript scriptPubKey(pkData.begin(), pkData.end()); | CScript scriptPubKey(pkData.begin(), pkData.end()); | ||||
{ | { | ||||
const Coin &coin = view.AccessCoin(out); | auto coin = coins.find(out); | ||||
if (!coin.IsSpent() && | if (coin != coins.end() && !coin->second.IsSpent() && | ||||
coin.GetTxOut().scriptPubKey != scriptPubKey) { | coin->second.GetTxOut().scriptPubKey != scriptPubKey) { | ||||
std::string err("Previous output scriptPubKey mismatch:\n"); | 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); | "\nvs:\n" + ScriptToAsmStr(scriptPubKey); | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); | ||||
} | } | ||||
CTxOut txout; | CTxOut txout; | ||||
txout.scriptPubKey = scriptPubKey; | txout.scriptPubKey = scriptPubKey; | ||||
txout.nValue = Amount::zero(); | txout.nValue = Amount::zero(); | ||||
if (prevOut.exists("amount")) { | if (prevOut.exists("amount")) { | ||||
txout.nValue = | txout.nValue = | ||||
AmountFromValue(find_value(prevOut, "amount")); | AmountFromValue(find_value(prevOut, "amount")); | ||||
} else { | } else { | ||||
// amount param is required in replay-protected txs. | // amount param is required in replay-protected txs. | ||||
// Note that we must check for its presence here rather | // Note that we must check for its presence here rather | ||||
// than use RPCTypeCheckObj() above, since UniValue::VNUM | // than use RPCTypeCheckObj() above, since UniValue::VNUM | ||||
// parser incorrectly parses numerics with quotes, eg | // parser incorrectly parses numerics with quotes, eg | ||||
// "3.12" as a string when JSON allows it to also parse | // "3.12" as a string when JSON allows it to also parse | ||||
// as numeric. And we have to accept numerics with quotes | // as numeric. And we have to accept numerics with quotes | ||||
// because our own dogfood (our rpc results) always | // because our own dogfood (our rpc results) always | ||||
// produces decimal numbers that are quoted | // produces decimal numbers that are quoted | ||||
// eg getbalance returns "3.14152" rather than 3.14152 | // eg getbalance returns "3.14152" rather than 3.14152 | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); | throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); | ||||
} | } | ||||
coins[out] = Coin(txout, 1, false); | |||||
view.AddCoin(out, Coin(txout, 1, false), true); | |||||
} | } | ||||
// If redeemScript and private keys were given, add redeemScript to | // If redeemScript and private keys were given, add redeemScript to | ||||
// the keystore so it can be signed | // the keystore so it can be signed | ||||
if (is_temp_keystore && scriptPubKey.IsPayToScriptHash()) { | if (is_temp_keystore && scriptPubKey.IsPayToScriptHash()) { | ||||
RPCTypeCheckObj( | RPCTypeCheckObj( | ||||
prevOut, { | prevOut, { | ||||
{"redeemScript", UniValueType(UniValue::VSTR)}, | {"redeemScript", UniValueType(UniValue::VSTR)}, | ||||
Show All 18 Lines | UniValue SignTransaction(interfaces::Chain &chain, CMutableTransaction &mtx, | ||||
UniValue vErrors(UniValue::VARR); | UniValue vErrors(UniValue::VARR); | ||||
// Use CTransaction for the constant parts of the transaction to avoid | // Use CTransaction for the constant parts of the transaction to avoid | ||||
// rehashing. | // rehashing. | ||||
const CTransaction txConst(mtx); | const CTransaction txConst(mtx); | ||||
// Sign what we can: | // Sign what we can: | ||||
for (size_t i = 0; i < mtx.vin.size(); i++) { | for (size_t i = 0; i < mtx.vin.size(); i++) { | ||||
CTxIn &txin = mtx.vin[i]; | CTxIn &txin = mtx.vin[i]; | ||||
const Coin &coin = view.AccessCoin(txin.prevout); | auto coin = coins.find(txin.prevout); | ||||
if (coin.IsSpent()) { | if (coin == coins.end() || coin->second.IsSpent()) { | ||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); | TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); | ||||
continue; | continue; | ||||
} | } | ||||
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; | const CScript &prevPubKey = coin->second.GetTxOut().scriptPubKey; | ||||
const Amount amount = coin.GetTxOut().nValue; | 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: | // Only sign SIGHASH_SINGLE if there's a corresponding output: | ||||
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || | if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || | ||||
(i < mtx.vout.size())) { | (i < mtx.vout.size())) { | ||||
ProduceSignature(*keystore, | ProduceSignature(*keystore, | ||||
MutableTransactionSignatureCreator(&mtx, i, amount, | MutableTransactionSignatureCreator(&mtx, i, amount, | ||||
sigHashType), | sigHashType), | ||||
prevPubKey, sigdata); | prevPubKey, sigdata); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 968 Lines • Show Last 20 Lines |