Merge pull request #5264
af3208b
Resolve issue 3166. These changes decode valid SIGHASH types on signatures in assembly (asm) representations of scriptSig scripts. This squashed commit incorporates substantial helpful feedback from jtimon, laanwj, and sipa. (mruddy)
This commit is contained in:
commit
48efbdbe98
12 changed files with 206 additions and 49 deletions
|
@ -124,6 +124,33 @@ git merge commit are mentioned.
|
||||||
|
|
||||||
### RPC and REST
|
### RPC and REST
|
||||||
|
|
||||||
|
Asm representations of scriptSig signatures now contain SIGHASH type decodes
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The `asm` property of each scriptSig now contains the decoded signature hash
|
||||||
|
type for each signature that provides a valid defined hash type.
|
||||||
|
|
||||||
|
The following items contain assembly representations of scriptSig signatures
|
||||||
|
and are affected by this change:
|
||||||
|
|
||||||
|
- RPC `getrawtransaction`
|
||||||
|
- RPC `decoderawtransaction`
|
||||||
|
- REST `/rest/tx/` (JSON format)
|
||||||
|
- REST `/rest/block/` (JSON format when including extended tx details)
|
||||||
|
- `bitcoin-tx -json`
|
||||||
|
|
||||||
|
For example, the `scriptSig.asm` property of a transaction input that
|
||||||
|
previously showed an assembly representation of:
|
||||||
|
|
||||||
|
304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001
|
||||||
|
|
||||||
|
now shows as:
|
||||||
|
|
||||||
|
304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c5090[ALL]
|
||||||
|
|
||||||
|
Note that the output of the RPC `decodescript` did not change because it is
|
||||||
|
configured specifically to process scriptPubKey and not scriptSig scripts.
|
||||||
|
|
||||||
### Configuration and command-line options
|
### Configuration and command-line options
|
||||||
|
|
||||||
### Block and transaction handling
|
### Block and transaction handling
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import *
|
from test_framework.util import *
|
||||||
|
from test_framework.mininode import *
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from cStringIO import StringIO
|
||||||
|
|
||||||
class DecodeScriptTest(BitcoinTestFramework):
|
class DecodeScriptTest(BitcoinTestFramework):
|
||||||
"""Tests decoding scripts via RPC command "decodescript"."""
|
"""Tests decoding scripts via RPC command "decodescript"."""
|
||||||
|
@ -107,10 +110,77 @@ class DecodeScriptTest(BitcoinTestFramework):
|
||||||
rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
|
rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
|
||||||
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_NOP2 OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
|
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_NOP2 OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
|
||||||
|
|
||||||
|
def decoderawtransaction_asm_sighashtype(self):
|
||||||
|
"""Tests decoding scripts via RPC command "decoderawtransaction".
|
||||||
|
|
||||||
|
This test is in with the "decodescript" tests because they are testing the same "asm" script decodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# this test case uses a random plain vanilla mainnet transaction with a single P2PKH input and output
|
||||||
|
tx = '0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000'
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(tx)
|
||||||
|
assert_equal('304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL] 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536', rpc_result['vin'][0]['scriptSig']['asm'])
|
||||||
|
|
||||||
|
# this test case uses a mainnet transaction that has a P2SH input and both P2PKH and P2SH outputs.
|
||||||
|
# it's from James D'Angelo's awesome introductory videos about multisig: https://www.youtube.com/watch?v=zIbUSaZBJgU and https://www.youtube.com/watch?v=OSA1pwlaypc
|
||||||
|
# verify that we have not altered scriptPubKey decoding.
|
||||||
|
tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000'
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(tx)
|
||||||
|
assert_equal('8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0', rpc_result['txid'])
|
||||||
|
assert_equal('0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm'])
|
||||||
|
assert_equal('OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm'])
|
||||||
|
assert_equal('OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm'])
|
||||||
|
txSave = CTransaction()
|
||||||
|
txSave.deserialize(StringIO(unhexlify(tx)))
|
||||||
|
|
||||||
|
# make sure that a specifically crafted op_return value will not pass all the IsDERSignature checks and then get decoded as a sighash type
|
||||||
|
tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000'
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(tx)
|
||||||
|
assert_equal('OP_RETURN 300602010002010001', rpc_result['vout'][0]['scriptPubKey']['asm'])
|
||||||
|
|
||||||
|
# verify that we have not altered scriptPubKey processing even of a specially crafted P2PKH pubkeyhash and P2SH redeem script hash that is made to pass the der signature checks
|
||||||
|
tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000'
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(tx)
|
||||||
|
assert_equal('OP_DUP OP_HASH160 3011020701010101010101020601010101010101 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm'])
|
||||||
|
assert_equal('OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm'])
|
||||||
|
|
||||||
|
# some more full transaction tests of varying specific scriptSigs. used instead of
|
||||||
|
# tests in decodescript_script_sig because the decodescript RPC is specifically
|
||||||
|
# for working on scriptPubKeys (argh!).
|
||||||
|
push_signature = hexlify(txSave.vin[0].scriptSig)[2:(0x48*2+4)]
|
||||||
|
signature = push_signature[2:]
|
||||||
|
der_signature = signature[:-2]
|
||||||
|
signature_sighash_decoded = der_signature + '[ALL]'
|
||||||
|
signature_2 = der_signature + '82'
|
||||||
|
push_signature_2 = '48' + signature_2
|
||||||
|
signature_2_sighash_decoded = der_signature + '[NONE|ANYONECANPAY]'
|
||||||
|
|
||||||
|
# 1) P2PK scriptSig
|
||||||
|
txSave.vin[0].scriptSig = unhexlify(push_signature)
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(hexlify(txSave.serialize()))
|
||||||
|
assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
|
||||||
|
|
||||||
|
# make sure that the sighash decodes come out correctly for a more complex / lesser used case.
|
||||||
|
txSave.vin[0].scriptSig = unhexlify(push_signature_2)
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(hexlify(txSave.serialize()))
|
||||||
|
assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
|
||||||
|
|
||||||
|
# 2) multisig scriptSig
|
||||||
|
txSave.vin[0].scriptSig = unhexlify('00' + push_signature + push_signature_2)
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(hexlify(txSave.serialize()))
|
||||||
|
assert_equal('0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
|
||||||
|
|
||||||
|
# 3) test a scriptSig that contains more than push operations.
|
||||||
|
# in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it.
|
||||||
|
txSave.vin[0].scriptSig = unhexlify('6a143011020701010101010101020601010101010101')
|
||||||
|
rpc_result = self.nodes[0].decoderawtransaction(hexlify(txSave.serialize()))
|
||||||
|
print(hexlify('636174'))
|
||||||
|
assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.decodescript_script_sig()
|
self.decodescript_script_sig()
|
||||||
self.decodescript_script_pub_key()
|
self.decodescript_script_pub_key()
|
||||||
|
self.decoderawtransaction_asm_sighashtype()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
DecodeScriptTest().main()
|
DecodeScriptTest().main()
|
||||||
|
|
||||||
|
|
|
@ -417,8 +417,8 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
|
||||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||||
string err("Previous output scriptPubKey mismatch:\n");
|
string err("Previous output scriptPubKey mismatch:\n");
|
||||||
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
|
||||||
scriptPubKey.ToString();
|
ScriptToAsmStr(scriptPubKey);
|
||||||
throw runtime_error(err);
|
throw runtime_error(err);
|
||||||
}
|
}
|
||||||
if ((unsigned int)nOut >= coins->vout.size())
|
if ((unsigned int)nOut >= coins->vout.size())
|
||||||
|
|
|
@ -16,6 +16,7 @@ class UniValue;
|
||||||
|
|
||||||
// core_read.cpp
|
// core_read.cpp
|
||||||
extern CScript ParseScript(const std::string& s);
|
extern CScript ParseScript(const std::string& s);
|
||||||
|
extern std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
|
||||||
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
|
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
|
||||||
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
|
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
|
||||||
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
|
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
|
||||||
|
@ -25,8 +26,7 @@ extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::strin
|
||||||
// core_write.cpp
|
// core_write.cpp
|
||||||
extern std::string FormatScript(const CScript& script);
|
extern std::string FormatScript(const CScript& script);
|
||||||
extern std::string EncodeHexTx(const CTransaction& tx);
|
extern std::string EncodeHexTx(const CTransaction& tx);
|
||||||
extern void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
||||||
UniValue& out, bool fIncludeHex);
|
|
||||||
extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
|
extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
|
||||||
|
|
||||||
#endif // BITCOIN_CORE_IO_H
|
#endif // BITCOIN_CORE_IO_H
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "utilmoneystr.h"
|
#include "utilmoneystr.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
|
#include <boost/assign/list_of.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -54,6 +55,67 @@ string FormatScript(const CScript& script)
|
||||||
return ret.substr(0, ret.size() - 1);
|
return ret.substr(0, ret.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const map<unsigned char, string> mapSigHashTypes =
|
||||||
|
boost::assign::map_list_of
|
||||||
|
(static_cast<unsigned char>(SIGHASH_ALL), string("ALL"))
|
||||||
|
(static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), string("ALL|ANYONECANPAY"))
|
||||||
|
(static_cast<unsigned char>(SIGHASH_NONE), string("NONE"))
|
||||||
|
(static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), string("NONE|ANYONECANPAY"))
|
||||||
|
(static_cast<unsigned char>(SIGHASH_SINGLE), string("SINGLE"))
|
||||||
|
(static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), string("SINGLE|ANYONECANPAY"))
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the assembly string representation of a CScript object.
|
||||||
|
* @param[in] script CScript object to convert into the asm string representation.
|
||||||
|
* @param[in] fAttemptSighashDecode Whether to attempt to decode sighash types on data within the script that matches the format
|
||||||
|
* of a signature. Only pass true for scripts you believe could contain signatures. For example,
|
||||||
|
* pass false, or omit the this argument (defaults to false), for scriptPubKeys.
|
||||||
|
*/
|
||||||
|
string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)
|
||||||
|
{
|
||||||
|
string str;
|
||||||
|
opcodetype opcode;
|
||||||
|
vector<unsigned char> vch;
|
||||||
|
CScript::const_iterator pc = script.begin();
|
||||||
|
while (pc < script.end()) {
|
||||||
|
if (!str.empty()) {
|
||||||
|
str += " ";
|
||||||
|
}
|
||||||
|
if (!script.GetOp(pc, opcode, vch)) {
|
||||||
|
str += "[error]";
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
if (0 <= opcode && opcode <= OP_PUSHDATA4) {
|
||||||
|
if (vch.size() <= static_cast<vector<unsigned char>::size_type>(4)) {
|
||||||
|
str += strprintf("%d", CScriptNum(vch, false).getint());
|
||||||
|
} else {
|
||||||
|
// the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature
|
||||||
|
if (fAttemptSighashDecode && !script.IsUnspendable()) {
|
||||||
|
string strSigHashDecode;
|
||||||
|
// goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig.
|
||||||
|
// this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to
|
||||||
|
// the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the
|
||||||
|
// checks in CheckSignatureEncoding.
|
||||||
|
if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, NULL)) {
|
||||||
|
const unsigned char chSigHashType = vch.back();
|
||||||
|
if (mapSigHashTypes.count(chSigHashType)) {
|
||||||
|
strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
|
||||||
|
vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += HexStr(vch) + strSigHashDecode;
|
||||||
|
} else {
|
||||||
|
str += HexStr(vch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str += GetOpName(opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
string EncodeHexTx(const CTransaction& tx)
|
string EncodeHexTx(const CTransaction& tx)
|
||||||
{
|
{
|
||||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
@ -68,7 +130,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||||
vector<CTxDestination> addresses;
|
vector<CTxDestination> addresses;
|
||||||
int nRequired;
|
int nRequired;
|
||||||
|
|
||||||
out.pushKV("asm", scriptPubKey.ToString());
|
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
|
||||||
if (fIncludeHex)
|
if (fIncludeHex)
|
||||||
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||||
|
|
||||||
|
@ -101,7 +163,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
|
||||||
in.pushKV("txid", txin.prevout.hash.GetHex());
|
in.pushKV("txid", txin.prevout.hash.GetHex());
|
||||||
in.pushKV("vout", (int64_t)txin.prevout.n);
|
in.pushKV("vout", (int64_t)txin.prevout.n);
|
||||||
UniValue o(UniValue::VOBJ);
|
UniValue o(UniValue::VOBJ);
|
||||||
o.pushKV("asm", txin.scriptSig.ToString());
|
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
|
||||||
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
||||||
in.pushKV("scriptSig", o);
|
in.pushKV("scriptSig", o);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ std::string CTxIn::ToString() const
|
||||||
if (prevout.IsNull())
|
if (prevout.IsNull())
|
||||||
str += strprintf(", coinbase %s", HexStr(scriptSig));
|
str += strprintf(", coinbase %s", HexStr(scriptSig));
|
||||||
else
|
else
|
||||||
str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24));
|
str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24));
|
||||||
if (nSequence != std::numeric_limits<unsigned int>::max())
|
if (nSequence != std::numeric_limits<unsigned int>::max())
|
||||||
str += strprintf(", nSequence=%u", nSequence);
|
str += strprintf(", nSequence=%u", nSequence);
|
||||||
str += ")";
|
str += ")";
|
||||||
|
@ -56,7 +56,7 @@ uint256 CTxOut::GetHash() const
|
||||||
|
|
||||||
std::string CTxOut::ToString() const
|
std::string CTxOut::ToString() const
|
||||||
{
|
{
|
||||||
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30));
|
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
||||||
}
|
}
|
||||||
|
|
||||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
||||||
|
|
|
@ -41,7 +41,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud
|
||||||
vector<CTxDestination> addresses;
|
vector<CTxDestination> addresses;
|
||||||
int nRequired;
|
int nRequired;
|
||||||
|
|
||||||
out.push_back(Pair("asm", scriptPubKey.ToString()));
|
out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey)));
|
||||||
if (fIncludeHex)
|
if (fIncludeHex)
|
||||||
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
||||||
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
||||||
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
||||||
UniValue o(UniValue::VOBJ);
|
UniValue o(UniValue::VOBJ);
|
||||||
o.push_back(Pair("asm", txin.scriptSig.ToString()));
|
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
|
||||||
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||||
in.push_back(Pair("scriptSig", o));
|
in.push_back(Pair("scriptSig", o));
|
||||||
}
|
}
|
||||||
|
@ -676,8 +676,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
|
||||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||||
string err("Previous output scriptPubKey mismatch:\n");
|
string err("Previous output scriptPubKey mismatch:\n");
|
||||||
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
|
||||||
scriptPubKey.ToString();
|
ScriptToAsmStr(scriptPubKey);
|
||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
||||||
}
|
}
|
||||||
if ((unsigned int)nOut >= coins->vout.size())
|
if ((unsigned int)nOut >= coins->vout.size())
|
||||||
|
|
|
@ -188,7 +188,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
|
bool CheckSignatureEncoding(const vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) {
|
||||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||||
if (vchSig.size() == 0) {
|
if (vchSig.size() == 0) {
|
||||||
|
|
|
@ -83,6 +83,8 @@ enum
|
||||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||||
|
|
||||||
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||||
|
|
||||||
class BaseSignatureChecker
|
class BaseSignatureChecker
|
||||||
|
|
|
@ -8,16 +8,6 @@
|
||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
|
||||||
{
|
|
||||||
if (vch.size() <= 4)
|
|
||||||
return strprintf("%d", CScriptNum(vch, false).getint());
|
|
||||||
else
|
|
||||||
return HexStr(vch);
|
|
||||||
}
|
|
||||||
} // anon namespace
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const char* GetOpName(opcodetype opcode)
|
const char* GetOpName(opcodetype opcode)
|
||||||
|
@ -237,26 +227,3 @@ bool CScript::IsPushOnly() const
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CScript::ToString() const
|
|
||||||
{
|
|
||||||
std::string str;
|
|
||||||
opcodetype opcode;
|
|
||||||
std::vector<unsigned char> vch;
|
|
||||||
const_iterator pc = begin();
|
|
||||||
while (pc < end())
|
|
||||||
{
|
|
||||||
if (!str.empty())
|
|
||||||
str += " ";
|
|
||||||
if (!GetOp(pc, opcode, vch))
|
|
||||||
{
|
|
||||||
str += "[error]";
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
if (0 <= opcode && opcode <= OP_PUSHDATA4)
|
|
||||||
str += ValueString(vch);
|
|
||||||
else
|
|
||||||
str += GetOpName(opcode);
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
|
@ -601,7 +601,6 @@ public:
|
||||||
return (size() > 0 && *begin() == OP_RETURN);
|
return (size() > 0 && *begin() == OP_RETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ToString() const;
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
// The default std::vector::clear() does not release memory.
|
// The default std::vector::clear() does not release memory.
|
||||||
|
|
|
@ -983,4 +983,34 @@ BOOST_AUTO_TEST_CASE(script_IsPushOnly_on_invalid_scripts)
|
||||||
BOOST_CHECK(!CScript(direct, direct+sizeof(direct)).IsPushOnly());
|
BOOST_CHECK(!CScript(direct, direct+sizeof(direct)).IsPushOnly());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_GetScriptAsm)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL("OP_NOP2", ScriptToAsmStr(CScript() << OP_NOP2, true));
|
||||||
|
BOOST_CHECK_EQUAL("OP_NOP2", ScriptToAsmStr(CScript() << OP_CHECKLOCKTIMEVERIFY, true));
|
||||||
|
BOOST_CHECK_EQUAL("OP_NOP2", ScriptToAsmStr(CScript() << OP_NOP2));
|
||||||
|
BOOST_CHECK_EQUAL("OP_NOP2", ScriptToAsmStr(CScript() << OP_CHECKLOCKTIMEVERIFY));
|
||||||
|
|
||||||
|
string derSig("304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c5090");
|
||||||
|
string pubKey("03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2");
|
||||||
|
vector<unsigned char> vchPubKey = ToByteVector(ParseHex(pubKey));
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "00 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "00")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "80 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "80")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "[ALL] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "01")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "[NONE] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "02")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "[SINGLE] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "03")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "[ALL|ANYONECANPAY] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "81")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "[NONE|ANYONECANPAY] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "82")) << vchPubKey, true));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "[SINGLE|ANYONECANPAY] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey, true));
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "00 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "00")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "80 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "80")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "01 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "01")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "02 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "02")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "03 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "03")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "81 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "81")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "82 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "82")) << vchPubKey));
|
||||||
|
BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Reference in a new issue