Browse Source

Merge pull request #5937

a71ab10 QA: add RPC tests for error reporting of "signrawtransaction" (dexX7)
8ac2a4e RPC: show script verification errors in "signrawtransaction" result (dexX7)
0.13
Wladimir J. van der Laan 10 years ago
parent
commit
40f5e8dc2a
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 3
      qa/pull-tester/rpc-tests.sh
  2. 109
      qa/rpc-tests/signrawtransactions.py
  3. 45
      src/rpcrawtransaction.cpp

3
qa/pull-tester/rpc-tests.sh

@ -22,6 +22,7 @@ testScripts=(
'txn_doublespend.py' 'txn_doublespend.py'
'txn_doublespend.py --mineblock' 'txn_doublespend.py --mineblock'
'getchaintips.py' 'getchaintips.py'
'rawtransactions.py'
'rest.py' 'rest.py'
'mempool_spendcoinbase.py' 'mempool_spendcoinbase.py'
'mempool_coinbase_spends.py' 'mempool_coinbase_spends.py'
@ -29,7 +30,7 @@ testScripts=(
'zapwallettxes.py' 'zapwallettxes.py'
'proxy_test.py' 'proxy_test.py'
'merkle_blocks.py' 'merkle_blocks.py'
'rawtransactions.py' 'signrawtransactions.py'
# 'forknotify.py' # 'forknotify.py'
'maxblocksinflight.py' 'maxblocksinflight.py'
'invalidblockrequest.py' 'invalidblockrequest.py'

109
qa/rpc-tests/signrawtransactions.py

@ -0,0 +1,109 @@
#!/usr/bin/env python2
# Copyright (c) 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.
from test_framework import BitcoinTestFramework
from util import *
class SignRawTransactionsTest(BitcoinTestFramework):
"""Tests transaction signing via RPC command "signrawtransaction"."""
def setup_chain(self):
print('Initializing test directory ' + self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 1)
def setup_network(self, split=False):
self.nodes = start_nodes(1, self.options.tmpdir)
self.is_network_split = False
def successful_signing_test(self):
"""Creates and signs a valid raw transaction with one input.
Expected results:
1) The transaction has a complete set of signatures
2) No script verification error occurred"""
privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N']
inputs = [
# Valid pay-to-pubkey script
{'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0,
'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'}
]
outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
rawTxSigned = self.nodes[0].signrawtransaction(rawTx, inputs, privKeys)
# 1) The transaction has a complete set of signatures
assert 'complete' in rawTxSigned
assert_equal(rawTxSigned['complete'], True)
# 2) No script verification error occurred
assert 'errors' not in rawTxSigned
def script_verification_error_test(self):
"""Creates and signs a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script.
Expected results:
3) The transaction has no complete set of signatures
4) Two script verification errors occurred
5) Script verification errors have certain properties ("txid", "vout", "scriptSig", "sequence", "error")
6) The verification errors refer to the invalid (vin 1) and missing input (vin 2)"""
privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N']
inputs = [
# Valid pay-to-pubkey script
{'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0},
# Invalid script
{'txid': '5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547', 'vout': 7},
# Missing scriptPubKey
{'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 1},
]
scripts = [
# Valid pay-to-pubkey script
{'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0,
'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'},
# Invalid script
{'txid': '5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547', 'vout': 7,
'scriptPubKey': 'badbadbadbad'}
]
outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys)
# 3) The transaction has no complete set of signatures
assert 'complete' in rawTxSigned
assert_equal(rawTxSigned['complete'], False)
# 4) Two script verification errors occurred
assert 'errors' in rawTxSigned
assert_equal(len(rawTxSigned['errors']), 2)
# 5) Script verification errors have certain properties
assert 'txid' in rawTxSigned['errors'][0]
assert 'vout' in rawTxSigned['errors'][0]
assert 'scriptSig' in rawTxSigned['errors'][0]
assert 'sequence' in rawTxSigned['errors'][0]
assert 'error' in rawTxSigned['errors'][0]
# 6) The verification errors refer to the invalid (vin 1) and missing input (vin 2)
assert_equal(rawTxSigned['errors'][0]['txid'], inputs[1]['txid'])
assert_equal(rawTxSigned['errors'][0]['vout'], inputs[1]['vout'])
assert_equal(rawTxSigned['errors'][1]['txid'], inputs[2]['txid'])
assert_equal(rawTxSigned['errors'][1]['vout'], inputs[2]['vout'])
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
if __name__ == '__main__':
SignRawTransactionsTest().main()

45
src/rpcrawtransaction.cpp

@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto // 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 // 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.
@ -13,6 +13,7 @@
#include "net.h" #include "net.h"
#include "rpcserver.h" #include "rpcserver.h"
#include "script/script.h" #include "script/script.h"
#include "script/script_error.h"
#include "script/sign.h" #include "script/sign.h"
#include "script/standard.h" #include "script/standard.h"
#include "uint256.h" #include "uint256.h"
@ -491,6 +492,18 @@ Value decodescript(const Array& params, bool fHelp)
return r; 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) Value signrawtransaction(const Array& params, bool fHelp)
{ {
if (fHelp || params.size() < 1 || params.size() > 4) if (fHelp || params.size() < 1 || params.size() > 4)
@ -532,8 +545,18 @@ Value signrawtransaction(const Array& params, bool fHelp)
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"hex\": \"value\", (string) The raw transaction with signature(s) (hex-encoded string)\n" " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
" \"complete\": true|false (boolean) if transaction has a complete set of signature\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" "}\n"
"\nExamples:\n" "\nExamples:\n"
@ -568,7 +591,6 @@ Value signrawtransaction(const Array& params, bool fHelp)
// mergedTx will end up with all the signatures; it // mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx: // starts as a clone of the rawtx:
CMutableTransaction mergedTx(txVariants[0]); CMutableTransaction mergedTx(txVariants[0]);
bool fComplete = true;
// Fetch previous transactions (inputs): // Fetch previous transactions (inputs):
CCoinsView viewDummy; CCoinsView viewDummy;
@ -683,12 +705,15 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Script verification errors
Array vErrors;
// Sign what we can: // Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i]; CTxIn& txin = mergedTx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash); const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
fComplete = false; TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
continue; continue;
} }
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
@ -702,13 +727,19 @@ Value signrawtransaction(const Array& params, bool fHelp)
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
} }
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) ScriptError serror = SCRIPT_ERR_OK;
fComplete = false; if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
} }
bool fComplete = vErrors.empty();
Object result; Object result;
result.push_back(Pair("hex", EncodeHexTx(mergedTx))); result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
result.push_back(Pair("complete", fComplete)); result.push_back(Pair("complete", fComplete));
if (!vErrors.empty()) {
result.push_back(Pair("errors", vErrors));
}
return result; return result;
} }

Loading…
Cancel
Save