diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index 6f5d89f5d..84970ec55 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -1,219 +1,218 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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 "merkleblock.h" #include "consensus/consensus.h" #include "hash.h" #include "utilstrencodings.h" CMerkleBlock::CMerkleBlock(const CBlock &block, CBloomFilter &filter) { header = block.GetBlockHeader(); std::vector vMatch; std::vector vHashes; vMatch.reserve(block.vtx.size()); vHashes.reserve(block.vtx.size()); for (size_t i = 0; i < block.vtx.size(); i++) { const CTransaction *tx = block.vtx[i].get(); const uint256 &txid = tx->GetId(); if (filter.IsRelevantAndUpdate(*tx)) { vMatch.push_back(true); vMatchedTxn.push_back(std::make_pair(i, txid)); } else { vMatch.push_back(false); } vHashes.push_back(txid); } txn = CPartialMerkleTree(vHashes, vMatch); } -CMerkleBlock::CMerkleBlock(const CBlock &block, - const std::set &txids) { +CMerkleBlock::CMerkleBlock(const CBlock &block, const std::set &txids) { header = block.GetBlockHeader(); std::vector vMatch; std::vector vHashes; vMatch.reserve(block.vtx.size()); vHashes.reserve(block.vtx.size()); for (const auto &tx : block.vtx) { - const uint256 &txid = tx->GetId(); + const TxId &txid = tx->GetId(); vMatch.push_back(txids.count(txid)); vHashes.push_back(txid); } txn = CPartialMerkleTree(vHashes, vMatch); } uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector &vTxid) { if (height == 0) { // hash at height 0 is the txids themself. return vTxid[pos]; } // Calculate left hash. uint256 left = CalcHash(height - 1, pos * 2, vTxid), right; // Calculate right hash if not beyond the end of the array - copy left hash // otherwise1. if (pos * 2 + 1 < CalcTreeWidth(height - 1)) { right = CalcHash(height - 1, pos * 2 + 1, vTxid); } else { right = left; } // Combine subhashes. return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); } void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch) { // Determine whether this node is the parent of at least one matched txid. bool fParentOfMatch = false; for (unsigned int p = pos << height; p < (pos + 1) << height && p < nTransactions; p++) { fParentOfMatch |= vMatch[p]; } // Store as flag bit. vBits.push_back(fParentOfMatch); if (height == 0 || !fParentOfMatch) { // If at height 0, or nothing interesting below, store hash and stop. vHash.push_back(CalcHash(height, pos, vTxid)); } else { // Otherwise, don't store any hash, but descend into the subtrees. TraverseAndBuild(height - 1, pos * 2, vTxid, vMatch); if (pos * 2 + 1 < CalcTreeWidth(height - 1)) { TraverseAndBuild(height - 1, pos * 2 + 1, vTxid, vMatch); } } } uint256 CPartialMerkleTree::TraverseAndExtract( int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch, std::vector &vnIndex) { if (nBitsUsed >= vBits.size()) { // Overflowed the bits array - failure fBad = true; return uint256(); } bool fParentOfMatch = vBits[nBitsUsed++]; if (height == 0 || !fParentOfMatch) { // If at height 0, or nothing interesting below, use stored hash and do // not descend. if (nHashUsed >= vHash.size()) { // Overflowed the hash array - failure fBad = true; return uint256(); } const uint256 &hash = vHash[nHashUsed++]; // In case of height 0, we have a matched txid. if (height == 0 && fParentOfMatch) { vMatch.push_back(hash); vnIndex.push_back(pos); } return hash; } // Otherwise, descend into the subtrees to extract matched txids and hashes. uint256 left = TraverseAndExtract(height - 1, pos * 2, nBitsUsed, nHashUsed, vMatch, vnIndex), right; if (pos * 2 + 1 < CalcTreeWidth(height - 1)) { right = TraverseAndExtract(height - 1, pos * 2 + 1, nBitsUsed, nHashUsed, vMatch, vnIndex); if (right == left) { // The left and right branches should never be identical, as the // transaction hashes covered by them must each be unique. fBad = true; } } else { right = left; } // and combine them before returning. return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); } CPartialMerkleTree::CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch) : nTransactions(vTxid.size()), fBad(false) { // reset state vBits.clear(); vHash.clear(); // calculate height of tree int nHeight = 0; while (CalcTreeWidth(nHeight) > 1) { nHeight++; } // traverse the partial tree TraverseAndBuild(nHeight, 0, vTxid, vMatch); } CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {} uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch, std::vector &vnIndex) { vMatch.clear(); // An empty set will not work if (nTransactions == 0) { return uint256(); } // Check for excessively high numbers of transactions. // FIXME: Track the maximum block size we've seen and use it here. // There can never be more hashes provided than one for every txid. if (vHash.size() > nTransactions) { return uint256(); } // There must be at least one bit per node in the partial tree, and at least // one node per hash. if (vBits.size() < vHash.size()) { return uint256(); } // calculate height of tree. int nHeight = 0; while (CalcTreeWidth(nHeight) > 1) { nHeight++; } // traverse the partial tree. unsigned int nBitsUsed = 0, nHashUsed = 0; uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch, vnIndex); // verify that no problems occurred during the tree traversal. if (fBad) { return uint256(); } // verify that all bits were consumed (except for the padding caused by // serializing it as a byte sequence) if ((nBitsUsed + 7) / 8 != (vBits.size() + 7) / 8) { return uint256(); } // verify that all hashes were consumed. if (nHashUsed != vHash.size()) { return uint256(); } return hashMerkleRoot; } diff --git a/src/merkleblock.h b/src/merkleblock.h index f8de87c63..4a5c8bc3c 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -1,178 +1,178 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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_MERKLEBLOCK_H #define BITCOIN_MERKLEBLOCK_H #include "bloom.h" #include "primitives/block.h" #include "serialize.h" #include "uint256.h" #include /** * Data structure that represents a partial merkle tree. * * It represents a subset of the txid's of a known block, in a way that * allows recovery of the list of txid's and the merkle root, in an * authenticated way. * * The encoding works as follows: we traverse the tree in depth-first order, * storing a bit for each traversed node, signifying whether the node is the * parent of at least one matched leaf txid (or a matched txid itself). In * case we are at the leaf level, or this bit is 0, its merkle node hash is * stored, and its children are not explorer further. Otherwise, no hash is * stored, but we recurse into both (or the only) child branch. During * decoding, the same depth-first traversal is performed, consuming bits and * hashes as they written during encoding. * * The serialization is fixed and provides a hard guarantee about the * encoded size: * * SIZE <= 10 + ceil(32.25*N) * * Where N represents the number of leaf nodes of the partial tree. N itself * is bounded by: * * N <= total_transactions * N <= 1 + matched_transactions*tree_height * * The serialization format: * - uint32 total_transactions (4 bytes) * - varint number of hashes (1-3 bytes) * - uint256[] hashes in depth-first order (<= 32*N bytes) * - varint number of bytes of flag bits (1-3 bytes) * - byte[] flag bits, packed per 8 in a byte, least significant bit first * (<= 2*N-1 bits) * The size constraints follow from this. */ class CPartialMerkleTree { protected: /** the total number of transactions in the block */ unsigned int nTransactions; /** node-is-parent-of-matched-txid bits */ std::vector vBits; /** txids and internal hashes */ std::vector vHash; /** flag set when encountering invalid data */ bool fBad; /** * Helper function to efficiently calculate the number of nodes at given * height in the merkle tree. */ unsigned int CalcTreeWidth(int height) { return (nTransactions + (1 << height) - 1) >> height; } /** * Calculate the hash of a node in the merkle tree (at leaf level: the * txid's themselves) */ uint256 CalcHash(int height, unsigned int pos, const std::vector &vTxid); /** * Recursive function that traverses tree nodes, storing the data as bits * and hashes. */ void TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch); /** * Recursive function that traverses tree nodes, consuming the bits and * hashes produced by TraverseAndBuild. It returns the hash of the * respective node and its respective index. */ uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch, std::vector &vnIndex); public: /** serialization implementation */ ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(nTransactions); READWRITE(vHash); std::vector vBytes; if (ser_action.ForRead()) { READWRITE(vBytes); CPartialMerkleTree &us = *(const_cast(this)); us.vBits.resize(vBytes.size() * 8); for (unsigned int p = 0; p < us.vBits.size(); p++) { us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; } us.fBad = false; } else { vBytes.resize((vBits.size() + 7) / 8); for (unsigned int p = 0; p < vBits.size(); p++) { vBytes[p / 8] |= vBits[p] << (p % 8); } READWRITE(vBytes); } } /** * Construct a partial merkle tree from a list of transaction ids, and a * mask that selects a subset of them. */ CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch); CPartialMerkleTree(); /** * Extract the matching txid's represented by this partial merkle tree and * their respective indices within the partial tree. Returns the merkle * root, or 0 in case of failure. */ uint256 ExtractMatches(std::vector &vMatch, std::vector &vnIndex); }; /** * Used to relay blocks as header + vector * to filtered nodes. */ class CMerkleBlock { public: /** Public only for unit testing */ CBlockHeader header; CPartialMerkleTree txn; public: /** Public only for unit testing and relay testing (not relayed) */ std::vector> vMatchedTxn; /** * Create from a CBlock, filtering transactions according to filter. Note * that this will call IsRelevantAndUpdate on the filter for each * transaction, thus the filter will likely be modified. */ CMerkleBlock(const CBlock &block, CBloomFilter &filter); // Create from a CBlock, matching the txids in the set. - CMerkleBlock(const CBlock &block, const std::set &txids); + CMerkleBlock(const CBlock &block, const std::set &txids); CMerkleBlock() {} ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(header); READWRITE(txn); } }; #endif // BITCOIN_MERKLEBLOCK_H diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index bb259f0df..bd15f9290 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1,1196 +1,1196 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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 "base58.h" #include "chain.h" #include "coins.h" #include "config.h" #include "consensus/validation.h" #include "core_io.h" #include "dstencode.h" #include "init.h" #include "keystore.h" #include "merkleblock.h" #include "net.h" #include "policy/policy.h" #include "primitives/transaction.h" #include "rpc/server.h" #include "rpc/tojson.h" #include "script/script.h" #include "script/script_error.h" #include "script/sign.h" #include "script/standard.h" #include "txmempool.h" #include "uint256.h" #include "utilstrencodings.h" #include "validation.h" #ifdef ENABLE_WALLET #include "wallet/rpcwallet.h" #include "wallet/wallet.h" #endif #include #include void ScriptPubKeyToJSON(const Config &config, const CScript &scriptPubKey, UniValue &out, bool fIncludeHex) { txnouttype type; std::vector addresses; int nRequired; out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); if (fIncludeHex) { out.push_back( Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); } if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { out.push_back(Pair("type", GetTxnOutputType(type))); return; } out.push_back(Pair("reqSigs", nRequired)); out.push_back(Pair("type", GetTxnOutputType(type))); UniValue a(UniValue::VARR); for (const CTxDestination &addr : addresses) { a.push_back(EncodeDestination(addr)); } out.push_back(Pair("addresses", a)); } void TxToJSON(const Config &config, const CTransaction &tx, const uint256 hashBlock, UniValue &entry) { entry.push_back(Pair("txid", tx.GetId().GetHex())); entry.push_back(Pair("hash", tx.GetHash().GetHex())); entry.push_back(Pair( "size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); UniValue vin(UniValue::VARR); for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxIn &txin = tx.vin[i]; UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) { in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); } else { in.push_back(Pair("txid", txin.prevout.GetTxId().GetHex())); in.push_back(Pair("vout", int64_t(txin.prevout.GetN()))); UniValue o(UniValue::VOBJ); o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); o.push_back(Pair( "hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); in.push_back(Pair("scriptSig", o)); } in.push_back(Pair("sequence", (int64_t)txin.nSequence)); vin.push_back(in); } entry.push_back(Pair("vin", vin)); UniValue vout(UniValue::VARR); for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut &txout = tx.vout[i]; UniValue out(UniValue::VOBJ); out.push_back(Pair("value", ValueFromAmount(txout.nValue))); out.push_back(Pair("n", (int64_t)i)); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(config, txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); vout.push_back(out); } entry.push_back(Pair("vout", vout)); if (!hashBlock.IsNull()) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex *pindex = (*mi).second; if (chainActive.Contains(pindex)) { entry.push_back( Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); entry.push_back(Pair("time", pindex->GetBlockTime())); entry.push_back(Pair("blocktime", pindex->GetBlockTime())); } else { entry.push_back(Pair("confirmations", 0)); } } } } static UniValue getrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getrawtransaction \"txid\" ( verbose )\n" "\nNOTE: By default this function only works for mempool " "transactions. If the -txindex option is\n" "enabled, it also works for blockchain transactions.\n" "DEPRECATED: for now, it also works for transactions with unspent " "outputs.\n" "\nReturn the raw transaction data.\n" "\nIf verbose is 'true', returns an Object with information about " "'txid'.\n" "If verbose is 'false' or omitted, returns a string that is " "serialized, hex-encoded data for 'txid'.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "2. verbose (bool, optional, default=false) If false, return " "a string, otherwise return a json object\n" "\nResult (if verbose is not set or set to false):\n" "\"data\" (string) The serialized, hex-encoded data for " "'txid'\n" "\nResult (if verbose is set to true):\n" "{\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded " "data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as " "provided)\n" " \"hash\" : \"id\", (string) The transaction hash " "(differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The serialized transaction " "size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" " \"vout\": n, (numeric) \n" " \"scriptSig\": { (json object) The script\n" " \"asm\": \"asm\", (string) asm\n" " \"hex\": \"hex\" (string) hex\n" " },\n" " \"sequence\": n (numeric) The script sequence number\n" " }\n" " ,...\n" " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" " \"hex\" : \"hex\", (string) the hex\n" " \"reqSigs\" : n, (numeric) The required sigs\n" " \"type\" : \"pubkeyhash\", (string) The type, eg " "'pubkeyhash'\n" " \"addresses\" : [ (json array of string)\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ]\n" " }\n" " }\n" " ,...\n" " ],\n" " \"blockhash\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The confirmations\n" " \"time\" : ttt, (numeric) The transaction time in " "seconds since epoch (Jan 1 1970 GMT)\n" " \"blocktime\" : ttt (numeric) The block time in seconds " "since epoch (Jan 1 1970 GMT)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + HelpExampleRpc("getrawtransaction", "\"mytxid\", true")); } LOCK(cs_main); uint256 hash = ParseHashV(request.params[0], "parameter 1"); // Accept either a bool (true) or a num (>=1) to indicate verbose output. bool fVerbose = false; if (request.params.size() > 1) { if (request.params[1].isNum()) { if (request.params[1].get_int() != 0) { fVerbose = true; } } else if (request.params[1].isBool()) { if (request.params[1].isTrue()) { fVerbose = true; } } else { throw JSONRPCError( RPC_TYPE_ERROR, "Invalid type provided. Verbose parameter must be a boolean."); } } CTransactionRef tx; uint256 hashBlock; if (!GetTransaction(config, hash, tx, hashBlock, true)) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" : "No such mempool transaction. Use -txindex " "to enable blockchain transaction queries") + ". Use gettransaction for wallet transactions."); } std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); if (!fVerbose) { return strHex; } UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", strHex)); TxToJSON(config, *tx, hashBlock, result); return result; } static UniValue gettxoutproof(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) { throw std::runtime_error( "gettxoutproof [\"txid\",...] ( blockhash )\n" "\nReturns a hex-encoded proof that \"txid\" was included in a " "block.\n" "\nNOTE: By default this function only works sometimes. This is " "when there is an\n" "unspent output in the utxo for this transaction. To make it " "always work,\n" "you need to maintain a transaction index, using the -txindex " "command line option or\n" "specify the block in which the transaction is included manually " "(by blockhash).\n" "\nArguments:\n" "1. \"txids\" (string) A json array of txids to filter\n" " [\n" " \"txid\" (string) A transaction hash\n" " ,...\n" " ]\n" "2. \"blockhash\" (string, optional) If specified, looks for " "txid in the block with this hash\n" "\nResult:\n" "\"data\" (string) A string that is a serialized, " "hex-encoded data for the proof.\n"); } - std::set setTxids; - uint256 oneTxid; + std::set setTxIds; + TxId oneTxId; UniValue txids = request.params[0].get_array(); for (unsigned int idx = 0; idx < txids.size(); idx++) { - const UniValue &txid = txids[idx]; - if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) { + const UniValue &utxid = txids[idx]; + if (utxid.get_str().length() != 64 || !IsHex(utxid.get_str())) { throw JSONRPCError(RPC_INVALID_PARAMETER, - std::string("Invalid txid ") + txid.get_str()); + std::string("Invalid txid ") + utxid.get_str()); } - uint256 hash(uint256S(txid.get_str())); - if (setTxids.count(hash)) { + TxId txid(uint256S(utxid.get_str())); + if (setTxIds.count(txid)) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + - txid.get_str()); + utxid.get_str()); } - setTxids.insert(hash); - oneTxid = hash; + setTxIds.insert(txid); + oneTxId = txid; } LOCK(cs_main); CBlockIndex *pblockindex = nullptr; uint256 hashBlock; if (request.params.size() > 1) { hashBlock = uint256S(request.params[1].get_str()); if (!mapBlockIndex.count(hashBlock)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); pblockindex = mapBlockIndex[hashBlock]; } else { // Loop through txids and try to find which block they're in. Exit loop // once a block is found. - for (const auto &tx : setTxids) { - const Coin &coin = AccessByTxid(*pcoinsTip, tx); + for (const auto &txid : setTxIds) { + const Coin &coin = AccessByTxid(*pcoinsTip, txid); if (!coin.IsSpent()) { pblockindex = chainActive[coin.GetHeight()]; break; } } } if (pblockindex == nullptr) { CTransactionRef tx; - if (!GetTransaction(config, oneTxid, tx, hashBlock, false) || + if (!GetTransaction(config, oneTxId, tx, hashBlock, false) || hashBlock.IsNull()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); } if (!mapBlockIndex.count(hashBlock)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); } pblockindex = mapBlockIndex[hashBlock]; } CBlock block; if (!ReadBlockFromDisk(block, pblockindex, config)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } unsigned int ntxFound = 0; for (const auto &tx : block.vtx) { - if (setTxids.count(tx->GetId())) { + if (setTxIds.count(tx->GetId())) { ntxFound++; } } - if (ntxFound != setTxids.size()) { + if (ntxFound != setTxIds.size()) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block"); } CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); - CMerkleBlock mb(block, setTxids); + CMerkleBlock mb(block, setTxIds); ssMB << mb; std::string strHex = HexStr(ssMB.begin(), ssMB.end()); return strHex; } static UniValue verifytxoutproof(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "verifytxoutproof \"proof\"\n" "\nVerifies that a proof points to a transaction in a block, " "returning the transaction it commits to\n" "and throwing an RPC error if the block is not in our best chain\n" "\nArguments:\n" "1. \"proof\" (string, required) The hex-encoded proof " "generated by gettxoutproof\n" "\nResult:\n" "[\"txid\"] (array, strings) The txid(s) which the proof " "commits to, or empty array if the proof is invalid\n"); } CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock merkleBlock; ssMB >> merkleBlock; UniValue res(UniValue::VARR); std::vector vMatch; std::vector vIndex; if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) { return res; } LOCK(cs_main); if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } for (const uint256 &hash : vMatch) { res.push_back(hash.GetHex()); } return res; } static UniValue createrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { throw std::runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] " "{\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" "\nCreate a transaction spending the given inputs and creating new " "outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n" "\nArguments:\n" "1. \"inputs\" (array, required) A json array of " "json objects\n" " [\n" " {\n" " \"txid\":\"id\", (string, required) The transaction " "id\n" " \"vout\":n, (numeric, required) The output " "number\n" " \"sequence\":n (numeric, optional) The sequence " "number\n" " } \n" " ,...\n" " ]\n" "2. \"outputs\" (object, required) a json object " "with outputs\n" " {\n" " \"address\": x.xxx, (numeric or string, required) The " "key is the bitcoin address, the numeric value (can be string) is " "the " + CURRENCY_UNIT + " amount\n" " \"data\": \"hex\" (string, required) The key is " "\"data\", the value is hex encoded data\n" " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw " "locktime. Non-0 value also locktime-activates inputs\n" "\nResult:\n" "\"transaction\" (string) hex string of the " "transaction\n" "\nExamples:\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" " "\"{\\\"address\\\":0.01}\"") + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" " "\"{\\\"data\\\":\\\"00010203\\\"}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", " "\"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", " "\"{\\\"data\\\":\\\"00010203\\\"}\"")); } RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM}, true); if (request.params[0].isNull() || request.params[1].isNull()) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); } UniValue inputs = request.params[0].get_array(); UniValue sendTo = request.params[1].get_obj(); CMutableTransaction rawTx; if (request.params.size() > 2 && !request.params[2].isNull()) { int64_t nLockTime = request.params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); } rawTx.nLockTime = nLockTime; } for (size_t idx = 0; idx < inputs.size(); idx++) { const UniValue &input = inputs[idx]; const UniValue &o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const UniValue &vout_v = find_value(o, "vout"); if (!vout_v.isNum()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); } int nOutput = vout_v.get_int(); if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); } uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); // Set the sequence number if passed in the parameters object. const UniValue &sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); } nSequence = uint32_t(seqNr64); } CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); } std::set destinations; std::vector addrList = sendTo.getKeys(); for (const std::string &name_ : addrList) { if (name_ == "data") { std::vector data = ParseHexV(sendTo[name_].getValStr(), "Data"); CTxOut out(Amount(0), CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { CTxDestination destination = DecodeDestination(name_, config.GetChainParams()); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } if (!destinations.insert(destination).second) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); } CScript scriptPubKey = GetScriptForDestination(destination); Amount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); } } return EncodeHexTx(CTransaction(rawTx)); } static UniValue decoderawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "decoderawtransaction \"hexstring\"\n" "\nReturn a JSON object representing the serialized, hex-encoded " "transaction.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex " "string\n" "\nResult:\n" "{\n" " \"txid\" : \"id\", (string) The transaction id\n" " \"hash\" : \"id\", (string) The transaction hash " "(differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" " \"vout\": n, (numeric) The output number\n" " \"scriptSig\": { (json object) The script\n" " \"asm\": \"asm\", (string) asm\n" " \"hex\": \"hex\" (string) hex\n" " },\n" " \"sequence\": n (numeric) The script sequence number\n" " }\n" " ,...\n" " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" " \"hex\" : \"hex\", (string) the hex\n" " \"reqSigs\" : n, (numeric) The required sigs\n" " \"type\" : \"pubkeyhash\", (string) The type, eg " "'pubkeyhash'\n" " \"addresses\" : [ (json array of string)\n" " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) " "bitcoin address\n" " ,...\n" " ]\n" " }\n" " }\n" " ,...\n" " ],\n" "}\n" "\nExamples:\n" + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + HelpExampleRpc("decoderawtransaction", "\"hexstring\"")); } LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR}); CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } UniValue result(UniValue::VOBJ); TxToJSON(config, CTransaction(std::move(mtx)), uint256(), result); return result; } static UniValue decodescript(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "decodescript \"hexstring\"\n" "\nDecode a hex-encoded script.\n" "\nArguments:\n" "1. \"hexstring\" (string) the hex encoded script\n" "\nResult:\n" "{\n" " \"asm\":\"asm\", (string) Script public key\n" " \"hex\":\"hex\", (string) hex encoded public key\n" " \"type\":\"type\", (string) The output type\n" " \"reqSigs\": n, (numeric) The required signatures\n" " \"addresses\": [ (json array of string)\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ],\n" " \"p2sh\",\"address\" (string) address of P2SH script wrapping " "this redeem script (not returned if the script is already a " "P2SH).\n" "}\n" "\nExamples:\n" + HelpExampleCli("decodescript", "\"hexstring\"") + HelpExampleRpc("decodescript", "\"hexstring\"")); } RPCTypeCheck(request.params, {UniValue::VSTR}); UniValue r(UniValue::VOBJ); CScript script; if (request.params[0].get_str().size() > 0) { std::vector scriptData( ParseHexV(request.params[0], "argument")); script = CScript(scriptData.begin(), scriptData.end()); } else { // Empty scripts are valid. } ScriptPubKeyToJSON(config, script, r, false); UniValue type; type = find_value(r, "type"); if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); } return r; } /** * Pushes a JSON object for script verification or signing errors to vErrorsRet. */ static void TxInErrorToJSON(const CTxIn &txin, UniValue &vErrorsRet, const std::string &strMessage) { UniValue entry(UniValue::VOBJ); entry.push_back(Pair("txid", txin.prevout.GetTxId().ToString())); entry.push_back(Pair("vout", uint64_t(txin.prevout.GetN()))); entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); entry.push_back(Pair("sequence", uint64_t(txin.nSequence))); entry.push_back(Pair("error", strMessage)); vErrorsRet.push_back(entry); } static UniValue signrawtransaction(const Config &config, const JSONRPCRequest &request) { #ifdef ENABLE_WALLET CWallet *const pwallet = GetWalletForJSONRPCRequest(request); #endif if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) { throw std::runtime_error( "signrawtransaction \"hexstring\" ( " "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\"," "\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype " ")\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n" "The second optional argument (may be null) is an array of " "previous transaction outputs that\n" "this transaction depends on but may not yet be in the block " "chain.\n" "The third optional argument (may be null) is an array of " "base58-encoded private\n" "keys that, if given, will be the only keys used to sign the " "transaction.\n" #ifdef ENABLE_WALLET + HelpRequiringPassphrase(pwallet) + "\n" #endif "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex " "string\n" "2. \"prevtxs\" (string, optional) An json array of previous " "dependent transaction outputs\n" " [ (json array of json objects, or 'null' if " "none provided)\n" " {\n" " \"txid\":\"id\", (string, required) The " "transaction id\n" " \"vout\":n, (numeric, required) The " "output number\n" " \"scriptPubKey\": \"hex\", (string, required) script " "key\n" " \"redeemScript\": \"hex\", (string, required for P2SH " "or P2WSH) redeem script\n" " \"amount\": value (numeric, required) The " "amount spent\n" " }\n" " ,...\n" " ]\n" "3. \"privkeys\" (string, optional) A json array of " "base58-encoded private keys for signing\n" " [ (json array of strings, or 'null' if none " "provided)\n" " \"privatekey\" (string) private key in base58-encoding\n" " ,...\n" " ]\n" "4. \"sighashtype\" (string, optional, default=ALL) The " "signature hash type. Must be one of\n" " \"ALL\"\n" " \"NONE\"\n" " \"SINGLE\"\n" " \"ALL|ANYONECANPAY\"\n" " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\"\n" " \"ALL|FORKID\"\n" " \"NONE|FORKID\"\n" " \"SINGLE|FORKID\"\n" " \"ALL|FORKID|ANYONECANPAY\"\n" " \"NONE|FORKID|ANYONECANPAY\"\n" " \"SINGLE|FORKID|ANYONECANPAY\"\n" "\nResult:\n" "{\n" " \"hex\" : \"value\", (string) The hex-encoded raw " "transaction with signature(s)\n" " \"complete\" : true|false, (boolean) If the transaction has a " "complete set of signatures\n" " \"errors\" : [ (json array of objects) Script " "verification errors (if there are any)\n" " {\n" " \"txid\" : \"hash\", (string) The hash of the " "referenced, previous transaction\n" " \"vout\" : n, (numeric) The index of the " "output to spent and used as input\n" " \"scriptSig\" : \"hex\", (string) The hex-encoded " "signature script\n" " \"sequence\" : n, (numeric) Script sequence " "number\n" " \"error\" : \"text\" (string) Verification or " "signing error related to the input\n" " }\n" " ,...\n" " ]\n" "}\n" "\nExamples:\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + HelpExampleRpc("signrawtransaction", "\"myhex\"")); } #ifdef ENABLE_WALLET LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else LOCK(cs_main); #endif RPCTypeCheck( request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); std::vector txData(ParseHexV(request.params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); std::vector txVariants; while (!ssData.empty()) { try { CMutableTransaction tx; ssData >> tx; txVariants.push_back(tx); } catch (const std::exception &) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } } if (txVariants.empty()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); } // mergedTx will end up with all the signatures; it starts as a clone of the // rawtx: CMutableTransaction mergedTx(txVariants[0]); // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, mempool); // Temporarily switch cache backend to db+mempool view. view.SetBackend(viewMempool); for (const CTxIn &txin : mergedTx.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); } bool fGivenKeys = false; CBasicKeyStore tempKeystore; if (request.params.size() > 2 && !request.params[2].isNull()) { fGivenKeys = true; UniValue keys = request.params[2].get_array(); for (size_t idx = 0; idx < keys.size(); idx++) { UniValue k = keys[idx]; CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(k.get_str()); if (!fGood) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); } CKey key = vchSecret.GetKey(); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); } tempKeystore.AddKey(key); } } #ifdef ENABLE_WALLET else if (pwallet) { EnsureWalletIsUnlocked(pwallet); } #endif // Add previous txouts given in the RPC call: if (request.params.size() > 1 && !request.params[1].isNull()) { UniValue prevTxs = request.params[1].get_array(); for (size_t idx = 0; idx < prevTxs.size(); idx++) { const UniValue &p = prevTxs[idx]; if (!p.isObject()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with " "{\"txid'\",\"vout\",\"scriptPubKey\"}"); } UniValue prevOut = p.get_obj(); RPCTypeCheckObj(prevOut, { {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, // "amount" is also required but check is done // below due to UniValue::VNUM erroneously // not accepting quoted numerics // (which are valid JSON) }); uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); } COutPoint out(txid, nOut); std::vector pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { const Coin &coin = view.AccessCoin(out); if (!coin.IsSpent() && coin.GetTxOut().scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) + "\nvs:\n" + ScriptToAsmStr(scriptPubKey); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } CTxOut txout; txout.scriptPubKey = scriptPubKey; txout.nValue = Amount(0); if (prevOut.exists("amount")) { txout.nValue = AmountFromValue(find_value(prevOut, "amount")); } else { // amount param is required in replay-protected txs. // Note that we must check for its presence here rather // than use RPCTypeCheckObj() above, since UniValue::VNUM // parser incorrectly parses numerics with quotes, eg // "3.12" as a string when JSON allows it to also parse // as numeric. And we have to accept numerics with quotes // because our own dogfood (our rpc results) always // produces decimal numbers that are quoted // eg getbalance returns "3.14152" rather than 3.14152 throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); } view.AddCoin(out, Coin(txout, 1, false), true); } // If redeemScript given and not using the local wallet (private // keys given), add redeemScript to the tempKeystore so it can be // signed: if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { RPCTypeCheckObj( prevOut, { {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, {"redeemScript", UniValueType(UniValue::VSTR)}, }); UniValue v = find_value(prevOut, "redeemScript"); if (!v.isNull()) { std::vector rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } } } #ifdef ENABLE_WALLET const CKeyStore &keystore = ((fGivenKeys || !pwallet) ? tempKeystore : *pwallet); #else const CKeyStore &keystore = tempKeystore; #endif SigHashType sigHashType = SigHashType().withForkId(); if (request.params.size() > 3 && !request.params[3].isNull()) { static std::map mapSigHashValues = { {"ALL", SIGHASH_ALL}, {"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY}, {"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID}, {"ALL|FORKID|ANYONECANPAY", SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, {"NONE", SIGHASH_NONE}, {"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY}, {"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID}, {"NONE|FORKID|ANYONECANPAY", SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, {"SINGLE", SIGHASH_SINGLE}, {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY}, {"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID}, {"SINGLE|FORKID|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, }; std::string strHashType = request.params[3].get_str(); if (!mapSigHashValues.count(strHashType)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); } sigHashType = SigHashType(mapSigHashValues[strHashType]); if (!sigHashType.hasForkId()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Signature must use SIGHASH_FORKID"); } } // Script verification errors. UniValue vErrors(UniValue::VARR); // Use CTransaction for the constant parts of the transaction to avoid // rehashing. const CTransaction txConst(mergedTx); // Sign what we can: for (size_t i = 0; i < mergedTx.vin.size(); i++) { CTxIn &txin = mergedTx.vin[i]; const Coin &coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); continue; } const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; const Amount amount = coin.GetTxOut().nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || (i < mergedTx.vout.size())) { ProduceSignature(MutableTransactionSignatureCreator( &keystore, &mergedTx, i, amount, sigHashType), prevPubKey, sigdata); } // ... and merge in other signatures: for (const CMutableTransaction &txv : txVariants) { if (txv.vin.size() > i) { sigdata = CombineSignatures( prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); } } UpdateTransaction(mergedTx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript( txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", EncodeHexTx(CTransaction(mergedTx)))); result.push_back(Pair("complete", fComplete)); if (!vErrors.empty()) { result.push_back(Pair("errors", vErrors)); } return result; } static UniValue sendrawtransaction(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "sendrawtransaction \"hexstring\" ( allowhighfees )\n" "\nSubmits raw transaction (serialized, hex-encoded) to local node " "and network.\n" "\nAlso see createrawtransaction and signrawtransaction calls.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw " "transaction)\n" "2. allowhighfees (boolean, optional, default=false) Allow high " "fees\n" "\nResult:\n" "\"hex\" (string) The transaction hash in hex\n" "\nExamples:\n" "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : " "\\\"mytxid\\\",\\\"vout\\\":0}]\" " "\"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + "\nAs a json rpc call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")); } LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); // parse hex string from parameter CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const uint256 &txid = tx->GetId(); bool fLimitFree = false; Amount nMaxRawTxFee = maxTxFee; if (request.params.size() > 1 && request.params[1].get_bool()) { nMaxRawTxFee = Amount(0); } CCoinsViewCache &view = *pcoinsTip; bool fHaveChain = false; for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o)); fHaveChain = !existingCoin.IsSpent(); } bool fHaveMempool = mempool.exists(txid); if (!fHaveMempool && !fHaveChain) { // Push to local node and sync with wallets. CValidationState state; bool fMissingInputs; if (!AcceptToMemoryPool(config, mempool, state, std::move(tx), fLimitFree, &fMissingInputs, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { if (fMissingInputs) { throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); } throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } CInv inv(MSG_TX, txid); g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); return txid.GetHex(); } // clang-format off static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // ------------------- ------------------------ ---------------------- ---------- { "rawtransactions", "getrawtransaction", getrawtransaction, true, {"txid","verbose"} }, { "rawtransactions", "createrawtransaction", createrawtransaction, true, {"inputs","outputs","locktime"} }, { "rawtransactions", "decoderawtransaction", decoderawtransaction, true, {"hexstring"} }, { "rawtransactions", "decodescript", decodescript, true, {"hexstring"} }, { "rawtransactions", "sendrawtransaction", sendrawtransaction, false, {"hexstring","allowhighfees"} }, { "rawtransactions", "signrawtransaction", signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ { "blockchain", "gettxoutproof", gettxoutproof, true, {"txids", "blockhash"} }, { "blockchain", "verifytxoutproof", verifytxoutproof, true, {"proof"} }, }; // clang-format on void RegisterRawTransactionRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } }