Interpreter::verify()   F
last analyzed

Complexity

Conditions 35
Paths 1176

Size

Total Lines 121
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 59
CRAP Score 35.1388

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 35
eloc 61
c 1
b 0
f 0
nc 1176
nop 5
dl 0
loc 121
ccs 59
cts 62
cp 0.9516
crap 35.1388
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Script\Interpreter;
6
7
use BitWasp\Bitcoin\Bitcoin;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
9
use BitWasp\Bitcoin\Crypto\Hash;
10
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
11
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
12
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
13
use BitWasp\Bitcoin\Script\Opcodes;
14
use BitWasp\Bitcoin\Script\Script;
15
use BitWasp\Bitcoin\Script\ScriptFactory;
16
use BitWasp\Bitcoin\Script\ScriptInterface;
17
use BitWasp\Bitcoin\Script\ScriptWitness;
18
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
19
use BitWasp\Bitcoin\Script\WitnessProgram;
20
use BitWasp\Bitcoin\Signature\TransactionSignature;
21
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
22
use BitWasp\Bitcoin\Transaction\TransactionInputInterface;
23
use BitWasp\Buffertools\Buffer;
24
use BitWasp\Buffertools\BufferInterface;
25
26
class Interpreter implements InterpreterInterface
27
{
28
29
    /**
30
     * @var \BitWasp\Bitcoin\Math\Math
31
     */
32
    private $math;
33
34
    /**
35
     * @var BufferInterface
36
     */
37
    private $vchFalse;
38
39
    /**
40
     * @var BufferInterface
41
     */
42
    private $vchTrue;
43
44
    /**
45
     * @var array
46
     */
47
    private $disabledOps = [
48
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
49
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
50
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
51
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
52
    ];
53
54
    /**
55
     * @param EcAdapterInterface $ecAdapter
56
     */
57 2468
    public function __construct(EcAdapterInterface $ecAdapter = null)
58
    {
59 2468
        $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter();
60 2468
        $this->math = $ecAdapter->getMath();
61 2468
        $this->vchFalse = new Buffer("", 0);
62 2468
        $this->vchTrue = new Buffer("\x01", 1);
63 2468
    }
64
65
    /**
66
     * Cast the value to a boolean
67
     *
68
     * @param BufferInterface $value
69
     * @return bool
70
     */
71 3518
    public function castToBool(BufferInterface $value): bool
72
    {
73 3518
        $val = $value->getBinary();
74 3518
        for ($i = 0, $size = strlen($val); $i < $size; $i++) {
75 3237
            $chr = ord($val[$i]);
76 3237
            if ($chr !== 0) {
77 3236
                if (($i === ($size - 1)) && $chr === 0x80) {
78
                    return false;
79
                }
80 3236
                return true;
81
            }
82
        }
83 867
        return false;
84
    }
85
86
    /**
87
     * @param BufferInterface $signature
88
     * @return bool
89
     */
90
    public function isValidSignatureEncoding(BufferInterface $signature): bool
91
    {
92
        try {
93
            TransactionSignature::isDERSignature($signature);
94
            return true;
95
        } catch (SignatureNotCanonical $e) {
96
            /* In any case, we will return false outside this block */
97
        }
98
99
        return false;
100
    }
101
102
    /**
103
     * @param int $opCode
104
     * @param BufferInterface $pushData
105
     * @return bool
106
     * @throws \Exception
107
     */
108 461
    public function checkMinimalPush($opCode, BufferInterface $pushData): bool
109
    {
110 461
        $pushSize = $pushData->getSize();
111 461
        $binary = $pushData->getBinary();
112
113 461
        if ($pushSize === 0) {
114 217
            return $opCode === Opcodes::OP_0;
115 377
        } elseif ($pushSize === 1) {
116 89
            $first = ord($binary[0]);
117
118 89
            if ($first >= 1 && $first <= 16) {
119 65
                return $opCode === (Opcodes::OP_1 + ($first - 1));
120 25
            } elseif ($first === 0x81) {
121 25
                return $opCode === Opcodes::OP_1NEGATE;
122
            }
123 289
        } elseif ($pushSize <= 75) {
124 281
            return $opCode === $pushSize;
125 9
        } elseif ($pushSize <= 255) {
126 5
            return $opCode === Opcodes::OP_PUSHDATA1;
127 5
        } elseif ($pushSize <= 65535) {
128 5
            return $opCode === Opcodes::OP_PUSHDATA2;
129
        }
130
131 21
        return true;
132
    }
133
134
    /**
135
     * @param int $count
136
     * @return $this
137
     */
138 4409
    private function checkOpcodeCount(int $count)
139
    {
140 4409
        if ($count > 201) {
141 20
            throw new \RuntimeException('Error: Script op code count');
142
        }
143
144 4409
        return $this;
145
    }
146
147
    /**
148
     * @param WitnessProgram $witnessProgram
149
     * @param ScriptWitnessInterface $scriptWitness
150
     * @param int $flags
151
     * @param CheckerBase $checker
152
     * @return bool
153
     */
154 268
    private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitnessInterface $scriptWitness, int $flags, CheckerBase $checker): bool
155
    {
156 268
        $witnessCount = count($scriptWitness);
157
158 268
        if ($witnessProgram->getVersion() === 0) {
159 264
            $buffer = $witnessProgram->getProgram();
160 264
            if ($buffer->getSize() === 32) {
161
                // Version 0 segregated witness program: SHA256(Script) in program, Script + inputs in witness
162 232
                if ($witnessCount === 0) {
163
                    // Must contain script at least
164 4
                    return false;
165
                }
166
167 228
                $scriptPubKey = new Script($scriptWitness[$witnessCount - 1]);
168 228
                $stackValues = $scriptWitness->slice(0, -1);
169 228
                if (!$buffer->equals($scriptPubKey->getWitnessScriptHash())) {
170 228
                    return false;
171
                }
172 32
            } elseif ($buffer->getSize() === 20) {
173
                // Version 0 special case for pay-to-pubkeyhash
174 28
                if ($witnessCount !== 2) {
175
                    // 2 items in witness - <signature> <pubkey>
176 4
                    return false;
177
                }
178
179 24
                $scriptPubKey = ScriptFactory::scriptPubKey()->payToPubKeyHash($buffer);
180 24
                $stackValues = $scriptWitness;
181
            } else {
182 248
                return false;
183
            }
184 4
        } elseif ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
185 4
            return false;
186
        } else {
187
            // Unknown versions are always 'valid' to permit future soft forks
188
            return true;
189
        }
190
191 244
        $mainStack = new Stack();
192 244
        foreach ($stackValues as $value) {
193 208
            $mainStack->push($value);
194
        }
195
196 244
        if (!$this->evaluate($scriptPubKey, $mainStack, SigHash::V1, $flags, $checker)) {
197 80
            return false;
198
        }
199
200 164
        if ($mainStack->count() !== 1) {
201 56
            return false;
202
        }
203
204 108
        if (!$this->castToBool($mainStack->bottom())) {
205 36
            return false;
206
        }
207
208 72
        return true;
209
    }
210
211
    /**
212
     * @param ScriptInterface $scriptSig
213
     * @param ScriptInterface $scriptPubKey
214
     * @param int $flags
215
     * @param CheckerBase $checker
216
     * @param ScriptWitnessInterface|null $witness
217
     * @return bool
218
     */
219 4667
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, int $flags, CheckerBase $checker, ScriptWitnessInterface $witness = null): bool
220
    {
221 4667
        static $emptyWitness = null;
222 4667
        if ($emptyWitness === null) {
223 1
            $emptyWitness = new ScriptWitness();
224
        }
225
226 4667
        $witness = is_null($witness) ? $emptyWitness : $witness;
227
228 4667
        if (($flags & self::VERIFY_SIGPUSHONLY) !== 0 && !$scriptSig->isPushOnly()) {
229 8
            return false;
230
        }
231
232 4659
        $stack = new Stack();
233 4659
        if (!$this->evaluate($scriptSig, $stack, SigHash::V0, $flags, $checker)) {
234 221
            return false;
235
        }
236
237 4438
        $backup = [];
238 4438
        if ($flags & self::VERIFY_P2SH) {
239 3315
            foreach ($stack as $s) {
240 2646
                $backup[] = $s;
241
            }
242
        }
243
244 4438
        if (!$this->evaluate($scriptPubKey, $stack, SigHash::V0, $flags, $checker)) {
245 1342
            return false;
246
        }
247
248 3096
        if ($stack->isEmpty()) {
249 61
            return false;
250
        }
251
252 3035
        if (false === $this->castToBool($stack[-1])) {
253 185
            return false;
254
        }
255
256 2850
        $program = null;
257 2850
        if ($flags & self::VERIFY_WITNESS) {
258 348
            if ($scriptPubKey->isWitness($program)) {
259
                /** @var WitnessProgram $program */
260 152
                if ($scriptSig->getBuffer()->getSize() !== 0) {
261 4
                    return false;
262
                }
263
264 148
                if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
265 112
                    return false;
266
                }
267
268 36
                $stack->resize(1);
269
            }
270
        }
271
272 2734
        if ($flags & self::VERIFY_P2SH && (new OutputClassifier())->isPayToScriptHash($scriptPubKey)) {
273 246
            if (!$scriptSig->isPushOnly()) {
274 13
                return false;
275
            }
276
277 233
            $stack = new Stack();
278 233
            foreach ($backup as $i) {
279 233
                $stack->push($i);
280
            }
281
282
            // Restore mainStack to how it was after evaluating scriptSig
283 233
            if ($stack->isEmpty()) {
284
                return false;
285
            }
286
287
            // Load redeemscript as the scriptPubKey
288 233
            $scriptPubKey = new Script($stack->bottom());
289 233
            $stack->pop();
290
291 233
            if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
292 25
                return false;
293
            }
