]>
Commit | Line | Data |
---|---|---|
3fc68461 WL |
1 | // Copyright (c) 2012-2013 The Bitcoin Core developers |
2 | // Distributed under the MIT/X11 software license, see the accompanying | |
3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
4 | ||
51ed9ec9 BD |
5 | #include "key.h" |
6 | #include "keystore.h" | |
7 | #include "main.h" | |
cbd22a50 | 8 | #include "script/script.h" |
e088d65a | 9 | #include "script/sign.h" |
0d2fa14a | 10 | |
11 | #ifdef ENABLE_WALLET | |
c1e433b7 | 12 | #include "wallet_ismine.h" |
0d2fa14a | 13 | #endif |
922e8e29 | 14 | |
51ed9ec9 BD |
15 | #include <vector> |
16 | ||
17 | #include <boost/test/unit_test.hpp> | |
922e8e29 GA |
18 | |
19 | using namespace std; | |
20 | ||
922e8e29 GA |
21 | // Helpers: |
22 | static std::vector<unsigned char> | |
23 | Serialize(const CScript& s) | |
24 | { | |
25 | std::vector<unsigned char> sSerialized(s); | |
26 | return sSerialized; | |
27 | } | |
28 | ||
29 | static bool | |
30 | Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) | |
31 | { | |
32 | // Create dummy to/from transactions: | |
4949004d | 33 | CMutableTransaction txFrom; |
922e8e29 GA |
34 | txFrom.vout.resize(1); |
35 | txFrom.vout[0].scriptPubKey = scriptPubKey; | |
36 | ||
4949004d | 37 | CMutableTransaction txTo; |
922e8e29 GA |
38 | txTo.vin.resize(1); |
39 | txTo.vout.resize(1); | |
40 | txTo.vin[0].prevout.n = 0; | |
41 | txTo.vin[0].prevout.hash = txFrom.GetHash(); | |
42 | txTo.vin[0].scriptSig = scriptSig; | |
43 | txTo.vout[0].nValue = 1; | |
44 | ||
99d0d0f3 | 45 | return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); |
922e8e29 GA |
46 | } |
47 | ||
48 | ||
49 | BOOST_AUTO_TEST_SUITE(script_P2SH_tests) | |
50 | ||
51 | BOOST_AUTO_TEST_CASE(sign) | |
52 | { | |
ed671005 | 53 | LOCK(cs_main); |
922e8e29 GA |
54 | // Pay-to-script-hash looks like this: |
55 | // scriptSig: <sig> <sig...> <serialized_script> | |
56 | // scriptPubKey: HASH160 <hash> EQUAL | |
57 | ||
58 | // Test SignSignature() (and therefore the version of Solver() that signs transactions) | |
59 | CBasicKeyStore keystore; | |
60 | CKey key[4]; | |
61 | for (int i = 0; i < 4; i++) | |
62 | { | |
0d56f11a | 63 | key[i].MakeNewKey(true); |
922e8e29 GA |
64 | keystore.AddKey(key[i]); |
65 | } | |
66 | ||
67 | // 8 Scripts: checking all combinations of | |
68 | // different keys, straight/P2SH, pubkey/pubkeyhash | |
69 | CScript standardScripts[4]; | |
70 | standardScripts[0] << key[0].GetPubKey() << OP_CHECKSIG; | |
0be990ba | 71 | standardScripts[1] = GetScriptForDestination(key[1].GetPubKey().GetID()); |
922e8e29 | 72 | standardScripts[2] << key[1].GetPubKey() << OP_CHECKSIG; |
0be990ba | 73 | standardScripts[3] = GetScriptForDestination(key[2].GetPubKey().GetID()); |
922e8e29 GA |
74 | CScript evalScripts[4]; |
75 | for (int i = 0; i < 4; i++) | |
76 | { | |
77 | keystore.AddCScript(standardScripts[i]); | |
0be990ba | 78 | evalScripts[i] = GetScriptForDestination(standardScripts[i].GetID()); |
922e8e29 GA |
79 | } |
80 | ||
4949004d | 81 | CMutableTransaction txFrom; // Funding transaction: |
980bfe6e | 82 | string reason; |
922e8e29 GA |
83 | txFrom.vout.resize(8); |
84 | for (int i = 0; i < 4; i++) | |
85 | { | |
86 | txFrom.vout[i].scriptPubKey = evalScripts[i]; | |
8de9bb53 | 87 | txFrom.vout[i].nValue = COIN; |
922e8e29 | 88 | txFrom.vout[i+4].scriptPubKey = standardScripts[i]; |
8de9bb53 | 89 | txFrom.vout[i+4].nValue = COIN; |
922e8e29 | 90 | } |
980bfe6e | 91 | BOOST_CHECK(IsStandardTx(txFrom, reason)); |
922e8e29 | 92 | |
4949004d | 93 | CMutableTransaction txTo[8]; // Spending transactions |
922e8e29 GA |
94 | for (int i = 0; i < 8; i++) |
95 | { | |
96 | txTo[i].vin.resize(1); | |
97 | txTo[i].vout.resize(1); | |
98 | txTo[i].vin[0].prevout.n = i; | |
99 | txTo[i].vin[0].prevout.hash = txFrom.GetHash(); | |
100 | txTo[i].vout[0].nValue = 1; | |
0d2fa14a | 101 | #ifdef ENABLE_WALLET |
922e8e29 | 102 | BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); |
0d2fa14a | 103 | #endif |
922e8e29 GA |
104 | } |
105 | for (int i = 0; i < 8; i++) | |
106 | { | |
107 | BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); | |
108 | } | |
109 | // All of the above should be OK, and the txTos have valid signatures | |
110 | // Check to make sure signature verification fails if we use the wrong ScriptSig: | |
111 | for (int i = 0; i < 8; i++) | |
112 | for (int j = 0; j < 8; j++) | |
113 | { | |
114 | CScript sigSave = txTo[i].vin[0].scriptSig; | |
115 | txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; | |
99d0d0f3 | 116 | bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); |
922e8e29 GA |
117 | if (i == j) |
118 | BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); | |
119 | else | |
120 | BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j)); | |
121 | txTo[i].vin[0].scriptSig = sigSave; | |
122 | } | |
123 | } | |
124 | ||
125 | BOOST_AUTO_TEST_CASE(norecurse) | |
126 | { | |
127 | // Make sure only the outer pay-to-script-hash does the | |
128 | // extra-validation thing: | |
129 | CScript invalidAsScript; | |
130 | invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE; | |
131 | ||
0be990ba | 132 | CScript p2sh = GetScriptForDestination(invalidAsScript.GetID()); |
922e8e29 GA |
133 | |
134 | CScript scriptSig; | |
135 | scriptSig << Serialize(invalidAsScript); | |
136 | ||
137 | // Should not verify, because it will try to execute OP_INVALIDOPCODE | |
138 | BOOST_CHECK(!Verify(scriptSig, p2sh, true)); | |
139 | ||
814efd6f | 140 | // Try to recur, and verification should succeed because |
922e8e29 | 141 | // the inner HASH160 <> EQUAL should only check the hash: |
0be990ba | 142 | CScript p2sh2 = GetScriptForDestination(p2sh.GetID()); |
922e8e29 GA |
143 | CScript scriptSig2; |
144 | scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh); | |
145 | ||
146 | BOOST_CHECK(Verify(scriptSig2, p2sh2, true)); | |
147 | } | |
148 | ||
149 | BOOST_AUTO_TEST_CASE(set) | |
150 | { | |
ed671005 | 151 | LOCK(cs_main); |
922e8e29 GA |
152 | // Test the CScript::Set* methods |
153 | CBasicKeyStore keystore; | |
154 | CKey key[4]; | |
dfa23b94 | 155 | std::vector<CPubKey> keys; |
922e8e29 GA |
156 | for (int i = 0; i < 4; i++) |
157 | { | |
0d56f11a | 158 | key[i].MakeNewKey(true); |
922e8e29 | 159 | keystore.AddKey(key[i]); |
dfa23b94 | 160 | keys.push_back(key[i].GetPubKey()); |
922e8e29 GA |
161 | } |
162 | ||
163 | CScript inner[4]; | |
0be990ba PW |
164 | inner[0] = GetScriptForDestination(key[0].GetPubKey().GetID()); |
165 | inner[1] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2)); | |
166 | inner[2] = GetScriptForMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2)); | |
167 | inner[3] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3)); | |
922e8e29 GA |
168 | |
169 | CScript outer[4]; | |
170 | for (int i = 0; i < 4; i++) | |
171 | { | |
0be990ba | 172 | outer[i] = GetScriptForDestination(inner[i].GetID()); |
922e8e29 GA |
173 | keystore.AddCScript(inner[i]); |
174 | } | |
175 | ||
4949004d | 176 | CMutableTransaction txFrom; // Funding transaction: |
980bfe6e | 177 | string reason; |
922e8e29 GA |
178 | txFrom.vout.resize(4); |
179 | for (int i = 0; i < 4; i++) | |
180 | { | |
181 | txFrom.vout[i].scriptPubKey = outer[i]; | |
8de9bb53 | 182 | txFrom.vout[i].nValue = CENT; |
922e8e29 | 183 | } |
980bfe6e | 184 | BOOST_CHECK(IsStandardTx(txFrom, reason)); |
922e8e29 | 185 | |
4949004d | 186 | CMutableTransaction txTo[4]; // Spending transactions |
922e8e29 GA |
187 | for (int i = 0; i < 4; i++) |
188 | { | |
189 | txTo[i].vin.resize(1); | |
190 | txTo[i].vout.resize(1); | |
191 | txTo[i].vin[0].prevout.n = i; | |
192 | txTo[i].vin[0].prevout.hash = txFrom.GetHash(); | |
8de9bb53 | 193 | txTo[i].vout[0].nValue = 1*CENT; |
922e8e29 | 194 | txTo[i].vout[0].scriptPubKey = inner[i]; |
0d2fa14a | 195 | #ifdef ENABLE_WALLET |
922e8e29 | 196 | BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); |
0d2fa14a | 197 | #endif |
922e8e29 GA |
198 | } |
199 | for (int i = 0; i < 4; i++) | |
200 | { | |
201 | BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); | |
980bfe6e | 202 | BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i)); |
922e8e29 GA |
203 | } |
204 | } | |
205 | ||
206 | BOOST_AUTO_TEST_CASE(is) | |
207 | { | |
208 | // Test CScript::IsPayToScriptHash() | |
209 | uint160 dummy; | |
210 | CScript p2sh; | |
211 | p2sh << OP_HASH160 << dummy << OP_EQUAL; | |
212 | BOOST_CHECK(p2sh.IsPayToScriptHash()); | |
213 | ||
214 | // Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes: | |
215 | static const unsigned char direct[] = { OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; | |
216 | BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToScriptHash()); | |
217 | static const unsigned char pushdata1[] = { OP_HASH160, OP_PUSHDATA1, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; | |
218 | BOOST_CHECK(!CScript(pushdata1, pushdata1+sizeof(pushdata1)).IsPayToScriptHash()); | |
219 | static const unsigned char pushdata2[] = { OP_HASH160, OP_PUSHDATA2, 20,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; | |
220 | BOOST_CHECK(!CScript(pushdata2, pushdata2+sizeof(pushdata2)).IsPayToScriptHash()); | |
221 | static const unsigned char pushdata4[] = { OP_HASH160, OP_PUSHDATA4, 20,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; | |
222 | BOOST_CHECK(!CScript(pushdata4, pushdata4+sizeof(pushdata4)).IsPayToScriptHash()); | |
223 | ||
224 | CScript not_p2sh; | |
225 | BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); | |
226 | ||
227 | not_p2sh.clear(); not_p2sh << OP_HASH160 << dummy << dummy << OP_EQUAL; | |
228 | BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); | |
229 | ||
230 | not_p2sh.clear(); not_p2sh << OP_NOP << dummy << OP_EQUAL; | |
231 | BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); | |
232 | ||
233 | not_p2sh.clear(); not_p2sh << OP_HASH160 << dummy << OP_CHECKSIG; | |
234 | BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); | |
235 | } | |
236 | ||
237 | BOOST_AUTO_TEST_CASE(switchover) | |
238 | { | |
814efd6f | 239 | // Test switch over code |
922e8e29 GA |
240 | CScript notValid; |
241 | notValid << OP_11 << OP_12 << OP_EQUALVERIFY; | |
242 | CScript scriptSig; | |
243 | scriptSig << Serialize(notValid); | |
244 | ||
0be990ba | 245 | CScript fund = GetScriptForDestination(notValid.GetID()); |
922e8e29 GA |
246 | |
247 | ||
248 | // Validation should succeed under old rules (hash is correct): | |
249 | BOOST_CHECK(Verify(scriptSig, fund, false)); | |
250 | // Fail under new: | |
251 | BOOST_CHECK(!Verify(scriptSig, fund, true)); | |
252 | } | |
253 | ||
254 | BOOST_AUTO_TEST_CASE(AreInputsStandard) | |
255 | { | |
ed671005 | 256 | LOCK(cs_main); |
450cbb09 PW |
257 | CCoinsView coinsDummy; |
258 | CCoinsViewCache coins(coinsDummy); | |
922e8e29 | 259 | CBasicKeyStore keystore; |
7f3b4e95 | 260 | CKey key[6]; |
dfa23b94 | 261 | vector<CPubKey> keys; |
7f3b4e95 | 262 | for (int i = 0; i < 6; i++) |
922e8e29 | 263 | { |
0d56f11a | 264 | key[i].MakeNewKey(true); |
922e8e29 | 265 | keystore.AddKey(key[i]); |
922e8e29 | 266 | } |
7f3b4e95 GA |
267 | for (int i = 0; i < 3; i++) |
268 | keys.push_back(key[i].GetPubKey()); | |
922e8e29 | 269 | |
4949004d | 270 | CMutableTransaction txFrom; |
7f3b4e95 | 271 | txFrom.vout.resize(7); |
922e8e29 GA |
272 | |
273 | // First three are standard: | |
0be990ba | 274 | CScript pay1 = GetScriptForDestination(key[0].GetPubKey().GetID()); |
922e8e29 | 275 | keystore.AddCScript(pay1); |
0be990ba | 276 | CScript pay1of3 = GetScriptForMultisig(1, keys); |
922e8e29 | 277 | |
0be990ba | 278 | txFrom.vout[0].scriptPubKey = GetScriptForDestination(pay1.GetID()); // P2SH (OP_CHECKSIG) |
450cbb09 | 279 | txFrom.vout[0].nValue = 1000; |
7f3b4e95 | 280 | txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG |
450cbb09 | 281 | txFrom.vout[1].nValue = 2000; |
7f3b4e95 | 282 | txFrom.vout[2].scriptPubKey = pay1of3; // ordinary OP_CHECKMULTISIG |
450cbb09 | 283 | txFrom.vout[2].nValue = 3000; |
922e8e29 | 284 | |
7f3b4e95 GA |
285 | // vout[3] is complicated 1-of-3 AND 2-of-3 |
286 | // ... that is OK if wrapped in P2SH: | |
287 | CScript oneAndTwo; | |
288 | oneAndTwo << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey(); | |
289 | oneAndTwo << OP_3 << OP_CHECKMULTISIGVERIFY; | |
290 | oneAndTwo << OP_2 << key[3].GetPubKey() << key[4].GetPubKey() << key[5].GetPubKey(); | |
291 | oneAndTwo << OP_3 << OP_CHECKMULTISIG; | |
292 | keystore.AddCScript(oneAndTwo); | |
0be990ba | 293 | txFrom.vout[3].scriptPubKey = GetScriptForDestination(oneAndTwo.GetID()); |
450cbb09 | 294 | txFrom.vout[3].nValue = 4000; |
7f3b4e95 GA |
295 | |
296 | // vout[4] is max sigops: | |
297 | CScript fifteenSigops; fifteenSigops << OP_1; | |
3faf1f82 | 298 | for (unsigned i = 0; i < MAX_P2SH_SIGOPS; i++) |
7f3b4e95 GA |
299 | fifteenSigops << key[i%3].GetPubKey(); |
300 | fifteenSigops << OP_15 << OP_CHECKMULTISIG; | |
301 | keystore.AddCScript(fifteenSigops); | |
0be990ba | 302 | txFrom.vout[4].scriptPubKey = GetScriptForDestination(fifteenSigops.GetID()); |
450cbb09 | 303 | txFrom.vout[4].nValue = 5000; |
7f3b4e95 GA |
304 | |
305 | // vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS | |
306 | CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG; | |
307 | keystore.AddCScript(sixteenSigops); | |
0be990ba | 308 | txFrom.vout[5].scriptPubKey = GetScriptForDestination(fifteenSigops.GetID()); |
7f3b4e95 GA |
309 | txFrom.vout[5].nValue = 5000; |
310 | CScript twentySigops; twentySigops << OP_CHECKMULTISIG; | |
311 | keystore.AddCScript(twentySigops); | |
0be990ba | 312 | txFrom.vout[6].scriptPubKey = GetScriptForDestination(twentySigops.GetID()); |
7f3b4e95 GA |
313 | txFrom.vout[6].nValue = 6000; |
314 | ||
922e8e29 | 315 | |
450cbb09 | 316 | coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); |
922e8e29 | 317 | |
4949004d | 318 | CMutableTransaction txTo; |
922e8e29 | 319 | txTo.vout.resize(1); |
0be990ba | 320 | txTo.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); |
922e8e29 | 321 | |
7f3b4e95 GA |
322 | txTo.vin.resize(5); |
323 | for (int i = 0; i < 5; i++) | |
324 | { | |
325 | txTo.vin[i].prevout.n = i; | |
326 | txTo.vin[i].prevout.hash = txFrom.GetHash(); | |
327 | } | |
922e8e29 | 328 | BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0)); |
922e8e29 | 329 | BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1)); |
922e8e29 | 330 | BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); |
7f3b4e95 GA |
331 | // SignSignature doesn't know how to sign these. We're |
332 | // not testing validating signatures, so just create | |
333 | // dummy signatures that DO include the correct P2SH scripts: | |
334 | txTo.vin[3].scriptSig << OP_11 << OP_11 << static_cast<vector<unsigned char> >(oneAndTwo); | |
335 | txTo.vin[4].scriptSig << static_cast<vector<unsigned char> >(fifteenSigops); | |
922e8e29 | 336 | |
05df3fc6 | 337 | BOOST_CHECK(::AreInputsStandard(txTo, coins)); |
7f3b4e95 GA |
338 | // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4] |
339 | BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 22U); | |
922e8e29 | 340 | |
39f0d968 GA |
341 | // Make sure adding crap to the scriptSigs makes them non-standard: |
342 | for (int i = 0; i < 3; i++) | |
343 | { | |
344 | CScript t = txTo.vin[i].scriptSig; | |
345 | txTo.vin[i].scriptSig = (CScript() << 11) + t; | |
05df3fc6 | 346 | BOOST_CHECK(!::AreInputsStandard(txTo, coins)); |
39f0d968 GA |
347 | txTo.vin[i].scriptSig = t; |
348 | } | |
349 | ||
7f3b4e95 GA |
350 | CMutableTransaction txToNonStd1; |
351 | txToNonStd1.vout.resize(1); | |
0be990ba | 352 | txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); |
7f3b4e95 GA |
353 | txToNonStd1.vout[0].nValue = 1000; |
354 | txToNonStd1.vin.resize(1); | |
355 | txToNonStd1.vin[0].prevout.n = 5; | |
356 | txToNonStd1.vin[0].prevout.hash = txFrom.GetHash(); | |
357 | txToNonStd1.vin[0].scriptSig << static_cast<vector<unsigned char> >(sixteenSigops); | |
358 | ||
359 | BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins)); | |
360 | BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U); | |
361 | ||
362 | CMutableTransaction txToNonStd2; | |
363 | txToNonStd2.vout.resize(1); | |
0be990ba | 364 | txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); |
7f3b4e95 GA |
365 | txToNonStd2.vout[0].nValue = 1000; |
366 | txToNonStd2.vin.resize(1); | |
367 | txToNonStd2.vin[0].prevout.n = 6; | |
368 | txToNonStd2.vin[0].prevout.hash = txFrom.GetHash(); | |
369 | txToNonStd2.vin[0].scriptSig << static_cast<vector<unsigned char> >(twentySigops); | |
370 | ||
371 | BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins)); | |
372 | BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U); | |
922e8e29 GA |
373 | } |
374 | ||
375 | BOOST_AUTO_TEST_SUITE_END() |