Browse Source

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)
0.13
Wladimir J. van der Laan 9 years ago
parent
commit
48efbdbe98
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 27
      doc/release-notes.md
  2. 72
      qa/rpc-tests/decodescript.py
  3. 4
      src/bitcoin-tx.cpp
  4. 4
      src/core_io.h
  5. 66
      src/core_write.cpp
  6. 4
      src/primitives/transaction.cpp
  7. 8
      src/rpcrawtransaction.cpp
  8. 2
      src/script/interpreter.cpp
  9. 2
      src/script/interpreter.h
  10. 33
      src/script/script.cpp
  11. 1
      src/script/script.h
  12. 32
      src/test/script_tests.cpp

27
doc/release-notes.md

@ -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

72
qa/rpc-tests/decodescript.py

@ -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()

4
src/bitcoin-tx.cpp

@ -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())

4
src/core_io.h

@ -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

66
src/core_write.cpp

@ -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);
} }

4
src/primitives/transaction.cpp

@ -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) {}

8
src/rpcrawtransaction.cpp

@ -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())

2
src/script/interpreter.cpp

@ -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) {

2
src/script/interpreter.h

@ -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

33
src/script/script.cpp

@ -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;
}

1
src/script/script.h

@ -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.

32
src/test/script_tests.cpp

@ -840,7 +840,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
} }
BOOST_AUTO_TEST_CASE(script_combineSigs) BOOST_AUTO_TEST_CASE(script_combineSigs)
{ {
@ -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…
Cancel
Save