294
295 208
            if ($stack->isEmpty()) {
296 20
                return false;
297
            }
298
299 188
            if (!$this->castToBool($stack->bottom())) {
300 4
                return false;
301
            }
302
303 184
            if ($flags & self::VERIFY_WITNESS) {
304 144
                if ($scriptPubKey->isWitness($program)) {
305
                    /** @var WitnessProgram $program */
306 124
                    if (!$scriptSig->equals(ScriptFactory::sequence([$scriptPubKey->getBuffer()]))) {
307 4
                        return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
308
                    }
309
310 120
                    if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
311 84
                        return false;
312
                    }
313
314 36
                    $stack->resize(1);
315
                }
316
            }
317
        }
318
319 2584
        if ($flags & self::VERIFY_CLEAN_STACK) {
320 12
            if (!($flags & self::VERIFY_P2SH !== 0) && ($flags & self::VERIFY_WITNESS !== 0)) {
321
                return false; // implied flags required
322
            }
323
324 12
            if (count($stack) !== 1) {
325 8
                return false; // Cleanstack
326
            }
327
        }
328
329 2576
        if ($flags & self::VERIFY_WITNESS) {
330 116
            if (!$flags & self::VERIFY_P2SH) {
331
                return false; //
332
            }
333
334 116
            if ($program === null && !$witness->isNull()) {
335 4
                return false; // SCRIPT_ERR_WITNESS_UNEXPECTED
336
            }
337
        }
338
339 2572
        return true;
340
    }
341
342
    /**
343
     * @param Stack $vfStack
344
     * @param bool $value
345
     * @return bool
346
     */
347 4760
    public function checkExec(Stack $vfStack, bool $value): bool
348
    {
349 4760
        $ret = 0;
350 4760
        foreach ($vfStack as $item) {
351 1171
            if ($item === $value) {
352 738
                $ret++;
353
            }
354
        }
355
356 4760
        return (bool) $ret;
357
    }
358
359
    /**
360
     * @param ScriptInterface $script
361
     * @param Stack $mainStack
362
     * @param int $sigVersion
363
     * @param int $flags
364
     * @param CheckerBase $checker
365
     * @return bool
366
     */
367 4660
    public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVersion, int $flags, CheckerBase $checker): bool
