aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2013-05-04 19:32:33 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2013-09-28 18:37:00 +0200
commitf5857e5cb5fb03bee9c05d1dd6ba2621cac49179 (patch)
tree6d5347773c08f057b056de27a79fe890023db872 /src/test
parentb41fa66ba7eaf3e56ce43bfb28114f59a7ea58ac (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.am2
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/script_tests.cpp2
-rw-r--r--src/test/sighash_tests.cpp120
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()
+