diff options
Diffstat (limited to 'src/rpcrawtransaction.cpp')
-rw-r--r-- | src/rpcrawtransaction.cpp | 170 |
1 files changed, 160 insertions, 10 deletions
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 8393a8502e..e84d2693a2 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,9 +9,11 @@ #include "init.h" #include "keystore.h" #include "main.h" +#include "merkleblock.h" #include "net.h" #include "rpcserver.h" #include "script/script.h" +#include "script/script_error.h" #include "script/sign.h" #include "script/standard.h" #include "uint256.h" @@ -193,6 +195,119 @@ Value getrawtransaction(const Array& params, bool fHelp) return result; } +Value gettxoutproof(const Array& params, bool fHelp) +{ + if (fHelp || (params.size() != 1 && params.size() != 2)) + throw 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 in manually (by blockhash).\n" + "\nReturn the raw transaction data.\n" + "\nArguments:\n" + "1. \"txids\" (string) A json array of txids to filter\n" + " [\n" + " \"txid\" (string) A transaction hash\n" + " ,...\n" + " ]\n" + "2. \"block hash\" (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" + ); + + set<uint256> setTxids; + uint256 oneTxid; + Array txids = params[0].get_array(); + BOOST_FOREACH(Value& txid, txids) { + if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); + uint256 hash(uint256S(txid.get_str())); + if (setTxids.count(hash)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); + setTxids.insert(hash); + oneTxid = hash; + } + + LOCK(cs_main); + + CBlockIndex* pblockindex = NULL; + + uint256 hashBlock; + if (params.size() > 1) + { + hashBlock = uint256S(params[1].get_str()); + if (!mapBlockIndex.count(hashBlock)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + pblockindex = mapBlockIndex[hashBlock]; + } else { + CCoins coins; + if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) + pblockindex = chainActive[coins.nHeight]; + } + + if (pblockindex == NULL) + { + CTransaction tx; + if (!GetTransaction(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)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + unsigned int ntxFound = 0; + BOOST_FOREACH(const CTransaction&tx, block.vtx) + if (setTxids.count(tx.GetHash())) + ntxFound++; + if (ntxFound != setTxids.size()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); + + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); + CMerkleBlock mb(block, setTxids); + ssMB << mb; + std::string strHex = HexStr(ssMB.begin(), ssMB.end()); + return strHex; +} + +Value verifytxoutproof(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw 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(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); + CMerkleBlock merkleBlock; + ssMB >> merkleBlock; + + Array res; + + vector<uint256> vMatch; + if (merkleBlock.txn.ExtractMatches(vMatch) != 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"); + + BOOST_FOREACH(const uint256& hash, vMatch) + res.push_back(hash.GetHex()); + return res; +} + Value createrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) @@ -377,6 +492,18 @@ Value decodescript(const Array& params, bool fHelp) return r; } +/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ +static void TxInErrorToJSON(const CTxIn& txin, Array& vErrorsRet, const std::string& strMessage) +{ + Object entry; + entry.push_back(Pair("txid", txin.prevout.hash.ToString())); + entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); + 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); +} + Value signrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 4) @@ -418,8 +545,18 @@ Value signrawtransaction(const Array& params, bool fHelp) "\nResult:\n" "{\n" - " \"hex\": \"value\", (string) The raw transaction with signature(s) (hex-encoded string)\n" - " \"complete\": true|false (boolean) if transaction has a complete set of signature\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" @@ -454,7 +591,6 @@ Value signrawtransaction(const Array& params, bool fHelp) // mergedTx will end up with all the signatures; it // starts as a clone of the rawtx: CMutableTransaction mergedTx(txVariants[0]); - bool fComplete = true; // Fetch previous transactions (inputs): CCoinsView viewDummy; @@ -569,12 +705,15 @@ Value signrawtransaction(const Array& params, bool fHelp) bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + // Script verification errors + Array vErrors; + // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; const CCoins* coins = view.AccessCoins(txin.prevout.hash); if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { - fComplete = false; + TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; @@ -588,13 +727,19 @@ Value signrawtransaction(const Array& params, bool fHelp) BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) - fComplete = false; + ScriptError serror = SCRIPT_ERR_OK; + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } } + bool fComplete = vErrors.empty(); Object result; result.push_back(Pair("hex", EncodeHexTx(mergedTx))); result.push_back(Pair("complete", fComplete)); + if (!vErrors.empty()) { + result.push_back(Pair("errors", vErrors)); + } return result; } @@ -642,11 +787,16 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; - if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) { - if(state.IsInvalid()) + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { + if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); - else + } 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"); |