368
    {
369 4660
        $hashStartPos = 0;
370 4660
        $opCount = 0;
371 4660
        $zero = gmp_init(0, 10);
372 4660
        $altStack = new Stack();
373 4660
        $vfStack = new Stack();
374 4660
        $minimal = ($flags & self::VERIFY_MINIMALDATA) !== 0;
375 4660
        $parser = $script->getScriptParser();
376
377 4660
        if ($script->getBuffer()->getSize() > 10000) {
378 4
            return false;
379
        }
380
381
        try {
382 4660
            foreach ($parser as $operation) {
383 4643
                $opCode = $operation->getOp();
384 4643
                $pushData = $operation->getData();
385 4643
                $fExec = !$this->checkExec($vfStack, false);
386
387
                // If pushdata was written to
388 4643
                if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
389 9
                    throw new \RuntimeException('Error - push size');
390
                }
391
392
                // OP_RESERVED should not count towards opCount
393 4642
                if ($opCode > Opcodes::OP_16 && ++$opCount) {
394 4409
                    $this->checkOpcodeCount($opCount);
395
                }
396
397 4642
                if (in_array($opCode, $this->disabledOps, true)) {
398 96
                    throw new \RuntimeException('Disabled Opcode');
399
                }
400
401 4642
                if ($fExec && $operation->isPush()) {
402
                    // In range of a pushdata opcode
403 3412
                    if ($minimal && !$this->checkMinimalPush($opCode, $pushData)) {
404 84
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
405
                    }
406
407 3328
                    $mainStack->push($pushData);
408
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
409 4501
                } elseif ($fExec || (Opcodes::OP_IF <= $opCode && $opCode <= Opcodes::OP_ENDIF)) {
410
                    // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n";
411 4501
                    switch ($opCode) {
412
                        case Opcodes::OP_1NEGATE:
413
                        case Opcodes::OP_1:
414
                        case Opcodes::OP_2:
415
                        case Opcodes::OP_3:
416
                        case Opcodes::OP_4:
417
                        case Opcodes::OP_5:
418
                        case Opcodes::OP_6:
419
                        case Opcodes::OP_7:
420
                        case Opcodes::OP_8:
421
                        case Opcodes::OP_9:
422
                        case Opcodes::OP_10:
423
                        case Opcodes::OP_11:
424
                        case Opcodes::OP_12:
425
                        case Opcodes::OP_13:
426
                        case Opcodes::OP_14:
427
                        case Opcodes::OP_15:
428
                        case Opcodes::OP_16:
429 2832
                            $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode);
430 2832
                            $mainStack->push(Number::int($num)->getBuffer());
431 2832
                            break;
432
433
                        case Opcodes::OP_CHECKLOCKTIMEVERIFY:
434 24
                            if (!($flags & self::VERIFY_CHECKLOCKTIMEVERIFY)) {
435 24
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
436 4
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
437
                                }
438 20
                                break;
439
                            }
440
441
                            if ($mainStack->isEmpty()) {
442
                                throw new \RuntimeException('Invalid stack operation - CLTV');
443
                            }
444
445
                            $lockTime = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
446
                            if (!$checker->checkLockTime($lockTime)) {
447
                                throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
448
                            }
449
450
                            break;
451
452
                        case Opcodes::OP_CHECKSEQUENCEVERIFY:
453 48
                            if (!($flags & self::VERIFY_CHECKSEQUENCEVERIFY)) {
454 24
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
455 4
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
456
                                }
457 20
                                break;
458
                            }
459
460 24
                            if ($mainStack->isEmpty()) {
461 4
                                throw new \RuntimeException('Invalid stack operation - CSV');
462
                            }
463
464 20
                            $sequence = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
465 16
                            $nSequence = $sequence->getGmp();
466 16
                            if ($this->math->cmp($nSequence, $zero) < 0) {
0 ignored issues
show
Bug introduced by
It seems like $zero can also be of type resource; however, parameter $other of Mdanter\Ecc\Math\GmpMath::cmp() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

466
                            if ($this->math->cmp($nSequence, /** @scrutinizer ignore-type */ $zero) < 0) {
Loading history...
467 4
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
468
                            }
469
470 12
                            if ($this->math->cmp($this->math->bitwiseAnd($nSequence, gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) {
0 ignored issues
show
Bug introduced by
It seems like gmp_init(BitWasp\Bitcoin...KTIME_DISABLE_FLAG, 10) can also be of type resource; however, parameter $other of Mdanter\Ecc\Math\GmpMath::bitwiseAnd() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

470
                            if ($this->math->cmp($this->math->bitwiseAnd($nSequence, /** @scrutinizer ignore-type */ gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) {
Loading history...
471 4
                                break;
472
                            }
473
474 8
                            if (!$checker->checkSequence($sequence)) {
475 8
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied sequence');
476
                            }
477
                            break;
478
479
                        case Opcodes::OP_NOP1:
480
                        case Opcodes::OP_NOP4:
481
                        case Opcodes::OP_NOP5:
482
                        case Opcodes::OP_NOP6:
483
                        case Opcodes::OP_NOP7:
484
                        case Opcodes::OP_NOP8:
485
                        case Opcodes::OP_NOP9:
486
                        case Opcodes::OP_NOP10:
487 108
                            if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
488 40
                                throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
489
                            }
490 68
                            break;
491
492
                        case Opcodes::OP_NOP:
493 336
                            break;
494
495
                        case Opcodes::OP_IF:
496
                        case Opcodes::OP_NOTIF:
497
                            // <expression> if [statements] [else [statements]] endif
498 1268
                            $value = false;
499 1268
                            if ($fExec) {
500 1268
                                if ($mainStack->isEmpty()) {
501 48
                                    throw new \RuntimeException('Unbalanced conditional');
502
                                }
503 1220
                                $vch = $mainStack[-1];
504
505 1220
                                if ($sigVersion === SigHash::V1 && ($flags & self::VERIFY_MINIMALIF)) {
506 80
                                    if ($vch->getSize() > 1) {
507 16
                                        throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded');
508
                                    }
509
510 64
                                    if ($vch->getSize() === 1 && $vch->getBinary() !== "\x01") {
511 32
                                        throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded');
512
                                    }
513
                                }
514
515 1172
                                $buffer = Number::buffer($mainStack->pop(), $minimal)->getBuffer();
516 1172
                                $value = $this->castToBool($buffer);
517 1172
                                if ($opCode === Opcodes::OP_NOTIF) {
518 164
                                    $value = !$value;
519
                                }
520
                            }
521 1172
                            $vfStack->push($value);
522 1172
                            break;
523
524
                        case Opcodes::OP_ELSE:
525 460
                            if ($vfStack->isEmpty()) {
526 16
                                throw new \RuntimeException('Unbalanced conditional');
527
                            }
528 452
                            $vfStack->push(!$vfStack->pop());
529 452
                            break;
530
531
                        case Opcodes::OP_ENDIF:
532 780
                            if ($vfStack->isEmpty()) {
533 28
                                throw new \RuntimeException('Unbalanced conditional');
534
                            }
535 764
                            $vfStack->pop();
536 764
                            break;
537
538
                        case Opcodes::OP_VERIFY:
539 120
                            if ($mainStack->isEmpty()) {
540 4
                                throw new \RuntimeException('Invalid stack operation');
541
                            }
542 116
                            $value = $this->castToBool($mainStack[-1]);
543 116
                            if (!$value) {
544 4
                                throw new \RuntimeException('Error: verify');
545
                            }
546 112
                            $mainStack->pop();
547 112
                            break;
548
549
                        case Opcodes::OP_TOALTSTACK:
550 32
                            if ($mainStack->isEmpty()) {
551 4
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
552
                            }
553 28
                            $altStack->push($mainStack->pop());
554 28
                            break;
555
556
                        case Opcodes::OP_FROMALTSTACK:
557 20
                            if ($altStack->isEmpty()) {
558 8
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
559
                            }
560 12
                            $mainStack->push($altStack->pop());
561 12
                            break;
562
563
                        case Opcodes::OP_IFDUP:
564
                            // If top value not zero, duplicate it.
565 24
                            if ($mainStack->isEmpty()) {
566 8
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
567
                            }
568 16
                            $vch = $mainStack[-1];
569 16
                            if ($this->castToBool($vch)) {
570 12
                                $mainStack->push($vch);
571
                            }
572 16
                            break;
573
574
                        case Opcodes::OP_DEPTH:
575 301
                            $num = count($mainStack);
576 301
                            $depth = Number::int($num)->getBuffer();
577 301
                            $mainStack->push($depth);
578 301
                            break;
579
580
                        case Opcodes::OP_DROP:
581 192
                            if ($mainStack->isEmpty()) {
582 8
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
583
                            }
584 184
                            $mainStack->pop();
585 184
                            break;
586
587
                        case Opcodes::OP_DUP:
588 112
                            if ($mainStack->isEmpty()) {
589 8
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
590
                            }
591 104
                            $vch = $mainStack[-1];
592 104
                            $mainStack->push($vch);
593 104
                            break;
594
595
                        case Opcodes::OP_NIP:
596 24
                            if (count($mainStack) < 2) {
597 12
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
598
                            }
599 12
                            unset($mainStack[-2]);
600 12
                            break;
601
602
                        case Opcodes::OP_OVER:
603 24
                            if (count($mainStack) < 2) {
604 12
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
605
                            }
606 12
                            $vch = $mainStack[-2];
607 12
                            $mainStack->push($vch);
608 12
                            break;
609
610
                        case Opcodes::OP_ROT:
611 48
                            if (count($mainStack) < 3) {
612 16
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
613
                            }
614 32
                            $mainStack->swap(-3, -2);
615 32
                            $mainStack->swap(-2, -1);
616 32
                            break;
617
618
                        case Opcodes::OP_SWAP:
619 40
                            if (count($mainStack) < 2) {
620 12
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
621
                            }
622 28
                            $mainStack->swap(-2, -1);
623 28
                            break;
624
625
                        case Opcodes::OP_TUCK:
626 24
                            if (count($mainStack) < 2) {
627 12
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
628
                            }
629 12
                            $vch = $mainStack[-1];
630 12
                            $mainStack->add(- 2, $vch);
631 12
                            break;
632
633
                        case Opcodes::OP_PICK:
634
                        case Opcodes::OP_ROLL:
635 116
                            if (count($mainStack) < 2) {
636 16
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
637
                            }
638
639 100
                            $n = Number::buffer($mainStack[-1], $minimal, 4)->getGmp();
640 92
                            $mainStack->pop();
641 92
                            if ($this->math->cmp($n, $zero) < 0 || $this->math->cmp($n, gmp_init(count($mainStack))) >= 0) {
642 20
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
643
                            }
644
645 72
                            $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), gmp_init(1)), 10);
0 ignored issues
show
Bug introduced by
It seems like gmp_init(1) can also be of type resource; however, parameter $subtrahend of Mdanter\Ecc\Math\GmpMath::sub() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

645
                            $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), /** @scrutinizer ignore-type */ gmp_init(1)), 10);
