Completed
Push — master ( c780c0...f2b04e )
by thomas
60:38 queued 58:10
created

Interpreter   F

Complexity

Total Complexity 274

Size/Duplication

Total Lines 994
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 21

Test Coverage

Coverage 95.58%

Importance

Changes 0
Metric Value
dl 0
loc 994
ccs 497
cts 520
cp 0.9558
rs 1.6059
c 0
b 0
f 0
wmc 274
lcom 1
cbo 21

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A castToBool() 0 14 5
A isValidSignatureEncoding() 0 11 2
B checkMinimalPush() 0 25 9
A checkOpcodeCount() 0 8 2
C verifyWitnessProgram() 0 55 12
F verify() 0 122 35
A checkExec() 0 11 3
F evaluate() 0 653 204

How to fix   Complexity   

Complex Class

Complex classes like Interpreter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Interpreter, and based on these observations, apply Extract Interface, too.

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 2469
    public function __construct(EcAdapterInterface $ecAdapter = null)
58
    {
59 2469
        $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter();
60 2469
        $this->math = $ecAdapter->getMath();
61 2469
        $this->vchFalse = new Buffer("", 0);
62 2469
        $this->vchTrue = new Buffer("\x01", 1);
63 2469
    }
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 Checker $checker
152
     * @return bool
153
     */
154 268
    private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitnessInterface $scriptWitness, int $flags, Checker $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
            return false;
188
        }
189
190 244
        $mainStack = new Stack();
191 244
        foreach ($stackValues as $value) {
192 208
            $mainStack->push($value);
193
        }
194
195 244
        if (!$this->evaluate($scriptPubKey, $mainStack, SigHash::V1, $flags, $checker)) {
196 80
            return false;
197
        }
198
199 164
        if ($mainStack->count() !== 1) {
200 56
            return false;
201
        }
202
203 108
        if (!$this->castToBool($mainStack->bottom())) {
204 36
            return false;
205
        }
206
207 72
        return true;
208
    }
209
210
    /**
211
     * @param ScriptInterface $scriptSig
212
     * @param ScriptInterface $scriptPubKey
213
     * @param int $flags
214
     * @param Checker $checker
215
     * @param ScriptWitnessInterface|null $witness
216
     * @return bool
217
     */
218 4667
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, int $flags, Checker $checker, ScriptWitnessInterface $witness = null): bool
219
    {
220 4667
        static $emptyWitness = null;
221 4667
        if ($emptyWitness === null) {
222 1
            $emptyWitness = new ScriptWitness();
223
        }
224
225 4667
        $witness = is_null($witness) ? $emptyWitness : $witness;
226
227 4667
        if (($flags & self::VERIFY_SIGPUSHONLY) !== 0 && !$scriptSig->isPushOnly()) {
228 8
            return false;
229
        }
230
231 4659
        $stack = new Stack();
232 4659
        if (!$this->evaluate($scriptSig, $stack, SigHash::V0, $flags, $checker)) {
233 221
            return false;
234
        }
235
236 4438
        $backup = [];
237 4438
        if ($flags & self::VERIFY_P2SH) {
238 3315
            foreach ($stack as $s) {
239 2646
                $backup[] = $s;
240
            }
241
        }
242
243 4438
        if (!$this->evaluate($scriptPubKey, $stack, SigHash::V0, $flags, $checker)) {
244 1342
            return false;
245
        }
246
247 3096
        if ($stack->isEmpty()) {
248 61
            return false;
249
        }
250
251 3035
        if (false === $this->castToBool($stack[-1])) {
252 185
            return false;
253
        }
254
255 2850
        $program = null;
256 2850
        if ($flags & self::VERIFY_WITNESS) {
257 348
            if ($scriptPubKey->isWitness($program)) {
258
                /** @var WitnessProgram $program */
259 152
                if ($scriptSig->getBuffer()->getSize() !== 0) {
260 4
                    return false;
261
                }
262
263 148
                if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
264 112
                    return false;
265
                }
266
267 36
                $stack->resize(1);
268
            }
269
        }
270
271 2734
        if ($flags & self::VERIFY_P2SH && (new OutputClassifier())->isPayToScriptHash($scriptPubKey)) {
272 246
            if (!$scriptSig->isPushOnly()) {
273 13
                return false;
274
            }
275
276 233
            $stack = new Stack();
277 233
            foreach ($backup as $i) {
278 233
                $stack->push($i);
279
            }
280
281
            // Restore mainStack to how it was after evaluating scriptSig
282 233
            if ($stack->isEmpty()) {
283
                return false;
284
            }
285
286
            // Load redeemscript as the scriptPubKey
287 233
            $scriptPubKey = new Script($stack->bottom());
288 233
            $stack->pop();
289
290 233
            if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
291 25
                return false;
292
            }
293
294 208
            if ($stack->isEmpty()) {
295 20
                return false;
296
            }
297
298 188
            if (!$this->castToBool($stack->bottom())) {
299 4
                return false;
300
            }
301
302 184
            if ($flags & self::VERIFY_WITNESS) {
303 144
                if ($scriptPubKey->isWitness($program)) {
304
                    /** @var WitnessProgram $program */
305 124
                    if (!$scriptSig->equals(ScriptFactory::sequence([$scriptPubKey->getBuffer()]))) {
306 4
                        return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
307
                    }
308
309 120
                    if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
310 84
                        return false;
311
                    }
312
313 36
                    $stack->resize(1);
314
                }
315
            }
316
        }
317
318 2584
        if ($flags & self::VERIFY_CLEAN_STACK) {
319 12
            if (!($flags & self::VERIFY_P2SH !== 0) && ($flags & self::VERIFY_WITNESS !== 0)) {
320
                return false; // implied flags required
321
            }
322
323 12
            if (count($stack) !== 1) {
324 8
                return false; // Cleanstack
325
            }
326
        }
327
328 2576
        if ($flags & self::VERIFY_WITNESS) {
329 116
            if (!$flags & self::VERIFY_P2SH) {
330
                return false; //
331
            }
332
333 116
            if ($program === null && !$witness->isNull()) {
334 4
                return false; // SCRIPT_ERR_WITNESS_UNEXPECTED
335
            }
336
        }
337
338 2572
        return true;
339
    }
340
341
    /**
342
     * @param Stack $vfStack
343
     * @param bool $value
344
     * @return bool
345
     */
346 4761
    public function checkExec(Stack $vfStack, $value): bool
347
    {
348 4761
        $ret = 0;
349 4761
        foreach ($vfStack as $item) {
350 1171
            if ($item === $value) {
351 1171
                $ret++;
352
            }
353
        }
354
355 4761
        return (bool) $ret;
356
    }
357
358
    /**
359
     * @param ScriptInterface $script
360
     * @param Stack $mainStack
361
     * @param int $sigVersion
362
     * @param int $flags
363
     * @param Checker $checker
364
     * @return bool
365
     */
366 4660
    public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVersion, int $flags, Checker $checker): bool
367
    {
368 4660
        $hashStartPos = 0;
369 4660
        $opCount = 0;
370 4660
        $zero = gmp_init(0, 10);
371 4660
        $altStack = new Stack();
372 4660
        $vfStack = new Stack();
373 4660
        $minimal = ($flags & self::VERIFY_MINIMALDATA) !== 0;
374 4660
        $parser = $script->getScriptParser();
375
376 4660
        if ($script->getBuffer()->getSize() > 10000) {
377 4
            return false;
378
        }
379
380
        try {
381 4660
            foreach ($parser as $operation) {
382 4643
                $opCode = $operation->getOp();
383 4643
                $pushData = $operation->getData();
384 4643
                $fExec = !$this->checkExec($vfStack, false);
385
386
                // If pushdata was written to
387 4643
                if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
388 9
                    throw new \RuntimeException('Error - push size');
389
                }
390
391
                // OP_RESERVED should not count towards opCount
392 4642
                if ($opCode > Opcodes::OP_16 && ++$opCount) {
393 4409
                    $this->checkOpcodeCount($opCount);
394
                }
395
396 4642
                if (in_array($opCode, $this->disabledOps, true)) {
397 96
                    throw new \RuntimeException('Disabled Opcode');
398
                }
399
400 4642
                if ($fExec && $operation->isPush()) {
401
                    // In range of a pushdata opcode
402 3412
                    if ($minimal && !$this->checkMinimalPush($opCode, $pushData)) {
403 84
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
404
                    }
405
406 3328
                    $mainStack->push($pushData);
407
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
408 4501
                } elseif ($fExec || (Opcodes::OP_IF <= $opCode && $opCode <= Opcodes::OP_ENDIF)) {
409
                    // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n";
410 4501
                    switch ($opCode) {
411
                        case Opcodes::OP_1NEGATE:
412
                        case Opcodes::OP_1:
413
                        case Opcodes::OP_2:
414
                        case Opcodes::OP_3:
415
                        case Opcodes::OP_4:
416
                        case Opcodes::OP_5:
417
                        case Opcodes::OP_6:
418
                        case Opcodes::OP_7:
419
                        case Opcodes::OP_8:
420
                        case Opcodes::OP_9:
421
                        case Opcodes::OP_10:
422
                        case Opcodes::OP_11:
423
                        case Opcodes::OP_12:
424
                        case Opcodes::OP_13:
425
                        case Opcodes::OP_14:
426
                        case Opcodes::OP_15:
427
                        case Opcodes::OP_16:
428 2832
                            $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode);
429 2832
                            $mainStack->push(Number::int($num)->getBuffer());
430 2832
                            break;
431
432
                        case Opcodes::OP_CHECKLOCKTIMEVERIFY:
433 24
                            if (!($flags & self::VERIFY_CHECKLOCKTIMEVERIFY)) {
434 24
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
435 4
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
436
                                }
437 20
                                break;
438
                            }
439
440
                            if ($mainStack->isEmpty()) {
441
                                throw new \RuntimeException('Invalid stack operation - CLTV');
442
                            }
443
444
                            $lockTime = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
445
                            if (!$checker->checkLockTime($lockTime)) {
446
                                throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
447
                            }
448
449
                            break;
450
451
                        case Opcodes::OP_CHECKSEQUENCEVERIFY:
452 48
                            if (!($flags & self::VERIFY_CHECKSEQUENCEVERIFY)) {
453 24
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
454 4
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
455
                                }
456 20
                                break;
457
                            }
458
459 24
                            if ($mainStack->isEmpty()) {
460 4
                                throw new \RuntimeException('Invalid stack operation - CSV');
461
                            }
462
463 20
                            $sequence = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
464 16
                            $nSequence = $sequence->getGmp();
465 16
                            if ($this->math->cmp($nSequence, $zero) < 0) {
466 4
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
467
                            }
468
469 12
                            if ($this->math->cmp($this->math->bitwiseAnd($nSequence, gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) {
470 4
                                break;
471
                            }
472
473 8
                            if (!$checker->checkSequence($sequence)) {
474 8
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied sequence');
475
                            }
476
                            break;
477
478
                        case Opcodes::OP_NOP1:
479
                        case Opcodes::OP_NOP4:
480
                        case Opcodes::OP_NOP5:
481
                        case Opcodes::OP_NOP6:
482
                        case Opcodes::OP_NOP7:
483
                        case Opcodes::OP_NOP8:
484
                        case Opcodes::OP_NOP9:
485
                        case Opcodes::OP_NOP10:
486 108
                            if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
487 40
                                throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
488
                            }
489 68
                            break;
490
491
                        case Opcodes::OP_NOP:
492 336
                            break;
493
494
                        case Opcodes::OP_IF:
495
                        case Opcodes::OP_NOTIF:
496
                            // <expression> if [statements] [else [statements]] endif
497 1268
                            $value = false;
498 1268
                            if ($fExec) {
499 1268
                                if ($mainStack->isEmpty()) {
500 48
                                    throw new \RuntimeException('Unbalanced conditional');
501
                                }
502 1220
                                $vch = $mainStack[-1];
503
504 1220
                                if ($sigVersion === SigHash::V1 && ($flags & self::VERIFY_MINIMALIF)) {
505 80
                                    if ($vch->getSize() > 1) {
506 16
                                        throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded');
507
                                    }
508
509 64
                                    if ($vch->getSize() === 1 && $vch->getBinary() !== "\x01") {
510 32
                                        throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded');
511
                                    }
512
                                }
513
514 1172
                                $buffer = Number::buffer($mainStack->pop(), $minimal)->getBuffer();
0 ignored issues
show
Bug introduced by
It seems like $mainStack->pop() can be null; however, buffer() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
515 1172
                                $value = $this->castToBool($buffer);
516 1172
                                if ($opCode === Opcodes::OP_NOTIF) {
517 164
                                    $value = !$value;
518
                                }
519
                            }
520 1172
                            $vfStack->push($value);
521 1172
                            break;
522
523
                        case Opcodes::OP_ELSE:
524 460
                            if ($vfStack->isEmpty()) {
525 16
                                throw new \RuntimeException('Unbalanced conditional');
526
                            }
527 452
                            $vfStack->push(!$vfStack->pop());
528 452
                            break;
529
530
                        case Opcodes::OP_ENDIF:
531 780
                            if ($vfStack->isEmpty()) {
532 28
                                throw new \RuntimeException('Unbalanced conditional');
533
                            }
534 764
                            $vfStack->pop();
535 764
                            break;
536
537
                        case Opcodes::OP_VERIFY:
538 120
                            if ($mainStack->isEmpty()) {
539 4
                                throw new \RuntimeException('Invalid stack operation');
540
                            }
541 116
                            $value = $this->castToBool($mainStack[-1]);
542 116
                            if (!$value) {
543 4
                                throw new \RuntimeException('Error: verify');
544
                            }
545 112
                            $mainStack->pop();
546 112
                            break;
547
548
                        case Opcodes::OP_TOALTSTACK:
549 32
                            if ($mainStack->isEmpty()) {
550 4
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
551
                            }
552 28
                            $altStack->push($mainStack->pop());
553 28
                            break;
554
555
                        case Opcodes::OP_FROMALTSTACK:
556 20
                            if ($altStack->isEmpty()) {
557 8
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
558
                            }
559 12
                            $mainStack->push($altStack->pop());
560 12
                            break;
561
562
                        case Opcodes::OP_IFDUP:
563
                            // If top value not zero, duplicate it.
564 24
                            if ($mainStack->isEmpty()) {
565 8
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
566
                            }
567 16
                            $vch = $mainStack[-1];
568 16
                            if ($this->castToBool($vch)) {
569 12
                                $mainStack->push($vch);
570
                            }
571 16
                            break;
572
573
                        case Opcodes::OP_DEPTH:
574 301
                            $num = count($mainStack);
575 301
                            $depth = Number::int($num)->getBuffer();
576 301
                            $mainStack->push($depth);
577 301
                            break;
578
579
                        case Opcodes::OP_DROP:
580 192
                            if ($mainStack->isEmpty()) {
581 8
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
582
                            }
583 184
                            $mainStack->pop();
584 184
                            break;
585
586
                        case Opcodes::OP_DUP:
587 112
                            if ($mainStack->isEmpty()) {
588 8
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
589
                            }
590 104
                            $vch = $mainStack[-1];
591 104
                            $mainStack->push($vch);
592 104
                            break;
593
594
                        case Opcodes::OP_NIP:
595 24
                            if (count($mainStack) < 2) {
596 12
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
597
                            }
598 12
                            unset($mainStack[-2]);
599 12
                            break;
600
601
                        case Opcodes::OP_OVER:
602 24
                            if (count($mainStack) < 2) {
603 12
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
604
                            }
605 12
                            $vch = $mainStack[-2];
606 12
                            $mainStack->push($vch);
607 12
                            break;
608
609
                        case Opcodes::OP_ROT:
610 48
                            if (count($mainStack) < 3) {
611 16
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
612
                            }
613 32
                            $mainStack->swap(-3, -2);
614 32
                            $mainStack->swap(-2, -1);
615 32
                            break;
616
617
                        case Opcodes::OP_SWAP:
618 40
                            if (count($mainStack) < 2) {
619 12
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
620
                            }
621 28
                            $mainStack->swap(-2, -1);
622 28
                            break;
623
624
                        case Opcodes::OP_TUCK:
625 24
                            if (count($mainStack) < 2) {
626 12
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
627
                            }
628 12
                            $vch = $mainStack[-1];
629 12
                            $mainStack->add(- 2, $vch);
630 12
                            break;
631
632
                        case Opcodes::OP_PICK:
633
                        case Opcodes::OP_ROLL:
634 116
                            if (count($mainStack) < 2) {
635 16
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
636
                            }
637
638 100
                            $n = Number::buffer($mainStack[-1], $minimal, 4)->getGmp();
639 92
                            $mainStack->pop();
640 92
                            if ($this->math->cmp($n, $zero) < 0 || $this->math->cmp($n, gmp_init(count($mainStack))) >= 0) {
641 20
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
642
                            }
643
644 72
                            $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), gmp_init(1)), 10);
