aboutsummaryrefslogtreecommitdiff
path: root/src/rpcrawtransaction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpcrawtransaction.cpp')
-rw-r--r--src/rpcrawtransaction.cpp170
1 files changed, 160 insertions, 10 deletions
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index c979217a13..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\": n (numeric) if transaction has a complete set of signature (0 if not)\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");