Loading history...
Bug introduced by
It seems like $zero can also be of type resource; however, parameter $minuend of Mdanter\Ecc\Math\GmpMath::sub() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

645
                            $pos = (int) gmp_strval($this->math->sub($this->math->sub(/** @scrutinizer ignore-type */ $zero, $n), gmp_init(1)), 10);
Loading history...
646 72
                            $vch = $mainStack[$pos];
647 72
                            if ($opCode === Opcodes::OP_ROLL) {
648 36
                                unset($mainStack[$pos]);
649
                            }
650 72
                            $mainStack->push($vch);
651 72
                            break;
652
653
                        case Opcodes::OP_2DROP:
654 36
                            if (count($mainStack) < 2) {
655 4
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
656
                            }
657 32
                            $mainStack->pop();
658 32
                            $mainStack->pop();
659 32
                            break;
660
661
                        case Opcodes::OP_2DUP:
662 24
                            if (count($mainStack) < 2) {
663 12
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
664
                            }
665 12
                            $string1 = $mainStack[-2];
666 12
                            $string2 = $mainStack[-1];
667 12
                            $mainStack->push($string1);
668 12
                            $mainStack->push($string2);
669 12
                            break;
670
671
                        case Opcodes::OP_3DUP:
672 44
                            if (count($mainStack) < 3) {
673 16
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
674
                            }