645 72
                            $vch = $mainStack[$pos];
646 72
                            if ($opCode === Opcodes::OP_ROLL) {
647 36
                                unset($mainStack[$pos]);
648
                            }
649 72
                            $mainStack->push($vch);
650 72
                            break;
651
652
                        case Opcodes::OP_2DROP:
653 36
                            if (count($mainStack) < 2) {
654 4
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
655
                            }
656 32
                            $mainStack->pop();
657 32
                            $mainStack->pop();
658 32
                            break;
659
660
                        case Opcodes::OP_2DUP:
661 24
                            if (count($mainStack) < 2) {
662 12
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
663
                            }
664 12
                            $string1 = $mainStack[-2];
665 12
                            $string2 = $mainStack[-1];
666 12
                            $mainStack->push($string1);
667 12
                            $mainStack->push($string2);
668 12
                            break;
669
670
                        case Opcodes::OP_3DUP:
671 44
                            if (count($mainStack) < 3) {
672 16
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
673
                            }
674 28
                            $string1 = $mainStack[-3];
675 28
                            $string2 = $mainStack[-2];
676 28
                            $string3 = $mainStack[-1];
677 28
                            $mainStack->push($string1);
678 28
                            $mainStack->push($string2);
679 28
                            $mainStack->push($string3);
