Interpreter::verifyWitnessProgram()   C
last analyzed

Complexity

Conditions 12
Paths 22

Size

Total Lines 55
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 12.0053

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 31
c 2
b 0
f 0
nc 22
nop 4
dl 0
loc 55
ccs 29
cts 30
cp 0.9667
crap 12.0053
rs 6.9666

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