675 28
                            $string1 = $mainStack[-3];
676 28
                            $string2 = $mainStack[-2];
677 28
                            $string3 = $mainStack[-1];
678 28
                            $mainStack->push($string1);
679 28
                            $mainStack->push($string2);
680 28
                            $mainStack->push($string3);
681 28
                            break;
682
683
                        case Opcodes::OP_2OVER:
684 20
                            if (count($mainStack) < 4) {
685 12
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
686
                            }
687 8
                            $string1 = $mainStack[-4];
688 8
                            $string2 = $mainStack[-3];
689 8
                            $mainStack->push($string1);
690 8
                            $mainStack->push($string2);
691 8
                            break;
692
693
                        case Opcodes::OP_2ROT:
694 40
                            if (count($mainStack) < 6) {
695 4
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
696
                            }
697 36
                            $string1 = $mainStack[-6];
698 36
                            $string2 = $mainStack[-5];
699 36
                            unset($mainStack[-6], $mainStack[-5]);
700 36
                            $mainStack->push($string1);
701 36
                            $mainStack->push($string2);
702 36
                            break;
703
704
                        case Opcodes::OP_2SWAP:
705 20
                            if (count($mainStack) < 4) {
706 12
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
707
                            }
708 8
                            $mainStack->swap(-3, -1);
709 8
                            $mainStack->swap(-4, -2);
710 8
                            break;
711
712
                        case Opcodes::OP_SIZE:
713 124
                            if ($mainStack->isEmpty()) {
714 8
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
715
                            }
716 116
                            $size = Number::int($mainStack[-1]->getSize());
717 116
                            $mainStack->push($size->getBuffer());
718 116
                            break;
719
720
                        case Opcodes::OP_EQUAL:
721
                        case Opcodes::OP_EQUALVERIFY:
722 1258
                            if (count($mainStack) < 2) {
723 16
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
724
                            }
725
726 1242
                            $equal = $mainStack[-2]->equals($mainStack[-1]);
727 1242
                            $mainStack->pop();
728 1242
                            $mainStack->pop();
729 1242
                            $mainStack->push($equal ? $this->vchTrue : $this->vchFalse);
730 1242
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
731 180
                                if ($equal) {
732 144
                                    $mainStack->pop();
733
                                } else {
734 36
                                    throw new \RuntimeException('Error EQUALVERIFY');
735
                                }
736
                            }
737
738 1210
                            break;
739
740
                        // Arithmetic operations
741
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
742 444
                            if ($mainStack->isEmpty()) {
743 24
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
744
                            }
745
746 420
                            $num = Number::buffer($mainStack[-1], $minimal)->getGmp();
747
748 328
                            if ($opCode === Opcodes::OP_1ADD) {
749 28
                                $num = $this->math->add($num, gmp_init(1));
0 ignored issues
show
Bug introduced by
It seems like gmp_init(1) can also be of type resource; however, parameter $addend of Mdanter\Ecc\Math\GmpMath::add() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

749
                                $num = $this->math->add($num, /** @scrutinizer ignore-type */ gmp_init(1));
Loading history...
750 300
                            } elseif ($opCode === Opcodes::OP_1SUB) {
751 12
                                $num = $this->math->sub($num, gmp_init(1));
752 288
                            } elseif ($opCode === Opcodes::OP_2MUL) {
753
                                $num = $this->math->mul(gmp_init(2), $num);
0 ignored issues
show
Bug introduced by
It seems like gmp_init(2) can also be of type resource; however, parameter $multiplier of Mdanter\Ecc\Math\GmpMath::mul() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

753
                                $num = $this->math->mul(/** @scrutinizer ignore-type */ gmp_init(2), $num);
Loading history...
754 288
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
755 16
                                $num = $this->math->sub($zero, $num);
756 276
                            } elseif ($opCode === Opcodes::OP_ABS) {
757 20
                                if ($this->math->cmp($num, $zero) < 0) {
758 20
                                    $num = $this->math->sub($zero, $num);
759
                                }
760 256
                            } elseif ($opCode === Opcodes::OP_NOT) {
761 232
                                $num = gmp_init($this->math->cmp($num, $zero) === 0 ? 1 : 0);
762
                            } else {
763
                                // is OP_0NOTEQUAL
764 24
                                $num = gmp_init($this->math->cmp($num, $zero) !== 0 ? 1 : 0);
765
                            }
766
767 328
                            $mainStack->pop();
768
769 328
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
770
771 328
                            $mainStack->push($buffer);
772 328
                            break;