680 28
                            break;
681
682
                        case Opcodes::OP_2OVER:
683 20
                            if (count($mainStack) < 4) {
684 12
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
685
                            }
686 8
                            $string1 = $mainStack[-4];
687 8
                            $string2 = $mainStack[-3];
688 8
                            $mainStack->push($string1);
689 8
                            $mainStack->push($string2);
690 8
                            break;
691
692
                        case Opcodes::OP_2ROT:
693 40
                            if (count($mainStack) < 6) {
694 4
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
695
                            }
696 36
                            $string1 = $mainStack[-6];
697 36
                            $string2 = $mainStack[-5];
698 36
                            unset($mainStack[-6], $mainStack[-5]);
699 36
                            $mainStack->push($string1);
700 36
                            $mainStack->push($string2);
701 36
                            break;
702
703
                        case Opcodes::OP_2SWAP:
704 20
                            if (count($mainStack) < 4) {
705 12
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
706
                            }
707 8
                            $mainStack->swap(-3, -1);
708 8
                            $mainStack->swap(-4, -2);
709 8
                            break;
710
711
                        case Opcodes::OP_SIZE:
712 124
                            if ($mainStack->isEmpty()) {
713 8
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
714
                            }
715 116
                            $size = Number::int($mainStack[-1]->getSize());
716 116
                            $mainStack->push($size->getBuffer());
