]>
Commit | Line | Data |
---|---|---|
3fc68461 | 1 | // Copyright (c) 2013 The Bitcoin Core developers |
78253fcb | 2 | // Distributed under the MIT software license, see the accompanying |
3fc68461 WL |
3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | ||
da29ecbc | 5 | #include "consensus/validation.h" |
6354935c | 6 | #include "data/sighash.json.h" |
f5857e5c | 7 | #include "main.h" |
6354935c | 8 | #include "random.h" |
da03e6ed | 9 | #include "script/interpreter.h" |
da29ecbc | 10 | #include "script/script.h" |
11 | #include "serialize.h" | |
12 | #include "test/test_bitcoin.h" | |
6354935c | 13 | #include "util.h" |
43cb4185 | 14 | #include "version.h" |
e857a0cd | 15 | #include "sodium.h" |
43cb4185 | 16 | |
6354935c PK |
17 | #include <iostream> |
18 | ||
19 | #include <boost/test/unit_test.hpp> | |
43cb4185 MA |
20 | #include "json/json_spirit_reader_template.h" |
21 | #include "json/json_spirit_utils.h" | |
22 | #include "json/json_spirit_writer_template.h" | |
23 | ||
24 | using namespace json_spirit; | |
25 | extern Array read_json(const std::string& jsondata); | |
f5857e5c | 26 | |
f5857e5c PW |
27 | // Old script.cpp SignatureHash function |
28 | uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) | |
29 | { | |
34cdc411 | 30 | static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); |
f5857e5c PW |
31 | if (nIn >= txTo.vin.size()) |
32 | { | |
5262fde0 | 33 | printf("ERROR: SignatureHash(): nIn=%d out of range\n", nIn); |
2eae3157 | 34 | return one; |
f5857e5c | 35 | } |
4949004d | 36 | CMutableTransaction txTmp(txTo); |
f5857e5c | 37 | |
f5857e5c PW |
38 | // Blank out other inputs' signatures |
39 | for (unsigned int i = 0; i < txTmp.vin.size(); i++) | |
40 | txTmp.vin[i].scriptSig = CScript(); | |
41 | txTmp.vin[nIn].scriptSig = scriptCode; | |
42 | ||
43 | // Blank out some of the outputs | |
44 | if ((nHashType & 0x1f) == SIGHASH_NONE) | |
45 | { | |
46 | // Wildcard payee | |
47 | txTmp.vout.clear(); | |
48 | ||
49 | // Let the others update at will | |
50 | for (unsigned int i = 0; i < txTmp.vin.size(); i++) | |
51 | if (i != nIn) | |
52 | txTmp.vin[i].nSequence = 0; | |
53 | } | |
54 | else if ((nHashType & 0x1f) == SIGHASH_SINGLE) | |
55 | { | |
56 | // Only lock-in the txout payee at same index as txin | |
57 | unsigned int nOut = nIn; | |
58 | if (nOut >= txTmp.vout.size()) | |
59 | { | |
5262fde0 | 60 | printf("ERROR: SignatureHash(): nOut=%d out of range\n", nOut); |
2eae3157 | 61 | return one; |
f5857e5c PW |
62 | } |
63 | txTmp.vout.resize(nOut+1); | |
64 | for (unsigned int i = 0; i < nOut; i++) | |
65 | txTmp.vout[i].SetNull(); | |
66 | ||
67 | // Let the others update at will | |
68 | for (unsigned int i = 0; i < txTmp.vin.size(); i++) | |
69 | if (i != nIn) | |
70 | txTmp.vin[i].nSequence = 0; | |
71 | } | |
72 | ||
73 | // Blank out other inputs completely, not recommended for open transactions | |
74 | if (nHashType & SIGHASH_ANYONECANPAY) | |
75 | { | |
76 | txTmp.vin[0] = txTmp.vin[nIn]; | |
77 | txTmp.vin.resize(1); | |
78 | } | |
79 | ||
320f2cc7 | 80 | // Blank out the joinsplit signature. |
7c68cc07 | 81 | memset(&txTmp.joinSplitSig[0], 0, txTmp.joinSplitSig.size()); |
b48122b5 | 82 | |
f5857e5c PW |
83 | // Serialize and hash |
84 | CHashWriter ss(SER_GETHASH, 0); | |
85 | ss << txTmp << nHashType; | |
86 | return ss.GetHash(); | |
87 | } | |
88 | ||
89 | void static RandomScript(CScript &script) { | |
7f01e437 | 90 | static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN}; |
f5857e5c PW |
91 | script = CScript(); |
92 | int ops = (insecure_rand() % 10); | |
93 | for (int i=0; i<ops; i++) | |
94 | script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; | |
95 | } | |
96 | ||
4949004d | 97 | void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { |
f5857e5c PW |
98 | tx.nVersion = insecure_rand(); |
99 | tx.vin.clear(); | |
100 | tx.vout.clear(); | |
101 | tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0; | |
102 | int ins = (insecure_rand() % 4) + 1; | |
103 | int outs = fSingle ? ins : (insecure_rand() % 4) + 1; | |
22de1602 | 104 | int joinsplits = (insecure_rand() % 4); |
f5857e5c PW |
105 | for (int in = 0; in < ins; in++) { |
106 | tx.vin.push_back(CTxIn()); | |
107 | CTxIn &txin = tx.vin.back(); | |
108 | txin.prevout.hash = GetRandHash(); | |
109 | txin.prevout.n = insecure_rand() % 4; | |
110 | RandomScript(txin.scriptSig); | |
111 | txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1; | |
112 | } | |
113 | for (int out = 0; out < outs; out++) { | |
114 | tx.vout.push_back(CTxOut()); | |
115 | CTxOut &txout = tx.vout.back(); | |
116 | txout.nValue = insecure_rand() % 100000000; | |
117 | RandomScript(txout.scriptPubKey); | |
118 | } | |
5884044b | 119 | if (tx.nVersion >= 2) { |
22de1602 SB |
120 | for (int js = 0; js < joinsplits; js++) { |
121 | JSDescription jsdesc; | |
3098bab6 | 122 | if (insecure_rand() % 2 == 0) { |
22de1602 | 123 | jsdesc.vpub_old = insecure_rand() % 100000000; |
3098bab6 | 124 | } else { |
22de1602 | 125 | jsdesc.vpub_new = insecure_rand() % 100000000; |
3098bab6 | 126 | } |
21406393 | 127 | |
22de1602 SB |
128 | jsdesc.anchor = GetRandHash(); |
129 | jsdesc.nullifiers[0] = GetRandHash(); | |
130 | jsdesc.nullifiers[1] = GetRandHash(); | |
131 | jsdesc.ephemeralKey = GetRandHash(); | |
132 | jsdesc.randomSeed = GetRandHash(); | |
133 | randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size()); | |
134 | randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].size()); | |
f0dab51c | 135 | jsdesc.proof = libzcash::ZCProof::random_invalid(); |
22de1602 SB |
136 | jsdesc.macs[0] = GetRandHash(); |
137 | jsdesc.macs[1] = GetRandHash(); | |
138 | ||
139 | tx.vjoinsplit.push_back(jsdesc); | |
5884044b | 140 | } |
b48122b5 | 141 | |
320f2cc7 SB |
142 | unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; |
143 | crypto_sign_keypair(tx.joinSplitPubKey.begin(), joinSplitPrivKey); | |
b48122b5 | 144 | |
320f2cc7 | 145 | // Empty output script. |
b48122b5 | 146 | CScript scriptCode; |
320f2cc7 | 147 | CTransaction signTx(tx); |
b48122b5 | 148 | uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); |
b48122b5 | 149 | |
320f2cc7 SB |
150 | assert(crypto_sign_detached(&tx.joinSplitSig[0], NULL, |
151 | dataToBeSigned.begin(), 32, | |
152 | joinSplitPrivKey | |
153 | ) == 0); | |
5884044b | 154 | } |
f5857e5c PW |
155 | } |
156 | ||
92fd887f | 157 | BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup) |
232aa9e0 | 158 | |
f5857e5c PW |
159 | BOOST_AUTO_TEST_CASE(sighash_test) |
160 | { | |
161 | seed_insecure_rand(false); | |
6354935c | 162 | |
232aa9e0 MA |
163 | #if defined(PRINT_SIGHASH_JSON) |
164 | std::cout << "[\n"; | |
165 | std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; | |
166 | #endif | |
167 | int nRandomTests = 50000; | |
168 | ||
169 | #if defined(PRINT_SIGHASH_JSON) | |
170 | nRandomTests = 500; | |
171 | #endif | |
172 | for (int i=0; i<nRandomTests; i++) { | |
f5857e5c | 173 | int nHashType = insecure_rand(); |
4949004d | 174 | CMutableTransaction txTo; |
f5857e5c PW |
175 | RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); |
176 | CScript scriptCode; | |
177 | RandomScript(scriptCode); | |
178 | int nIn = insecure_rand() % txTo.vin.size(); | |
43cb4185 MA |
179 | |
180 | uint256 sh, sho; | |
181 | sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType); | |
182 | sh = SignatureHash(scriptCode, txTo, nIn, nHashType); | |
232aa9e0 MA |
183 | #if defined(PRINT_SIGHASH_JSON) |
184 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
185 | ss << txTo; | |
186 | ||
187 | std::cout << "\t[\"" ; | |
188 | std::cout << HexStr(ss.begin(), ss.end()) << "\", \""; | |
189 | std::cout << HexStr(scriptCode) << "\", "; | |
190 | std::cout << nIn << ", "; | |
191 | std::cout << nHashType << ", \""; | |
192 | std::cout << sho.GetHex() << "\"]"; | |
193 | if (i+1 != nRandomTests) { | |
194 | std::cout << ","; | |
195 | } | |
196 | std::cout << "\n"; | |
197 | #endif | |
43cb4185 | 198 | BOOST_CHECK(sh == sho); |
f5857e5c | 199 | } |
232aa9e0 MA |
200 | #if defined(PRINT_SIGHASH_JSON) |
201 | std::cout << "]\n"; | |
202 | #endif | |
f5857e5c PW |
203 | } |
204 | ||
43cb4185 | 205 | // Goal: check that SignatureHash generates correct hash |
43cb4185 MA |
206 | BOOST_AUTO_TEST_CASE(sighash_from_data) |
207 | { | |
208 | Array tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash))); | |
209 | ||
210 | BOOST_FOREACH(Value& tv, tests) | |
211 | { | |
212 | Array test = tv.get_array(); | |
213 | std::string strTest = write_string(tv, false); | |
214 | if (test.size() < 1) // Allow for extra stuff (useful for comments) | |
215 | { | |
216 | BOOST_ERROR("Bad test: " << strTest); | |
217 | continue; | |
218 | } | |
219 | if (test.size() == 1) continue; // comment | |
220 | ||
81bfb5ae MA |
221 | std::string raw_tx, raw_script, sigHashHex; |
222 | int nIn, nHashType; | |
43cb4185 | 223 | uint256 sh; |
43cb4185 | 224 | CTransaction tx; |
43cb4185 | 225 | CScript scriptCode = CScript(); |
43cb4185 | 226 | |
81bfb5ae MA |
227 | try { |
228 | // deserialize test data | |
229 | raw_tx = test[0].get_str(); | |
230 | raw_script = test[1].get_str(); | |
231 | nIn = test[2].get_int(); | |
232 | nHashType = test[3].get_int(); | |
233 | sigHashHex = test[4].get_str(); | |
234 | ||
235 | uint256 sh; | |
236 | CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION); | |
237 | stream >> tx; | |
238 | ||
239 | CValidationState state; | |
f4f1b4b0 JG |
240 | if (tx.nVersion < MIN_TX_VERSION) { |
241 | // Transaction must be invalid | |
242 | BOOST_CHECK_MESSAGE(!CheckTransactionWithoutProofVerification(tx, state), strTest); | |
243 | BOOST_CHECK(!state.IsValid()); | |
244 | } else { | |
245 | BOOST_CHECK_MESSAGE(CheckTransactionWithoutProofVerification(tx, state), strTest); | |
246 | BOOST_CHECK(state.IsValid()); | |
247 | } | |
81bfb5ae MA |
248 | |
249 | std::vector<unsigned char> raw = ParseHex(raw_script); | |
250 | scriptCode.insert(scriptCode.end(), raw.begin(), raw.end()); | |
251 | } catch (...) { | |
252 | BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest); | |
253 | continue; | |
254 | } | |
6354935c | 255 | |
43cb4185 | 256 | sh = SignatureHash(scriptCode, tx, nIn, nHashType); |
43cb4185 MA |
257 | BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); |
258 | } | |
259 | } | |
f5857e5c | 260 | BOOST_AUTO_TEST_SUITE_END() |