773
774 2209
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
775 656
                            if (count($mainStack) < 2) {
776 52
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
777
                            }
778
779 604
                            $num1 = Number::buffer($mainStack[-2], $minimal)->getGmp();
780 536
                            $num2 = Number::buffer($mainStack[-1], $minimal)->getGmp();
781
782 484
                            if ($opCode === Opcodes::OP_ADD) {
783 120
                                $num = $this->math->add($num1, $num2);
784 388
                            } else if ($opCode === Opcodes::OP_SUB) {
785 24
                                $num = $this->math->sub($num1, $num2);
786 364
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
787 32
                                $num = (int) ($this->math->cmp($num1, $zero) !== 0 && $this->math->cmp($num2, $zero) !== 0);
788 332
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
789 32
                                $num = (int) ($this->math->cmp($num1, $zero) !== 0 || $this->math->cmp($num2, $zero) !== 0);
790 300
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
791 112
                                $num = (int) ($this->math->cmp($num1, $num2) === 0);
792 220
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
793 16
                                $num = (int) ($this->math->cmp($num1, $num2) === 0);
794 204
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
795 20
                                $num = (int) ($this->math->cmp($num1, $num2) !== 0);
796 184
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) {
797 32
                                $num = (int) ($this->math->cmp($num1, $num2) < 0);
798 152
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
799 32
                                $num = (int) ($this->math->cmp($num1, $num2) > 0);
800 120
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
801 32
                                $num = (int) ($this->math->cmp($num1, $num2) <= 0);
802 88
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
803 32
                                $num = (int) ($this->math->cmp($num1, $num2) >= 0);
804 56
                            } elseif ($opCode === Opcodes::OP_MIN) {
805 28
                                $num = ($this->math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
806
                            } else {
807 28
                                $num = ($this->math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
808
                            }
809
810 484
                            $mainStack->pop();
811 484
                            $mainStack->pop();
812 484
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
813 484
                            $mainStack->push($buffer);
814
815 484
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
816 16
                                if ($this->castToBool($mainStack[-1])) {
817 16
                                    $mainStack->pop();
818
                                } else {
819
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
820
                                }
821
                            }
822 484
                            break;
823
824 1553
                        case Opcodes::OP_WITHIN:
825 60
                            if (count($mainStack) < 3) {
826 4
                                throw new \RuntimeException('Invalid stack operation');
827
                            }
828
829 56
                            $num1 = Number::buffer($mainStack[-3], $minimal)->getGmp();
830 52
                            $num2 = Number::buffer($mainStack[-2], $minimal)->getGmp();
831 48
                            $num3 = Number::buffer($mainStack[-1], $minimal)->getGmp();
832
833 44
                            $value = $this->math->cmp($num2, $num1) <= 0 && $this->math->cmp($num1, $num3) < 0;
834 44
                            $mainStack->pop();
835 44
                            $mainStack->pop();
836 44
                            $mainStack->pop();
837 44
                            $mainStack->push($value ? $this->vchTrue : $this->vchFalse);
838 44
                            break;
839
840
                        // Hash operation
841 1493
                        case Opcodes::OP_RIPEMD160:
842 1469
                        case Opcodes::OP_SHA1:
843 1437
                        case Opcodes::OP_SHA256:
844 1413
                        case Opcodes::OP_HASH160:
845 1183
                        case Opcodes::OP_HASH256:
846 415
                            if ($mainStack->isEmpty()) {
847 41
                                throw new \RuntimeException('Invalid stack operation');
848
                            }
849
850 374
                            $buffer = $mainStack[-1];
851 374
                            if ($opCode === Opcodes::OP_RIPEMD160) {
852 20
                                $hash = Hash::ripemd160($buffer);
853 358
                            } elseif ($opCode === Opcodes::OP_SHA1) {
854 24
                                $hash = Hash::sha1($buffer);
855 334
                            } elseif ($opCode === Opcodes::OP_SHA256) {
856 24
                                $hash = Hash::sha256($buffer);
857 318
                            } elseif ($opCode === Opcodes::OP_HASH160) {
858 298
                                $hash = Hash::sha256ripe160($buffer);
859
                            } else {
860 20
                                $hash = Hash::sha256d($buffer);
861
                            }
862
863 374
                            $mainStack->pop();
864 374
                            $mainStack->push($hash);
865 374
                            break;
866
867 1155
                        case Opcodes::OP_CODESEPARATOR:
868 4
                            $hashStartPos = $parser->getPosition();
869 4
                            break;
870
871 1151
                        case Opcodes::OP_CHECKSIG:
872 815
                        case Opcodes::OP_CHECKSIGVERIFY:
873 336
                            if (count($mainStack) < 2) {
874 8
                                throw new \RuntimeException('Invalid stack operation');
875
                            }
876
877 328
                            $vchPubKey = $mainStack[-1];
878 328
                            $vchSig = $mainStack[-2];
879
880 328
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
881
882 328
                            $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags);