717 116
                            break;
718
719
                        case Opcodes::OP_EQUAL:
720
                        case Opcodes::OP_EQUALVERIFY:
721 1258
                            if (count($mainStack) < 2) {
722 16
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
723
                            }
724
725 1242
                            $equal = $mainStack[-2]->equals($mainStack[-1]);
726 1242
                            $mainStack->pop();
727 1242
                            $mainStack->pop();
728 1242
                            $mainStack->push($equal ? $this->vchTrue : $this->vchFalse);
729 1242
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
730 180
                                if ($equal) {
731 144
                                    $mainStack->pop();
732
                                } else {
733 36
                                    throw new \RuntimeException('Error EQUALVERIFY');
734
                                }
735
                            }
736
737 1210
                            break;
738
739
                        // Arithmetic operations
740
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
741 444
                            if ($mainStack->isEmpty()) {
742 24
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
743
                            }
744
745 420
                            $num = Number::buffer($mainStack[-1], $minimal)->getGmp();
746
747 328
                            if ($opCode === Opcodes::OP_1ADD) {
748 28
                                $num = $this->math->add($num, gmp_init(1));
749 300
                            } elseif ($opCode === Opcodes::OP_1SUB) {
750 12
                                $num = $this->math->sub($num, gmp_init(1));
751 288
                            } elseif ($opCode === Opcodes::OP_2MUL) {
752
                                $num = $this->math->mul(gmp_init(2), $num);
753 288
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
754 16
                                $num = $this->math->sub($zero, $num);
755 276
                            } elseif ($opCode === Opcodes::OP_ABS) {
756 20
                                if ($this->math->cmp($num, $zero) < 0) {
757 20
                                    $num = $this->math->sub($zero, $num);
758
                                }
759 256
                            } elseif ($opCode === Opcodes::OP_NOT) {
760 232
                                $num = gmp_init($this->math->cmp($num, $zero) === 0 ? 1 : 0);
761
                            } else {
762
                                // is OP_0NOTEQUAL
763 24
                                $num = gmp_init($this->math->cmp($num, $zero) !== 0 ? 1 : 0);
764
                            }
765
766 328
                            $mainStack->pop();
767
768 328
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
769
770 328
                            $mainStack->push($buffer);
771 328
                            break;
772
773 2209
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
774 656
                            if (count($mainStack) < 2) {
775 52
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
776
                            }
777
778 604
                            $num1 = Number::buffer($mainStack[-2], $minimal)->getGmp();
779 536
                            $num2 = Number::buffer($mainStack[-1], $minimal)->getGmp();
