diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2015-09-25 19:11:26 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2015-09-25 19:19:07 +0200 |
commit | 48efbdbe986355bd2478f0fdd366b20952fbf30a (patch) | |
tree | b4581561aa9d51763bda985063d0ce4913cef19d | |
parent | 486270854ec0f89e798f2baff3fe01623a274ed4 (diff) | |
parent | af3208bfa6967d6b35aecf0ba35d9d6bf0f8317e (diff) |
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)
-rw-r--r-- | doc/release-notes.md | 27 | ||||
-rwxr-xr-x | qa/rpc-tests/decodescript.py | 72 | ||||
-rw-r--r-- | src/bitcoin-tx.cpp | 4 | ||||
-rw-r--r-- | src/core_io.h | 4 | ||||
-rw-r--r-- | src/core_write.cpp | 66 | ||||
-rw-r--r-- | src/primitives/transaction.cpp | 4 | ||||
-rw-r--r-- | src/rpcrawtransaction.cpp | 8 | ||||
-rw-r--r-- | src/script/interpreter.cpp | 2 | ||||
-rw-r--r-- | src/script/interpreter.h | 2 | ||||
-rw-r--r-- | src/script/script.cpp | 33 | ||||
-rw-r--r-- | src/script/script.h | 1 | ||||
-rw-r--r-- | src/test/script_tests.cpp | 32 |
12 files changed, 206 insertions, 49 deletions
diff --git a/doc/release-notes.md b/doc/release-notes.md index bef9af60ef..85cdabc7e3 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -124,6 +124,33 @@ git merge commit are mentioned. ### 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 ### Block and transaction handling diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py index ce3bc94ef7..4bca623380 100755 --- a/qa/rpc-tests/decodescript.py +++ b/qa/rpc-tests/decodescript.py @@ -5,6 +5,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * +from test_framework.mininode import * +from binascii import hexlify, unhexlify +from cStringIO import StringIO class DecodeScriptTest(BitcoinTestFramework): """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') 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): self.decodescript_script_sig() self.decodescript_script_pub_key() + self.decoderawtransaction_asm_sighashtype() if __name__ == '__main__': DecodeScriptTest().main() - diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 97a073174d..5beab265bc 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -417,8 +417,8 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) CCoinsModifier coins = view.ModifyCoins(txid); if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { string err("Previous output scriptPubKey mismatch:\n"); - err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ - scriptPubKey.ToString(); + err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ + ScriptToAsmStr(scriptPubKey); throw runtime_error(err); } if ((unsigned int)nOut >= coins->vout.size()) diff --git a/src/core_io.h b/src/core_io.h index 115e3199dc..ba5b4e6487 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -16,6 +16,7 @@ class UniValue; // core_read.cpp 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 DecodeHexBlk(CBlock&, const std::string& strHexBlk); 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 extern std::string FormatScript(const CScript& script); extern std::string EncodeHexTx(const CTransaction& tx); -extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, - UniValue& out, bool fIncludeHex); +extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry); #endif // BITCOIN_CORE_IO_H diff --git a/src/core_write.cpp b/src/core_write.cpp index c3babec2fc..2ad42baddf 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -15,6 +15,7 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" +#include <boost/assign/list_of.hpp> #include <boost/foreach.hpp> using namespace std; @@ -54,6 +55,67 @@ string FormatScript(const CScript& script) 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) { CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -68,7 +130,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, vector<CTxDestination> addresses; int nRequired; - out.pushKV("asm", scriptPubKey.ToString()); + out.pushKV("asm", ScriptToAsmStr(scriptPubKey)); if (fIncludeHex) 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("vout", (int64_t)txin.prevout.n); 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())); in.pushKV("scriptSig", o); } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 606dbea798..46d3cbbe2e 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -36,7 +36,7 @@ std::string CTxIn::ToString() const if (prevout.IsNull()) str += strprintf(", coinbase %s", HexStr(scriptSig)); 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()) str += strprintf(", nSequence=%u", nSequence); str += ")"; @@ -56,7 +56,7 @@ uint256 CTxOut::GetHash() 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) {} diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 9eeca5b7d9..fa3150cd7f 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -41,7 +41,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud vector<CTxDestination> addresses; int nRequired; - out.push_back(Pair("asm", scriptPubKey.ToString())); + out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); if (fIncludeHex) 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("vout", (int64_t)txin.prevout.n)); 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()))); in.push_back(Pair("scriptSig", o)); } @@ -676,8 +676,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) CCoinsModifier coins = view.ModifyCoins(txid); if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { string err("Previous output scriptPubKey mismatch:\n"); - err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ - scriptPubKey.ToString(); + err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ + ScriptToAsmStr(scriptPubKey); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } if ((unsigned int)nOut >= coins->vout.size()) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index bd5e54b33f..d3aec26020 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -188,7 +188,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) { 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 // compact way to provide an invalid signature for use with CHECK(MULTI)SIG if (vchSig.size() == 0) { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 35d572f0ad..213e8c7651 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -83,6 +83,8 @@ enum 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); class BaseSignatureChecker diff --git a/src/script/script.cpp b/src/script/script.cpp index fd33924732..58dbade0e2 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -8,16 +8,6 @@ #include "tinyformat.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; const char* GetOpName(opcodetype opcode) @@ -237,26 +227,3 @@ bool CScript::IsPushOnly() const } 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; -} diff --git a/src/script/script.h b/src/script/script.h index e39ca57f4f..f0725bbbf6 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -601,7 +601,6 @@ public: return (size() > 0 && *begin() == OP_RETURN); } - std::string ToString() const; void clear() { // The default std::vector::clear() does not release memory. diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 37c046935f..225da0801a 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -840,7 +840,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); -} +} 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_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() |