883
884 232
                            $mainStack->pop();
885 232
                            $mainStack->pop();
886 232
                            $mainStack->push($success ? $this->vchTrue : $this->vchFalse);
887
888 232
                            if (!$success && ($flags & self::VERIFY_NULLFAIL) && $vchSig->getSize() > 0) {
889 4
                                throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Signature must be zero for failed OP_CHECK(MULTIS)SIG operation');
890
                            }
891
892 228
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
893
                                if ($success) {
894
                                    $mainStack->pop();
895
                                } else {
896
                                    throw new \RuntimeException('Checksig verify');
897
                                }
898
                            }
899 228
                            break;
900
901 815
                        case Opcodes::OP_CHECKMULTISIG:
902 495
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
903 448
                            $i = 1;
904 448
                            if (count($mainStack) < $i) {
905 4
                                throw new \RuntimeException('Invalid stack operation');
906
                            }
907
908 444
                            $keyCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
909 436
                            if ($keyCount < 0 || $keyCount > 20) {
910 8
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
911
                            }
912
913 428
                            $opCount += $keyCount;
914 428
                            $this->checkOpcodeCount($opCount);
915
916
                            // Extract positions of the keys, and signatures, from the stack.
917 428
                            $ikey = ++$i;
918 428
                            $ikey2 = $keyCount + 2;
919 428
                            $i += $keyCount;
920 428
                            if (count($mainStack) < $i) {
921 4
                                throw new \RuntimeException('Invalid stack operation');
922
                            }
923
924 424
                            $sigCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
925 412
                            if ($sigCount < 0 || $sigCount > $keyCount) {
926 8
                                throw new \RuntimeException('Invalid Signature count');
927
                            }
928
929 404
                            $isig = ++$i;
930 404
                            $i += $sigCount;
931
932
                            // Extract the script since the last OP_CODESEPARATOR
933 404
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
934
935 404
                            $fSuccess = true;
936 404
                            while ($fSuccess && $sigCount > 0) {
937
                                // Fetch the signature and public key
938 164
                                $sig = $mainStack[-$isig];
939 160
                                $pubkey = $mainStack[-$ikey];
940
941 160
                                if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) {
942 56
                                    $isig++;
943 56
                                    $sigCount--;
944
                                }
945
946 136
                                $ikey++;
947 136
                                $keyCount--;
948
949
                                // If there are more signatures left than keys left,
950
                                // then too many signatures have failed. Exit early,
951
                                // without checking any further signatures.
952 136
                                if ($sigCount > $keyCount) {
953 88
                                    $fSuccess = false;
954
                                }
955
                            }
956
957 368
                            while ($i-- > 1) {
958
                                // If the operation failed, we require that all signatures must be empty vector
959 368
                                if (!$fSuccess && ($flags & self::VERIFY_NULLFAIL) && !$ikey2 && $mainStack[-1]->getSize() > 0) {
960 8
                                    throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Bad signature must be empty vector');
961
                                }
962
963 368
                                if ($ikey2 > 0) {
964 368
                                    $ikey2--;
965
                                }
966
967 368
                                $mainStack->pop();
968
                            }
969
970
                            // A bug causes CHECKMULTISIG to consume one extra argument
971
                            // whose contents were not checked in any way.
972
                            //
973
                            // Unfortunately this is a potential source of mutability,
974
                            // so optionally verify it is exactly equal to zero prior
975
                            // to removing it from the stack.
976 360
                            if ($mainStack->isEmpty()) {
977 4
                                throw new \RuntimeException('Invalid stack operation');
978
                            }
979
980 360
                            if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) {
981 12
                                throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
982
                            }
983
984 348
                            $mainStack->pop();
985 348
                            $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
986
987 348
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
988 120
                                if ($fSuccess) {
989 120
                                    $mainStack->pop();
990
                                } else {
991
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
992
                                }
993
                            }
994 348
                            break;
995
996
                        default:
997 367
                            throw new \RuntimeException('Opcode not found');
998
                    }
999
1000 4066
                    if (count($mainStack) + count($altStack) > 1000) {
1001 8
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
1002
                    }
1003
                }
1004
            }
1005
1006 4459
            if (count($vfStack) !== 0) {
1007 24
                throw new \RuntimeException('Unbalanced conditional at script end');
1008
            }
1009
1010 4439
            return true;
1011 1665
        } catch (ScriptRuntimeException $e) {
1012
            // echo "\n Runtime: " . $e->getMessage() . "\n" . $e->getTraceAsString() . PHP_EOL;
1013
            // Failure due to script tags, can access flag: $e->getFailureFlag()
1014 344
            return false;
1015 1321
        } catch (\Exception $e) {
1016
            // echo "\n General: " . $e->getMessage()  . PHP_EOL . $e->getTraceAsString() . PHP_EOL;
1017 1321
            return false;
1018
        }
1019
    }
1020
}
1021