780
781 484
                            if ($opCode === Opcodes::OP_ADD) {
782 120
                                $num = $this->math->add($num1, $num2);
783 388
                            } else if ($opCode === Opcodes::OP_SUB) {
784 24
                                $num = $this->math->sub($num1, $num2);
785 364
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
786 32
                                $num = $this->math->cmp($num1, $zero) !== 0 && $this->math->cmp($num2, $zero) !== 0;
787 332
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
788 32
                                $num = $this->math->cmp($num1, $zero) !== 0 || $this->math->cmp($num2, $zero) !== 0;
789 300
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
790 112
                                $num = $this->math->cmp($num1, $num2) === 0;
791 220
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
792 16
                                $num = $this->math->cmp($num1, $num2) === 0;
793 204
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
794 20
                                $num = $this->math->cmp($num1, $num2) !== 0;
795 184
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) {
796 32
                                $num = $this->math->cmp($num1, $num2) < 0;
797 152
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
798 32
                                $num = $this->math->cmp($num1, $num2) > 0;
799 120
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
800 32
                                $num = $this->math->cmp($num1, $num2) <= 0;
801 88
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
802 32
                                $num = $this->math->cmp($num1, $num2) >= 0;
803 56
                            } elseif ($opCode === Opcodes::OP_MIN) {
804 28
                                $num = ($this->math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
805
                            } else {
806 28
                                $num = ($this->math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
807
                            }
808
809 484
                            $mainStack->pop();
810 484
                            $mainStack->pop();
811 484
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
812 484
                            $mainStack->push($buffer);
813
814 484
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
815 16
                                if ($this->castToBool($mainStack[-1])) {
816 16
                                    $mainStack->pop();
817
                                } else {
818
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
819
                                }
820
                            }
821 484
                            break;
822
823 1553
                        case Opcodes::OP_WITHIN:
824 60
                            if (count($mainStack) < 3) {
825 4
                                throw new \RuntimeException('Invalid stack operation');
826
                            }
827
828 56
                            $num1 = Number::buffer($mainStack[-3], $minimal)->getGmp();
829 52
                            $num2 = Number::buffer($mainStack[-2], $minimal)->getGmp();
830 48
                            $num3 = Number::buffer($mainStack[-1], $minimal)->getGmp();
831
832 44
                            $value = $this->math->cmp($num2, $num1) <= 0 && $this->math->cmp($num1, $num3) < 0;
833 44
                            $mainStack->pop();
834 44
                            $mainStack->pop();
835 44
                            $mainStack->pop();
836 44
                            $mainStack->push($value ? $this->vchTrue : $this->vchFalse);
837 44
                            break;
838
839
                        // Hash operation
840 1493
                        case Opcodes::OP_RIPEMD160:
841 1469
                        case Opcodes::OP_SHA1:
842 1437
                        case Opcodes::OP_SHA256:
843 1413
                        case Opcodes::OP_HASH160:
844 1183
                        case Opcodes::OP_HASH256:
845 415
                            if ($mainStack->isEmpty()) {
846 41
                                throw new \RuntimeException('Invalid stack operation');
847
                            }
848
849 374
                            $buffer = $mainStack[-1];
850 374
                            if ($opCode === Opcodes::OP_RIPEMD160) {
851 20
                                $hash = Hash::ripemd160($buffer);
852 358
                            } elseif ($opCode === Opcodes::OP_SHA1) {
853 24
                                $hash = Hash::sha1($buffer);
854 334
                            } elseif ($opCode === Opcodes::OP_SHA256) {
855 24
                                $hash = Hash::sha256($buffer);
856 318
                            } elseif ($opCode === Opcodes::OP_HASH160) {
857 298
                                $hash = Hash::sha256ripe160($buffer);
858
                            } else {
859 20
                                $hash = Hash::sha256d($buffer);
860
                            }
861
862 374
                            $mainStack->pop();
863 374
                            $mainStack->push($hash);
864 374
                            break;
865
866 1155
                        case Opcodes::OP_CODESEPARATOR:
867 4
                            $hashStartPos = $parser->getPosition();
868 4
                            break;
869
870 1151
                        case Opcodes::OP_CHECKSIG:
871 815
                        case Opcodes::OP_CHECKSIGVERIFY:
872 336
                            if (count($mainStack) < 2) {
873 8
                                throw new \RuntimeException('Invalid stack operation');
874
                            }
875
876 328
                            $vchPubKey = $mainStack[-1];
877 328
                            $vchSig = $mainStack[-2];
878
879 328
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
880
881 328
                            $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags);
882
883 232
                            $mainStack->pop();
884 232
                            $mainStack->pop();
885 232
                            $mainStack->push($success ? $this->vchTrue : $this->vchFalse);
886
887 232
                            if (!$success && ($flags & self::VERIFY_NULLFAIL) && $vchSig->getSize() > 0) {
888 4
                                throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Signature must be zero for failed OP_CHECK(MULTIS)SIG operation');
889
                            }
890
891 228
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
892
                                if ($success) {
893
                                    $mainStack->pop();
894
                                } else {
895
                                    throw new \RuntimeException('Checksig verify');
896
                                }
897
                            }
898 228
                            break;
899
900 815
                        case Opcodes::OP_CHECKMULTISIG:
901 495
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
902 448
                            $i = 1;
903 448
                            if (count($mainStack) < $i) {
904 4
                                throw new \RuntimeException('Invalid stack operation');
905
                            }
