1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BitWasp\Bitcoin\Script\Interpreter; |
4
|
|
|
|
5
|
|
|
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface; |
6
|
|
|
use BitWasp\Bitcoin\Crypto\Hash; |
7
|
|
|
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical; |
8
|
|
|
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException; |
9
|
|
|
use BitWasp\Bitcoin\Flags; |
10
|
|
|
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey; |
11
|
|
|
use BitWasp\Bitcoin\Key\PublicKeyFactory; |
12
|
|
|
use BitWasp\Bitcoin\Script\Interpreter\Stack; |
13
|
|
|
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier; |
14
|
|
|
use BitWasp\Bitcoin\Script\Opcodes; |
15
|
|
|
use BitWasp\Bitcoin\Script\Script; |
16
|
|
|
use BitWasp\Bitcoin\Script\ScriptInterface; |
17
|
|
|
use BitWasp\Bitcoin\Signature\TransactionSignature; |
18
|
|
|
use BitWasp\Bitcoin\Signature\TransactionSignatureFactory; |
19
|
|
|
use BitWasp\Bitcoin\Transaction\SignatureHash\SignatureHashInterface; |
20
|
|
|
use BitWasp\Bitcoin\Transaction\TransactionInterface; |
21
|
|
|
use BitWasp\Buffertools\Buffer; |
22
|
|
|
|
23
|
|
|
class Interpreter implements InterpreterInterface |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* @var int|string |
27
|
|
|
*/ |
28
|
|
|
private $inputToSign; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var ScriptInterface |
32
|
|
|
*/ |
33
|
|
|
private $script; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var TransactionInterface |
37
|
|
|
*/ |
38
|
|
|
private $transaction; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Position of OP_CODESEPARATOR, for calculating SigHash |
42
|
|
|
* @var int |
43
|
|
|
*/ |
44
|
|
|
private $hashStartPos; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var int |
48
|
|
|
*/ |
49
|
|
|
private $opCount; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var \BitWasp\Bitcoin\Flags |
53
|
|
|
*/ |
54
|
|
|
private $flags; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var EcAdapterInterface |
58
|
|
|
*/ |
59
|
|
|
private $ecAdapter; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var \BitWasp\Bitcoin\Math\Math |
63
|
|
|
*/ |
64
|
|
|
private $math; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var State |
68
|
|
|
*/ |
69
|
|
|
private $state; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var array |
73
|
|
|
*/ |
74
|
|
|
private $disabledOps = [ |
75
|
|
|
Opcodes::OP_CAT, Opcodes::OP_SUBSTR, Opcodes::OP_LEFT, Opcodes::OP_RIGHT, |
76
|
|
|
Opcodes::OP_INVERT, Opcodes::OP_AND, Opcodes::OP_OR, Opcodes::OP_XOR, |
77
|
|
|
Opcodes::OP_2MUL, Opcodes::OP_2DIV, Opcodes::OP_MUL, Opcodes::OP_DIV, |
78
|
|
|
Opcodes::OP_MOD, Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT |
79
|
|
|
]; |
80
|
|
|
|
81
|
|
|
public $checkDisabledOpcodes = true; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param EcAdapterInterface $ecAdapter |
85
|
|
|
* @param TransactionInterface $transaction |
86
|
|
|
* @param \BitWasp\Bitcoin\Flags $flags |
87
|
|
|
*/ |
88
|
1488 |
|
public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction, Flags $flags) |
89
|
|
|
{ |
90
|
1488 |
|
$this->ecAdapter = $ecAdapter; |
91
|
1488 |
|
$this->math = $ecAdapter->getMath(); |
92
|
1488 |
|
$this->transaction = $transaction; |
93
|
1488 |
|
$this->flags = $flags; |
94
|
1488 |
|
$this->script = new Script(); |
95
|
1488 |
|
$this->state = new State(); |
96
|
1488 |
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @return State |
100
|
|
|
*/ |
101
|
6 |
|
public function getStackState() |
102
|
|
|
{ |
103
|
6 |
|
return $this->state; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @param ScriptInterface $script |
108
|
|
|
* @return $this |
109
|
|
|
*/ |
110
|
1392 |
|
public function setScript(ScriptInterface $script) |
111
|
|
|
{ |
112
|
1392 |
|
$this->script = $script; |
113
|
1392 |
|
return $this; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @return array |
118
|
|
|
*/ |
119
|
|
|
public function getDisabledOps() |
120
|
|
|
{ |
121
|
|
|
return $this->disabledOps; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @param int $op |
126
|
|
|
* @return bool |
127
|
|
|
*/ |
128
|
1386 |
|
public function isDisabledOp($op) |
129
|
|
|
{ |
130
|
1386 |
|
return in_array($op, $this->disabledOps, true); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Cast the value to a boolean |
135
|
|
|
* |
136
|
|
|
* @param $value |
137
|
|
|
* @return bool |
138
|
|
|
*/ |
139
|
450 |
|
public function castToBool(Buffer $value) |
140
|
|
|
{ |
141
|
450 |
|
if ($value->getSize() === 0) { |
142
|
66 |
|
return true; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
// Since we're using buffers, lets try ensuring the contents are not 0. |
146
|
384 |
|
return $this->math->cmp($value->getInt(), 0) > 0; // cscriptNum or edge case. |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param Buffer $signature |
151
|
|
|
* @return bool |
152
|
|
|
*/ |
153
|
60 |
|
public function isValidSignatureEncoding(Buffer $signature) |
154
|
|
|
{ |
155
|
|
|
try { |
156
|
60 |
|
TransactionSignature::isDERSignature($signature); |
157
|
42 |
|
return true; |
158
|
18 |
|
} catch (SignatureNotCanonical $e) { |
|
|
|
|
159
|
|
|
/* In any case, we will return false outside this block */ |
160
|
|
|
} |
161
|
|
|
|
162
|
18 |
|
return false; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @param Buffer $signature |
167
|
|
|
* @return bool |
168
|
|
|
* @throws ScriptRuntimeException |
169
|
|
|
* @throws \Exception |
170
|
|
|
*/ |
171
|
36 |
|
public function isLowDerSignature(Buffer $signature) |
172
|
|
|
{ |
173
|
36 |
|
if (!$this->isValidSignatureEncoding($signature)) { |
174
|
6 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DERSIG, 'Signature with incorrect encoding'); |
175
|
|
|
} |
176
|
|
|
|
177
|
30 |
|
$binary = $signature->getBinary(); |
178
|
30 |
|
$nLenR = ord($binary[3]); |
179
|
30 |
|
$nLenS = ord($binary[5 + $nLenR]); |
180
|
30 |
|
$s = $signature->slice(6 + $nLenR, $nLenS)->getInt(); |
181
|
|
|
|
182
|
30 |
|
return $this->ecAdapter->validateSignatureElement($s, true); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Determine whether the sighash byte appended to the signature encodes |
187
|
|
|
* a valid sighash type. |
188
|
|
|
* |
189
|
|
|
* @param Buffer $signature |
190
|
|
|
* @return bool |
191
|
|
|
*/ |
192
|
42 |
|
public function isDefinedHashtypeSignature(Buffer $signature) |
193
|
|
|
{ |
194
|
42 |
|
if ($signature->getSize() === 0) { |
195
|
6 |
|
return false; |
196
|
|
|
} |
197
|
|
|
|
198
|
36 |
|
$binary = $signature->getBinary(); |
199
|
36 |
|
$nHashType = ord(substr($binary, -1)) & (~(SignatureHashInterface::SIGHASH_ANYONECANPAY)); |
200
|
|
|
|
201
|
36 |
|
$math = $this->math; |
202
|
36 |
|
return ! ($math->cmp($nHashType, SignatureHashInterface::SIGHASH_ALL) < 0 || $math->cmp($nHashType, SignatureHashInterface::SIGHASH_SINGLE) > 0); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* @param Buffer $signature |
207
|
|
|
* @return $this |
208
|
|
|
* @throws \BitWasp\Bitcoin\Exceptions\ScriptRuntimeException |
209
|
|
|
*/ |
210
|
66 |
|
public function checkSignatureEncoding(Buffer $signature) |
211
|
|
|
{ |
212
|
66 |
|
if ($signature->getSize() === 0) { |
213
|
6 |
|
return $this; |
214
|
|
|
} |
215
|
|
|
|
216
|
60 |
|
if ($this->flags->checkFlags(InterpreterInterface::VERIFY_DERSIG | InterpreterInterface::VERIFY_LOW_S | InterpreterInterface::VERIFY_STRICTENC) && !$this->isValidSignatureEncoding($signature)) { |
217
|
12 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DERSIG, 'Signature with incorrect encoding'); |
218
|
48 |
|
} else if ($this->flags->checkFlags(InterpreterInterface::VERIFY_LOW_S) && !$this->isLowDerSignature($signature)) { |
219
|
6 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_LOW_S, 'Signature s element was not low'); |
220
|
42 |
|
} else if ($this->flags->checkFlags(InterpreterInterface::VERIFY_STRICTENC) && !$this->isDefinedHashtypeSignature($signature)) { |
221
|
6 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_STRICTENC, 'Signature with invalid hashtype'); |
222
|
|
|
} |
223
|
|
|
|
224
|
36 |
|
return $this; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* @param Buffer $publicKey |
229
|
|
|
* @return $this |
230
|
|
|
* @throws \Exception |
231
|
|
|
*/ |
232
|
30 |
|
public function checkPublicKeyEncoding(Buffer $publicKey) |
233
|
|
|
{ |
234
|
30 |
|
if ($this->flags->checkFlags(InterpreterInterface::VERIFY_STRICTENC) && !PublicKey::isCompressedOrUncompressed($publicKey)) { |
235
|
6 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_STRICTENC, 'Public key with incorrect encoding'); |
236
|
|
|
} |
237
|
|
|
|
238
|
24 |
|
return $this; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* @param $opCode |
243
|
|
|
* @param Buffer $pushData |
244
|
|
|
* @return bool |
245
|
|
|
* @throws \Exception |
246
|
|
|
*/ |
247
|
12 |
|
public function checkMinimalPush($opCode, Buffer $pushData) |
248
|
|
|
{ |
249
|
12 |
|
$pushSize = $pushData->getSize(); |
250
|
12 |
|
$binary = $pushData->getBinary(); |
251
|
|
|
|
252
|
12 |
|
if ($pushSize === 0) { |
253
|
6 |
|
return $opCode === Opcodes::OP_0; |
254
|
12 |
|
} elseif ($pushSize === 1) { |
255
|
6 |
|
$first = ord($binary[0]); |
256
|
6 |
|
if ($first >= 1 && $first <= 16) { |
257
|
6 |
|
return $opCode === (Opcodes::OP_1 + ($first - 1)); |
258
|
6 |
|
} elseif ($first === 0x81) { |
259
|
6 |
|
return $opCode === Opcodes::OP_1NEGATE; |
260
|
|
|
} |
261
|
12 |
|
} elseif ($pushSize <= 75) { |
262
|
12 |
|
return $opCode === $pushSize; |
263
|
6 |
|
} elseif ($pushSize <= 255) { |
264
|
6 |
|
return $opCode === Opcodes::OP_PUSHDATA1; |
265
|
6 |
|
} elseif ($pushSize <= 65535) { |
266
|
6 |
|
return $opCode === Opcodes::OP_PUSHDATA2; |
267
|
|
|
} |
268
|
|
|
|
269
|
6 |
|
return true; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @return $this |
274
|
|
|
* @throws \Exception |
275
|
|
|
*/ |
276
|
1368 |
|
private function checkOpcodeCount() |
277
|
|
|
{ |
278
|
1368 |
|
if ($this->math->cmp($this->opCount, 201) > 0) { |
279
|
6 |
|
throw new \RuntimeException('Error: Script op code count'); |
280
|
|
|
} |
281
|
|
|
|
282
|
1368 |
|
return $this; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @param ScriptInterface $script |
287
|
|
|
* @param Buffer $sigBuf |
288
|
|
|
* @param Buffer $keyBuf |
289
|
|
|
* @return bool |
290
|
|
|
* @throws ScriptRuntimeException |
291
|
|
|
* @throws \Exception |
292
|
|
|
*/ |
293
|
24 |
|
private function checkSig(ScriptInterface $script, Buffer $sigBuf, Buffer $keyBuf) |
294
|
|
|
{ |
295
|
24 |
|
$this |
296
|
24 |
|
->checkSignatureEncoding($sigBuf) |
297
|
18 |
|
->checkPublicKeyEncoding($keyBuf); |
298
|
|
|
|
299
|
|
|
try { |
300
|
18 |
|
$txSignature = TransactionSignatureFactory::fromHex($sigBuf->getHex()); |
301
|
18 |
|
$publicKey = PublicKeyFactory::fromHex($keyBuf->getHex()); |
302
|
|
|
|
303
|
18 |
|
return $this->ecAdapter->verify( |
304
|
18 |
|
$this |
305
|
|
|
->transaction |
306
|
18 |
|
->getSignatureHash() |
307
|
18 |
|
->calculate($script, $this->inputToSign, $txSignature->getHashType()), |
308
|
18 |
|
$publicKey, |
309
|
18 |
|
$txSignature->getSignature() |
310
|
18 |
|
); |
311
|
|
|
} catch (\Exception $e) { |
312
|
|
|
return false; |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @param ScriptInterface $scriptSig |
318
|
|
|
* @param ScriptInterface $scriptPubKey |
319
|
|
|
* @param int $nInputToSign |
320
|
|
|
* @return bool |
321
|
|
|
* @throws \Exception |
322
|
|
|
*/ |
323
|
1392 |
|
public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $nInputToSign) |
324
|
|
|
{ |
325
|
1392 |
|
$this->inputToSign = $nInputToSign; |
326
|
1392 |
|
if (!$this->setScript($scriptSig)->run()) { |
327
|
672 |
|
return false; |
328
|
|
|
} |
329
|
|
|
|
330
|
720 |
|
$mainStack = $this->state->getMainStack(); |
331
|
720 |
|
$stackCopy = new Stack; |
332
|
720 |
|
if ($this->flags->checkFlags(InterpreterInterface::VERIFY_P2SH)) { |
333
|
174 |
|
$stackCopy = $this->state->cloneMainStack(); |
334
|
174 |
|
} |
335
|
|
|
|
336
|
720 |
|
if (!$this->setScript($scriptPubKey)->run()) { |
337
|
270 |
|
return false; |
338
|
|
|
} |
339
|
|
|
|
340
|
450 |
|
if (count($mainStack) === 0) { |
341
|
6 |
|
return false; |
342
|
|
|
} |
343
|
|
|
|
344
|
444 |
|
if (false === $this->castToBool($mainStack[-1])) { |
345
|
6 |
|
return false; |
346
|
|
|
} |
347
|
|
|
|
348
|
438 |
|
$verifier = new OutputClassifier($scriptPubKey); |
349
|
|
|
|
350
|
438 |
|
if ($this->flags->checkFlags(InterpreterInterface::VERIFY_P2SH) && $verifier->isPayToScriptHash()) { |
351
|
18 |
|
if (!$scriptSig->isPushOnly()) { |
352
|
6 |
|
return false; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
// Restore mainStack to how it was after evaluating scriptSig |
356
|
12 |
|
$mainStack = $this->state->restoreMainStack($stackCopy)->getMainStack(); |
357
|
12 |
|
if (count($mainStack) === 0) { |
358
|
|
|
return false; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
// Load redeemscript as the scriptPubKey |
362
|
12 |
|
$scriptPubKey = new Script($mainStack[-1]); |
363
|
12 |
|
$mainStack->pop(); |
364
|
12 |
|
if (!$this->setScript($scriptPubKey)->run()) { |
365
|
6 |
|
return false; |
366
|
|
|
} |
367
|
6 |
|
} |
368
|
|
|
|
369
|
426 |
|
return true; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* @return bool |
374
|
|
|
*/ |
375
|
1386 |
|
private function checkExec() |
376
|
|
|
{ |
377
|
1386 |
|
$vfStack = $this->state->getVfStack(); |
378
|
1386 |
|
$c = 0; |
379
|
1386 |
|
$len = $vfStack->end(); |
380
|
1386 |
|
for ($i = 0; $i < $len; $i++) { |
381
|
|
|
if ($vfStack->top(0 - $len - $i) === true) { |
382
|
|
|
$c++; |
383
|
|
|
} |
384
|
|
|
} |
385
|
1386 |
|
return !(bool)$c; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* @return bool |
390
|
|
|
*/ |
391
|
1392 |
|
public function run() |
392
|
|
|
{ |
393
|
1392 |
|
$math = $this->math; |
394
|
|
|
|
395
|
1392 |
|
$flags = $this->flags; |
396
|
1392 |
|
$mainStack = $this->state->getMainStack(); |
397
|
1392 |
|
$altStack = $this->state->getAltStack(); |
398
|
1392 |
|
$vfStack = $this->state->getVfStack(); |
399
|
|
|
|
400
|
1392 |
|
$this->hashStartPos = 0; |
401
|
1392 |
|
$this->opCount = 0; |
402
|
1392 |
|
$parser = $this->script->getScriptParser(); |
403
|
1392 |
|
$vchFalse = new Buffer("", 0, $math); |
404
|
1392 |
|
$vchTrue = new Buffer("\x01", 1, $math); |
405
|
1392 |
|
$int0 = new ScriptNum($this->math, new Flags(0), $vchFalse, 4); |
406
|
1392 |
|
$int1 = new ScriptNum($this->math, new Flags(0), $vchTrue, 1); |
|
|
|
|
407
|
|
|
|
408
|
1392 |
|
if ($this->script->getBuffer()->getSize() > 10000) { |
409
|
|
|
return false; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
try { |
413
|
1392 |
|
foreach ($parser as $exec) { |
414
|
1386 |
|
$opCode = $exec->getOp(); |
415
|
1386 |
|
$pushData = $exec->getData(); |
416
|
1386 |
|
$fExec = $this->checkExec(); |
417
|
|
|
|
418
|
|
|
// If pushdata was written to, |
419
|
1386 |
|
if ($pushData instanceof Buffer && $pushData->getSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) { |
420
|
|
|
throw new \RuntimeException('Error - push size'); |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
// OP_RESERVED should not count towards opCount |
424
|
1386 |
|
if ($opCode > Opcodes::OP_16 && ++$this->opCount) { |
425
|
1368 |
|
$this->checkOpcodeCount(); |
426
|
1368 |
|
} |
427
|
|
|
|
428
|
1386 |
|
if ($this->checkDisabledOpcodes && $this->isDisabledOp($opCode)) { |
429
|
12 |
|
throw new \RuntimeException('Disabled Opcode'); |
430
|
|
|
} |
431
|
|
|
|
432
|
1386 |
|
if ($fExec && $opCode >= 0 && $opCode <= Opcodes::OP_PUSHDATA4) { |
433
|
|
|
// In range of a pushdata opcode |
434
|
450 |
|
if ($flags->checkFlags(InterpreterInterface::VERIFY_MINIMALDATA) && !$this->checkMinimalPush($opCode, $pushData)) { |
|
|
|
|
435
|
6 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_MINIMALDATA, 'Minimal pushdata required'); |
436
|
|
|
} |
437
|
|
|
|
438
|
444 |
|
$mainStack->push($pushData); |
439
|
|
|
// echo " - [pushed '" . $pushData->getHex() . "']\n"; |
440
|
1380 |
|
} elseif ($fExec || ($opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF)) { |
441
|
|
|
// echo "OPCODE - " . $this->script->getOpCodes()->getOp($opCode) . "\n"; |
442
|
|
|
switch ($opCode) { |
443
|
1356 |
|
case Opcodes::OP_1: |
444
|
1356 |
|
case Opcodes::OP_2: |
445
|
1356 |
|
case Opcodes::OP_3: |
446
|
1356 |
|
case Opcodes::OP_4: |
447
|
1356 |
|
case Opcodes::OP_5: |
448
|
1356 |
|
case Opcodes::OP_6: |
449
|
1356 |
|
case Opcodes::OP_7: |
450
|
1356 |
|
case Opcodes::OP_8: |
451
|
1356 |
|
case Opcodes::OP_9: |
452
|
1356 |
|
case Opcodes::OP_10: |
453
|
1356 |
|
case Opcodes::OP_11: |
454
|
1356 |
|
case Opcodes::OP_12: |
455
|
1356 |
|
case Opcodes::OP_13: |
456
|
1356 |
|
case Opcodes::OP_14: |
457
|
1356 |
|
case Opcodes::OP_15: |
458
|
1356 |
|
case Opcodes::OP_16: |
459
|
192 |
|
$num = $opCode - (Opcodes::OP_1 - 1); |
460
|
192 |
|
$mainStack->push(new ScriptNum($this->math, new Flags(0), new Buffer(chr($num), 4), 4)); |
461
|
192 |
|
break; |
462
|
|
|
|
463
|
1356 |
|
case $opCode >= Opcodes::OP_NOP1 && $opCode <= Opcodes::OP_NOP10: |
464
|
6 |
|
if ($flags->checkFlags(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) { |
465
|
6 |
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOPS found - this is discouraged'); |
466
|
|
|
} |
467
|
|
|
break; |
468
|
|
|
|
469
|
1350 |
|
case Opcodes::OP_NOP: |
470
|
12 |
|
break; |
471
|
|
|
|
472
|
1338 |
|
case Opcodes::OP_IF: |
473
|
1338 |
|
case Opcodes::OP_NOTIF: |
474
|
|
|
// <expression> if [statements] [else [statements]] endif |
475
|
18 |
|
$value = false; |
476
|
18 |
|
if ($fExec) { |
477
|
18 |
|
if (count($mainStack) < 1) { |
478
|
6 |
|
throw new \RuntimeException('Unbalanced conditional'); |
479
|
|
|
} |
480
|
|
|
// todo |
481
|
12 |
|
$buffer = new ScriptNum($math, $this->flags, $mainStack->pop(), 4); |
482
|
12 |
|
$value = $this->castToBool($buffer); |
483
|
12 |
|
if ($opCode === Opcodes::OP_NOTIF) { |
484
|
6 |
|
$value = !$value; |
485
|
6 |
|
} |
486
|
12 |
|
} |
487
|
12 |
|
$vfStack->push($value ? $vchTrue : $vchFalse); |
488
|
12 |
|
break; |
489
|
|
|
|
490
|
1332 |
|
case Opcodes::OP_ELSE: |
491
|
18 |
|
if (count($vfStack) === 0) { |
492
|
6 |
|
throw new \RuntimeException('Unbalanced conditional'); |
493
|
|
|
} |
494
|
12 |
|
$vfStack[-1] = !$vfStack->end() ? $vchTrue : $vchFalse; |
495
|
12 |
|
break; |
496
|
|
|
|
497
|
1326 |
|
case Opcodes::OP_ENDIF: |
498
|
18 |
|
if (count($vfStack) === 0) { |
499
|
6 |
|
throw new \RuntimeException('Unbalanced conditional'); |
500
|
|
|
} |
501
|
12 |
|
break; |
502
|
|
|
|
503
|
1320 |
|
case Opcodes::OP_VERIFY: |
504
|
24 |
|
if (count($mainStack) < 1) { |
505
|
6 |
|
throw new \RuntimeException('Invalid stack operation'); |
506
|
|
|
} |
507
|
18 |
|
$value = $this->castToBool($mainStack[-1]); |
508
|
18 |
|
if (!$value) { |
509
|
6 |
|
throw new \RuntimeException('Error: verify'); |
510
|
|
|
} |
511
|
12 |
|
$mainStack->pop(); |
512
|
12 |
|
break; |
513
|
|
|
|
514
|
1296 |
|
case Opcodes::OP_RESERVED: |
515
|
|
|
// todo |
516
|
12 |
|
break; |
517
|
|
|
|
518
|
1284 |
|
case Opcodes::OP_TOALTSTACK: |
519
|
18 |
|
if (count($mainStack) < 1) { |
520
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK'); |
521
|
|
|
} |
522
|
12 |
|
$altStack->push($mainStack->pop()); |
523
|
12 |
|
break; |
524
|
|
|
|
525
|
1278 |
|
case Opcodes::OP_FROMALTSTACK: |
526
|
12 |
|
if (count($altStack) < 1) { |
527
|
6 |
|
throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK'); |
528
|
|
|
} |
529
|
6 |
|
$mainStack->push($altStack->pop()); |
530
|
6 |
|
break; |
531
|
|
|
|
532
|
1272 |
|
case Opcodes::OP_IFDUP: |
533
|
|
|
// If top value not zero, duplicate it. |
534
|
12 |
|
if (count($mainStack) < 1) { |
535
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_IFDUP'); |
536
|
|
|
} |
537
|
6 |
|
$vch = $mainStack[-1]; |
538
|
6 |
|
if ($this->castToBool($vch)) { |
539
|
6 |
|
$mainStack->push($vch); |
540
|
6 |
|
} |
541
|
6 |
|
break; |
542
|
|
|
|
543
|
1266 |
|
case Opcodes::OP_DEPTH: |
544
|
78 |
|
$num = count($mainStack); |
545
|
78 |
|
if ($num === 0) { |
546
|
30 |
|
$depth = $vchFalse; |
547
|
30 |
|
} else { |
548
|
54 |
|
$depth = (new ScriptNum($math, $this->flags, Buffer::int($num), 4)); |
549
|
|
|
} |
550
|
|
|
|
551
|
78 |
|
$mainStack->push($depth); |
552
|
78 |
|
break; |
553
|
|
|
|
554
|
1266 |
|
case Opcodes::OP_DROP: |
555
|
6 |
|
if (count($mainStack) < 1) { |
556
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_DROP'); |
557
|
|
|
} |
558
|
|
|
$mainStack->pop(); |
559
|
|
|
break; |
560
|
1260 |
|
case Opcodes::OP_DUP: |
561
|
18 |
|
if (count($mainStack) < 1) { |
562
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_DUP'); |
563
|
|
|
} |
564
|
12 |
|
$vch = $mainStack[-1]; |
565
|
12 |
|
$mainStack->push($vch); |
566
|
12 |
|
break; |
567
|
|
|
|
568
|
1254 |
|
case Opcodes::OP_NIP: |
569
|
18 |
|
if (count($mainStack) < 2) { |
570
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_NIP'); |
571
|
|
|
} |
572
|
12 |
|
unset($mainStack[-2]); |
573
|
12 |
|
break; |
574
|
|
|
|
575
|
1248 |
|
case Opcodes::OP_OVER: |
576
|
18 |
|
if (count($mainStack) < 2) { |
577
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_OVER'); |
578
|
|
|
} |
579
|
12 |
|
$vch = $mainStack[-2]; |
580
|
12 |
|
$mainStack->push($vch); |
581
|
12 |
|
break; |
582
|
|
|
|
583
|
1242 |
|
case Opcodes::OP_ROT: |
584
|
12 |
|
if (count($mainStack) < 3) { |
585
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_ROT'); |
586
|
|
|
} |
587
|
6 |
|
$mainStack->swap(-3, -2); |
588
|
6 |
|
$mainStack->swap(-2, -1); |
589
|
6 |
|
break; |
590
|
|
|
|
591
|
1236 |
|
case Opcodes::OP_SWAP: |
592
|
12 |
|
if (count($mainStack) < 2) { |
593
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_SWAP'); |
594
|
|
|
} |
595
|
6 |
|
$mainStack->swap(-2, -1); |
596
|
6 |
|
break; |
597
|
|
|
|
598
|
1230 |
|
case Opcodes::OP_TUCK: |
599
|
12 |
|
if (count($mainStack) < 2) { |
600
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_TUCK'); |
601
|
|
|
} |
602
|
6 |
|
$vch = $mainStack[-1]; |
603
|
6 |
|
$mainStack->add(count($mainStack) - 1 - 2, $vch); |
604
|
|
|
break; |
605
|
|
|
|
606
|
1218 |
|
case Opcodes::OP_PICK: |
607
|
1218 |
|
case Opcodes::OP_ROLL: |
608
|
24 |
|
if (count($mainStack) < 2) { |
609
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_PICK'); |
610
|
|
|
} |
611
|
18 |
|
$top = $mainStack[-1]; |
612
|
18 |
|
$n = (new ScriptNum($math, $this->flags, $top, 4))->getInt(); |
613
|
18 |
|
$mainStack->pop(); |
614
|
18 |
|
if ($math->cmp($n, 0) < 0 || $math->cmp($n, count($mainStack)) >= 0) { |
615
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_PICK'); |
616
|
|
|
} |
617
|
|
|
|
618
|
12 |
|
$pos = (int) $math->sub($math->sub(0, $n), 1); |
619
|
12 |
|
$vch = $mainStack[$pos]; |
620
|
12 |
|
if ($opCode === Opcodes::OP_ROLL) { |
621
|
6 |
|
unset($mainStack[$pos]); |
622
|
6 |
|
} |
623
|
12 |
|
$mainStack->push($vch); |
624
|
12 |
|
break; |
625
|
|
|
|
626
|
1206 |
|
case Opcodes::OP_2DROP: |
627
|
12 |
|
if (count($mainStack) < 2) { |
628
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_2DROP'); |
629
|
|
|
} |
630
|
6 |
|
$mainStack->pop(); |
631
|
6 |
|
$mainStack->pop(); |
632
|
6 |
|
break; |
633
|
|
|
|
634
|
1200 |
|
case Opcodes::OP_2DUP: |
635
|
18 |
|
if (count($mainStack) < 2) { |
636
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_2DUP'); |
637
|
|
|
} |
638
|
12 |
|
$string1 = $mainStack[-2]; |
639
|
12 |
|
$string2 = $mainStack[-1]; |
640
|
12 |
|
$mainStack->push($string1); |
641
|
12 |
|
$mainStack->push($string2); |
642
|
12 |
|
break; |
643
|
|
|
|
644
|
1194 |
|
case Opcodes::OP_3DUP: |
645
|
18 |
|
if (count($mainStack) < 3) { |
646
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_3DUP'); |
647
|
|
|
} |
648
|
12 |
|
$string1 = $mainStack[-3]; |
649
|
12 |
|
$string2 = $mainStack[-2]; |
650
|
12 |
|
$string3 = $mainStack[-1]; |
651
|
12 |
|
$mainStack->push($string1); |
652
|
12 |
|
$mainStack->push($string2); |
653
|
12 |
|
$mainStack->push($string3); |
654
|
12 |
|
break; |
655
|
|
|
|
656
|
1188 |
|
case Opcodes::OP_2OVER: |
657
|
18 |
|
if (count($mainStack) < 4) { |
658
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_2OVER'); |
659
|
|
|
} |
660
|
12 |
|
$string1 = $mainStack[-4]; |
661
|
12 |
|
$string2 = $mainStack[-3]; |
662
|
12 |
|
$mainStack->push($string1); |
663
|
12 |
|
$mainStack->push($string2); |
664
|
12 |
|
break; |
665
|
|
|
|
666
|
1182 |
|
case Opcodes::OP_2ROT: |
667
|
12 |
|
if (count($mainStack) < 6) { |
668
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_2ROT'); |
669
|
|
|
} |
670
|
6 |
|
$string1 = $mainStack[-6]; |
671
|
6 |
|
$string2 = $mainStack[-5]; |
672
|
6 |
|
unset($mainStack[-6], $mainStack[-5]); |
673
|
6 |
|
$mainStack->push($string1); |
674
|
6 |
|
$mainStack->push($string2); |
675
|
6 |
|
break; |
676
|
|
|
|
677
|
1176 |
|
case Opcodes::OP_2SWAP: |
678
|
12 |
|
if (count($mainStack) < 4) { |
679
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_2SWAP'); |
680
|
|
|
} |
681
|
6 |
|
$mainStack->swap(-3, -1); |
682
|
6 |
|
$mainStack->swap(-4, -2); |
683
|
6 |
|
break; |
684
|
|
|
|
685
|
1170 |
|
case Opcodes::OP_SIZE: |
686
|
12 |
|
if (count($mainStack) < 1) { |
687
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_SIZE'); |
688
|
|
|
} |
689
|
|
|
// todo: Int sizes? |
690
|
6 |
|
$vch = $mainStack[-1]; |
691
|
6 |
|
$size = Buffer::int($vch->getSize(), null, $math); |
692
|
|
|
|
693
|
6 |
|
$mainStack->push($size); |
694
|
6 |
|
break; |
695
|
|
|
|
696
|
1164 |
|
case Opcodes::OP_EQUAL: |
697
|
1164 |
|
case Opcodes::OP_EQUALVERIFY: |
698
|
408 |
|
if (count($mainStack) < 2) { |
699
|
6 |
|
throw new \RuntimeException('Invalid stack operation OP_EQUAL'); |
700
|
|
|
} |
701
|
402 |
|
$vch1 = $mainStack[-2]; |
702
|
402 |
|
$vch2 = $mainStack[-1]; |
703
|
|
|
|
704
|
402 |
|
$equal = ($vch1->getBinary() === $vch2->getBinary()); |
705
|
|
|
|
706
|
402 |
|
$mainStack->pop(); |
707
|
402 |
|
$mainStack->pop(); |
708
|
402 |
|
$mainStack->push(($equal ? $vchTrue : $vchFalse)); |
709
|
402 |
|
if ($opCode === Opcodes::OP_EQUALVERIFY) { |
710
|
114 |
|
if ($equal) { |
711
|
102 |
|
$mainStack->pop(); |
712
|
102 |
|
} else { |
713
|
12 |
|
throw new \RuntimeException('Error EQUALVERIFY'); |
714
|
|
|
} |
715
|
102 |
|
} |
716
|
|
|
|
717
|
390 |
|
break; |
718
|
|
|
|
719
|
|
|
// Arithmetic operations |
720
|
978 |
|
case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL: |
721
|
48 |
|
if (count($mainStack) < 1) { |
722
|
6 |
|
throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL'); |
723
|
|
|
} |
724
|
|
|
|
725
|
42 |
|
$num = (new ScriptNum($math, $this->flags, $mainStack[-1], 4))->getInt(); |
726
|
|
|
|
727
|
42 |
|
if ($opCode === Opcodes::OP_1ADD) { // cscriptnum |
728
|
6 |
|
$num = $math->add($num, '1'); |
729
|
42 |
|
} elseif ($opCode === Opcodes::OP_1SUB) { |
730
|
6 |
|
$num = $math->sub($num, '1'); |
731
|
36 |
|
} elseif ($opCode === Opcodes::OP_2MUL) { |
732
|
|
|
$num = $math->mul(2, $num); |
733
|
30 |
|
} elseif ($opCode === Opcodes::OP_NEGATE) { |
734
|
|
|
$num = $math->sub(0, $num); |
735
|
30 |
|
} elseif ($opCode === Opcodes::OP_ABS) { |
736
|
|
|
if ($math->cmp($num, '0') < 0) { |
737
|
|
|
$num = $math->sub(0, $num); |
738
|
|
|
} |
739
|
30 |
|
} elseif ($opCode === Opcodes::OP_NOT) { |
740
|
18 |
|
$num = ($math->cmp($num, '0') === 0); |
741
|
18 |
|
} else { |
742
|
|
|
// is OP_0NOTEQUAL |
743
|
12 |
|
$num = ($math->cmp($num, '0') !== 0); |
744
|
|
|
} |
745
|
|
|
|
746
|
42 |
|
$mainStack->pop(); |
747
|
|
|
|
748
|
42 |
|
$buffer = Buffer::int($num, 4, $math); |
749
|
42 |
|
$mainStack->push($buffer); |
750
|
42 |
|
break; |
751
|
|
|
|
752
|
930 |
|
case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX: |
753
|
150 |
|
if (count($mainStack) < 2) { |
754
|
24 |
|
throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)'); |
755
|
|
|
} |
756
|
|
|
|
757
|
126 |
|
$num1 = (new ScriptNum($math, $this->flags, $mainStack[-2], 4))->getInt(); |
758
|
126 |
|
$num2 = (new ScriptNum($math, $this->flags, $mainStack[-1], 4))->getInt(); |
759
|
|
|
|
760
|
126 |
|
if ($opCode === Opcodes::OP_ADD) { |
761
|
6 |
|
$num = $math->add($num1, $num2); |
762
|
126 |
|
} else if ($opCode === Opcodes::OP_SUB) { |
763
|
6 |
|
$num = $math->sub($num1, $num2); |
764
|
120 |
|
} else if ($opCode === Opcodes::OP_BOOLAND) { |
765
|
12 |
|
$num = $math->cmp($num1, $int0->getInt()) !== 0 && $math->cmp($num2, $int0->getInt()) !== 0; |
766
|
114 |
|
} else if ($opCode === Opcodes::OP_BOOLOR) { |
767
|
18 |
|
$num = $math->cmp($num1, $int0->getInt()) !== 0 || $math->cmp($num2, $int0->getInt()) !== 0; |
768
|
102 |
|
} elseif ($opCode === Opcodes::OP_NUMEQUAL) { |
769
|
18 |
|
$num = $math->cmp($num1, $num2) === 0; |
770
|
84 |
|
} elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) { |
771
|
|
|
$num = $math->cmp($num1, $num2) === 0; |
772
|
66 |
|
} elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) { |
773
|
6 |
|
$num = $math->cmp($num1, $num2) !== 0; |
774
|
66 |
|
} elseif ($opCode === Opcodes::OP_LESSTHAN) { // cscriptnum |
775
|
12 |
|
$num = $math->cmp($num1, $num2) < 0; |
776
|
60 |
|
} elseif ($opCode === Opcodes::OP_GREATERTHAN) { |
777
|
12 |
|
$num = $math->cmp($num1, $num2) > 0; |
778
|
48 |
|
} elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) { // cscriptnum |
779
|
12 |
|
$num = $math->cmp($num1, $num2) <= 0; |
780
|
36 |
|
} elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) { |
781
|
12 |
|
$num = $math->cmp($num1, $num2) >= 0; |
782
|
24 |
|
} elseif ($opCode === Opcodes::OP_MIN) { |
783
|
6 |
|
$num = ($math->cmp($num1, $num2) <= 0) ? $num1 : $num2; |
784
|
6 |
|
} else { |
785
|
6 |
|
$num = ($math->cmp($num1, $num2) >= 0) ? $num1 : $num2; |
786
|
|
|
} |
787
|
|
|
|
788
|
126 |
|
$mainStack->pop(); |
789
|
126 |
|
$mainStack->pop(); |
790
|
126 |
|
$buffer = Buffer::int($num, 4, $math); |
791
|
126 |
|
$mainStack->push($buffer); |
792
|
|
|
|
793
|
126 |
|
if ($opCode === Opcodes::OP_NUMEQUALVERIFY) { |
794
|
|
|
if ($this->castToBool($mainStack[-1])) { |
795
|
|
|
$mainStack->pop(); |
796
|
|
|
} else { |
797
|
|
|
throw new \RuntimeException('NUM EQUAL VERIFY error'); |
798
|
|
|
} |
799
|
|
|
} |
800
|
126 |
|
break; |
801
|
|
|
|
802
|
780 |
|
case Opcodes::OP_WITHIN: |
803
|
12 |
|
if (count($mainStack) < 3) { |
804
|
6 |
|
throw new \RuntimeException('Invalid stack operation'); |
805
|
|
|
} |
806
|
|
|
|
807
|
6 |
|
$num1 = (new ScriptNum($math, $this->flags, $mainStack[-3], 4))->getInt(); |
808
|
6 |
|
$num2 = (new ScriptNum($math, $this->flags, $mainStack[-2], 4))->getInt(); |
809
|
6 |
|
$num3 = (new ScriptNum($math, $this->flags, $mainStack[-1], 4))->getInt(); |
810
|
|
|
|
811
|
6 |
|
$value = $math->cmp($num2, $num1) <= 0 && $math->cmp($num1, $num3) < 0; |
812
|
6 |
|
$mainStack->pop(); |
813
|
6 |
|
$mainStack->pop(); |
814
|
6 |
|
$mainStack->pop(); |
815
|
6 |
|
$mainStack->push($value ? $vchFalse : $vchTrue); |
816
|
6 |
|
break; |
817
|
|
|
|
818
|
|
|
// Hash operation |
819
|
768 |
|
case Opcodes::OP_RIPEMD160: |
820
|
768 |
|
case Opcodes::OP_SHA1: |
821
|
768 |
|
case Opcodes::OP_SHA256: |
822
|
768 |
|
case Opcodes::OP_HASH160: |
823
|
768 |
|
case Opcodes::OP_HASH256: |
824
|
66 |
|
if (count($mainStack) < 1) { |
825
|
12 |
|
throw new \RuntimeException('Invalid stack operation'); |
826
|
|
|
} |
827
|
|
|
|
828
|
54 |
|
$buffer = $mainStack[-1]; |
829
|
54 |
|
if ($opCode === Opcodes::OP_RIPEMD160) { |
830
|
6 |
|
$hash = Hash::ripemd160($buffer); |
831
|
54 |
|
} elseif ($opCode === Opcodes::OP_SHA1) { |
832
|
6 |
|
$hash = Hash::sha1($buffer); |
833
|
48 |
|
} elseif ($opCode === Opcodes::OP_SHA256) { |
834
|
6 |
|
$hash = Hash::sha256($buffer); |
835
|
42 |
|
} elseif ($opCode === Opcodes::OP_HASH160) { |
836
|
30 |
|
$hash = Hash::sha256ripe160($buffer); |
837
|
30 |
|
} else { |
838
|
6 |
|
$hash = Hash::sha256d($buffer); |
839
|
|
|
} |
840
|
|
|
|
841
|
54 |
|
$mainStack->pop(); |
842
|
54 |
|
$mainStack->push($hash); |
843
|
54 |
|
break; |
844
|
|
|
|
845
|
720 |
|
case Opcodes::OP_CODESEPARATOR: |
846
|
|
|
$this->hashStartPos = $parser->getPosition(); |
847
|
|
|
break; |
848
|
|
|
|
849
|
720 |
|
case Opcodes::OP_CHECKSIG: |
850
|
720 |
|
case Opcodes::OP_CHECKSIGVERIFY: |
851
|
24 |
|
if (count($mainStack) < 2) { |
852
|
6 |
|
throw new \RuntimeException('Invalid stack operation'); |
853
|
|
|
} |
854
|
|
|
|
855
|
18 |
|
$vchPubKey = $mainStack[-1]; |
856
|
18 |
|
$vchSig = $mainStack[-2]; |
857
|
|
|
|
858
|
18 |
|
$scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos)); |
859
|
|
|
|
860
|
18 |
|
$success = $this->checkSig($scriptCode, $vchSig, $vchPubKey); |
861
|
|
|
|
862
|
12 |
|
$mainStack->pop(); |
863
|
12 |
|
$mainStack->pop(); |
864
|
12 |
|
$mainStack->push($success ? $vchTrue : $vchFalse); |
865
|
|
|
|
866
|
12 |
|
if ($opCode === Opcodes::OP_CHECKSIGVERIFY) { |
867
|
|
|
if ($success) { |
868
|
|
|
$mainStack->pop(); |
869
|
|
|
} else { |
870
|
|
|
throw new \RuntimeException('Checksig verify'); |
871
|
|
|
} |
872
|
|
|
} |
873
|
|
|
|
874
|
12 |
|
break; |
875
|
|
|
|
876
|
696 |
|
case Opcodes::OP_CHECKMULTISIG: |
877
|
696 |
|
case Opcodes::OP_CHECKMULTISIGVERIFY: |
878
|
6 |
|
$i = 1; |
879
|
6 |
|
if (count($mainStack) < $i) { |
880
|
|
|
throw new \RuntimeException('Invalid stack operation'); |
881
|
|
|
} |
882
|
|
|
|
883
|
6 |
|
$keyCount = $mainStack[-$i]->getInt(); |
884
|
6 |
|
if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) { |
885
|
|
|
throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20'); |
886
|
|
|
} |
887
|
6 |
|
$this->opCount += $keyCount; |
888
|
6 |
|
$this->checkOpcodeCount(); |
889
|
|
|
|
890
|
|
|
// Extract positions of the keys, and signatures, from the stack. |
891
|
6 |
|
$ikey = ++$i; |
892
|
6 |
|
$i += $keyCount; /** @var int $i */ |
893
|
6 |
|
if (count($mainStack) < $i) { |
894
|
|
|
throw new \RuntimeException('Invalid stack operation'); |
895
|
|
|
} |
896
|
|
|
|
897
|
6 |
|
$sigCount = $mainStack[-$i]->getInt(); // cscriptnum |
898
|
6 |
|
if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) { |
899
|
|
|
throw new \RuntimeException('Invalid Signature count'); |
900
|
|
|
} |
901
|
6 |
|
$isig = ++$i; |
902
|
6 |
|
$i += $sigCount; |
903
|
|
|
|
904
|
|
|
// Extract the script since the last OP_CODESEPARATOR |
905
|
6 |
|
$scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos)); |
906
|
|
|
|
907
|
6 |
|
$fSuccess = true; |
908
|
6 |
|
while ($fSuccess && $sigCount > 0) { |
909
|
|
|
// Fetch the signature and public key |
910
|
6 |
|
$sig = $mainStack[-$isig]; |
911
|
6 |
|
$pubkey = $mainStack[-$ikey]; |
912
|
|
|
|
913
|
|
|
// Erase the signature and public key. |
914
|
6 |
|
unset($mainStack[-$isig], $mainStack[-$ikey]); |
915
|
|
|
|
916
|
|
|
// Decrement $i, since we are consuming stack values. |
917
|
6 |
|
$i -= 2; |
918
|
|
|
|
919
|
6 |
|
if ($this->checkSig($scriptCode, $sig, $pubkey)) { |
920
|
6 |
|
$isig++; |
921
|
6 |
|
$sigCount--; |
922
|
6 |
|
} |
923
|
|
|
|
924
|
6 |
|
$ikey++; |
925
|
6 |
|
$keyCount--; |
926
|
|
|
|
927
|
|
|
// If there are more signatures left than keys left, |
928
|
|
|
// then too many signatures have failed. Exit early, |
929
|
|
|
// without checking any further signatures. |
930
|
6 |
|
if ($sigCount > $keyCount) { |
931
|
|
|
$fSuccess = false; |
932
|
|
|
} |
933
|
6 |
|
} |
934
|
|
|
|
935
|
6 |
|
while ($i-- > 1) { |
936
|
6 |
|
$mainStack->pop(); |
937
|
6 |
|
} |
938
|
|
|
|
939
|
|
|
// A bug causes CHECKMULTISIG to consume one extra argument |
940
|
|
|
// whose contents were not checked in any way. |
941
|
|
|
// |
942
|
|
|
// Unfortunately this is a potential source of mutability, |
943
|
|
|
// so optionally verify it is exactly equal to zero prior |
944
|
|
|
// to removing it from the stack. |
945
|
6 |
|
if (count($mainStack) < 1) { |
946
|
|
|
throw new \RuntimeException('Invalid stack operation'); |
947
|
|
|
} |
948
|
|
|
|
949
|
6 |
|
if ($flags->checkFlags(InterpreterInterface::VERIFY_NULL_DUMMY) && $mainStack[-1]->getSize()) { |
950
|
|
|
throw new ScriptRuntimeException(InterpreterInterface::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0'); |
951
|
|
|
} |
952
|
|
|
|
953
|
6 |
|
$mainStack->pop(); |
954
|
6 |
|
$mainStack->push($fSuccess ? $vchTrue : $vchFalse); |
955
|
|
|
|
956
|
6 |
|
if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) { |
957
|
|
|
if ($fSuccess) { |
958
|
|
|
$mainStack->pop(); |
959
|
|
|
} else { |
960
|
|
|
throw new \RuntimeException('OP_CHECKMULTISIG verify'); |
961
|
|
|
} |
962
|
|
|
} |
963
|
6 |
|
break; |
964
|
|
|
|
965
|
690 |
|
default: |
966
|
690 |
|
throw new \RuntimeException('Opcode not found'); |
967
|
690 |
|
} |
968
|
|
|
|
969
|
438 |
|
if (count($mainStack) + count($altStack) > 1000) { |
970
|
|
|
throw new \RuntimeException('Invalid stack size, exceeds 1000'); |
971
|
|
|
} |
972
|
438 |
|
} |
973
|
720 |
|
} |
974
|
|
|
|
975
|
720 |
|
if (!$vfStack->end() === 0) { |
976
|
|
|
throw new \RuntimeException('Unbalanced conditional at script end'); |
977
|
|
|
} |
978
|
|
|
|
979
|
720 |
|
return true; |
980
|
948 |
|
} catch (ScriptRuntimeException $e) { |
981
|
|
|
// echo "\n Runtime: " . $e->getMessage() . "\n"; |
982
|
|
|
// Failure due to script tags, can access flag: $e->getFailureFlag() |
983
|
18 |
|
return false; |
984
|
930 |
|
} catch (\Exception $e) { |
985
|
|
|
// echo "\n General: " . $e->getMessage() ; |
986
|
930 |
|
return false; |
987
|
|
|
} |
988
|
|
|
} |
989
|
|
|
} |
990
|
|
|
|
This check looks for ``catch` blocks that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.
Empty
catch
blocks will swallow any caught exception, sometimes causing bugs in your code that are very hard to debug. Consider logging the exception to a debug log or re-throwing it in some way, shape or form.