diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2013-05-04 19:32:33 +0200 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2013-09-28 18:37:00 +0200 |
commit | f5857e5cb5fb03bee9c05d1dd6ba2621cac49179 (patch) | |
tree | 6d5347773c08f057b056de27a79fe890023db872 /src/test | |
parent | b41fa66ba7eaf3e56ce43bfb28114f59a7ea58ac (diff) |
Inline signature serializer
Instead of building a full copy of a CTransaction being signed, and
then modifying bits and pieces until its fits the form necessary
for computing the signature hash, use a wrapper serializer that
only serializes the necessary bits on-the-fly.
This makes it easier to see which data is actually being hash,
reduces load on the heap, and also marginally improves performances
(around 3-4us/sigcheck here). The performance improvements are much
larger for large transactions, though.
The old implementation of SignatureHash is moved to a unit tests,
to test whether the old and new algorithm result in the same value
for randomly-constructed transactions.
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/Makefile.am | 2 | ||||
-rw-r--r-- | src/test/multisig_tests.cpp | 2 | ||||
-rw-r--r-- | src/test/script_tests.cpp | 2 | ||||
-rw-r--r-- | src/test/sighash_tests.cpp | 120 |
4 files changed, 123 insertions, 3 deletions
diff --git a/src/test/Makefile.am b/src/test/Makefile.am index d193b729f8..bedc5d0c29 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -34,7 +34,7 @@ test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ netbase_tests.cpp pmt_tests.cpp rpc_tests.cpp script_P2SH_tests.cpp \ script_tests.cpp serialize_tests.cpp sigopcount_tests.cpp test_bitcoin.cpp \ transaction_tests.cpp uint160_tests.cpp uint256_tests.cpp util_tests.cpp \ - wallet_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + wallet_tests.cpp sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES) diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 9ef932b5b4..7f6f141c62 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -19,7 +19,7 @@ using namespace boost::assign; typedef vector<unsigned char> valtype; -extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); BOOST_AUTO_TEST_SUITE(multisig_tests) diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index dfa5529b87..32be914414 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -21,7 +21,7 @@ using namespace std; using namespace json_spirit; using namespace boost::algorithm; -extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp new file mode 100644 index 0000000000..f098d46186 --- /dev/null +++ b/src/test/sighash_tests.cpp @@ -0,0 +1,120 @@ +#include <boost/test/unit_test.hpp> + +#include "main.h" +#include "util.h" + +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + +// Old script.cpp SignatureHash function +uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lock-in the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (unsigned int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CHashWriter ss(SER_GETHASH, 0); + ss << txTmp << nHashType; + return ss.GetHash(); +} + +void static RandomScript(CScript &script) { + static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; + script = CScript(); + int ops = (insecure_rand() % 10); + for (int i=0; i<ops; i++) + script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; +} + +void static RandomTransaction(CTransaction &tx, bool fSingle) { + tx.nVersion = insecure_rand(); + tx.vin.clear(); + tx.vout.clear(); + tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0; + int ins = (insecure_rand() % 4) + 1; + int outs = fSingle ? ins : (insecure_rand() % 4) + 1; + for (int in = 0; in < ins; in++) { + tx.vin.push_back(CTxIn()); + CTxIn &txin = tx.vin.back(); + txin.prevout.hash = GetRandHash(); + txin.prevout.n = insecure_rand() % 4; + RandomScript(txin.scriptSig); + txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1; + } + for (int out = 0; out < outs; out++) { + tx.vout.push_back(CTxOut()); + CTxOut &txout = tx.vout.back(); + txout.nValue = insecure_rand() % 100000000; + RandomScript(txout.scriptPubKey); + } +} + +BOOST_AUTO_TEST_SUITE(sighash_tests) + +BOOST_AUTO_TEST_CASE(sighash_test) +{ + seed_insecure_rand(false); + + for (int i=0; i<50000; i++) { + int nHashType = insecure_rand(); + CTransaction txTo; + RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); + CScript scriptCode; + RandomScript(scriptCode); + int nIn = insecure_rand() % txTo.vin.size(); + BOOST_CHECK(SignatureHash(scriptCode, txTo, nIn, nHashType) == + SignatureHashOld(scriptCode, txTo, nIn, nHashType)); + } +} + +BOOST_AUTO_TEST_SUITE_END() + |