906
907 444
                            $keyCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
908 436
                            if ($keyCount < 0 || $keyCount > 20) {
909 8
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
910
                            }
911
912 428
                            $opCount += $keyCount;
913 428
                            $this->checkOpcodeCount($opCount);
914
915
                            // Extract positions of the keys, and signatures, from the stack.
916 428
                            $ikey = ++$i;
917 428
                            $ikey2 = $keyCount + 2;
918 428
                            $i += $keyCount;
919 428
                            if (count($mainStack) < $i) {
920 4
                                throw new \RuntimeException('Invalid stack operation');
921
                            }
922
923 424
                            $sigCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
924 412
                            if ($sigCount < 0 || $sigCount > $keyCount) {
925 8
                                throw new \RuntimeException('Invalid Signature count');
926
                            }
927
928 404
                            $isig = ++$i;
929 404
                            $i += $sigCount;
930
931
                            // Extract the script since the last OP_CODESEPARATOR
932 404
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
933
934 404
                            $fSuccess = true;
935 404
                            while ($fSuccess && $sigCount > 0) {
936
                                // Fetch the signature and public key
937 164
                                $sig = $mainStack[-$isig];
938 160
                                $pubkey = $mainStack[-$ikey];
939
940 160
                                if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) {
941 56
                                    $isig++;
942 56
                                    $sigCount--;
943
                                }
944
945 136
                                $ikey++;
946 136
                                $keyCount--;
947
948
                                // If there are more signatures left than keys left,
949
                                // then too many signatures have failed. Exit early,
950
                                // without checking any further signatures.
951 136
                                if ($sigCount > $keyCount) {
952 88
                                    $fSuccess = false;
953
                                }
954
                            }
955
956 368
                            while ($i-- > 1) {
957
                                // If the operation failed, we require that all signatures must be empty vector
958 368
                                if (!$fSuccess && ($flags & self::VERIFY_NULLFAIL) && !$ikey2 && $mainStack[-1]->getSize() > 0) {
959 8
                                    throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Bad signature must be empty vector');
960
                                }
961
962 368
                                if ($ikey2 > 0) {
963 368
                                    $ikey2--;
964
                                }
965
966 368
                                $mainStack->pop();
967
                            }
968
969
                            // A bug causes CHECKMULTISIG to consume one extra argument
970
                            // whose contents were not checked in any way.
971
                            //
972
                            // Unfortunately this is a potential source of mutability,
973
                            // so optionally verify it is exactly equal to zero prior
974
                            // to removing it from the stack.
975 360
                            if ($mainStack->isEmpty()) {
976 4
                                throw new \RuntimeException('Invalid stack operation');
977
                            }
978
979 360
                            if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) {
980 12
                                throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
981
                            }
982
983 348
                            $mainStack->pop();
984 348
                            $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
985
986 348
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
987 120
                                if ($fSuccess) {
988 120
                                    $mainStack->pop();
989
                                } else {
990
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
991
                                }
992
                            }
993 348
                            break;
994
995
                        default:
996 367
                            throw new \RuntimeException('Opcode not found');
997
                    }
998
999 4066
                    if (count($mainStack) + count($altStack) > 1000) {
1000 4520
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
1001
                    }
1002
                }
1003
            }
1004
1005 4459
            if (count($vfStack) !== 0) {
1006 24
                throw new \RuntimeException('Unbalanced conditional at script end');
1007
            }
1008
1009 4439
            return true;
1010 1665
        } catch (ScriptRuntimeException $e) {
1011
            // echo "\n Runtime: " . $e->getMessage() . "\n" . $e->getTraceAsString() . PHP_EOL;
1012
            // Failure due to script tags, can access flag: $e->getFailureFlag()
1013 344
            return false;
1014 1321
        } catch (\Exception $e) {
1015
            // echo "\n General: " . $e->getMessage()  . PHP_EOL . $e->getTraceAsString() . PHP_EOL;
1016 1321
            return false;
1017
        }
1018
    }
1019
}
1020