Completed
Push — master ( 7ac9fb...3b1821 )
by thomas
28:43
created

Interpreter   F

Complexity

Total Complexity 274

Size/Duplication

Total Lines 994
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 21

Test Coverage

Coverage 97.22%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 994
ccs 559
cts 575
cp 0.9722
rs 1.263
c 1
b 0
f 0
wmc 274
lcom 1
cbo 21

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A isValidSignatureEncoding() 0 11 2
D checkMinimalPush() 0 25 9
B castToBool() 0 14 5
A checkOpcodeCount() 0 8 2
C verifyWitnessProgram() 0 55 12
A checkExec() 0 11 3
F verify() 0 122 35
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
namespace BitWasp\Bitcoin\Script\Interpreter;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
7
use BitWasp\Bitcoin\Crypto\Hash;
8
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
9
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
10
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
11
use BitWasp\Bitcoin\Script\Opcodes;
12
use BitWasp\Bitcoin\Script\Script;
13
use BitWasp\Bitcoin\Script\ScriptFactory;
14
use BitWasp\Bitcoin\Script\ScriptInterface;
15
use BitWasp\Bitcoin\Script\ScriptWitness;
16
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
17
use BitWasp\Bitcoin\Script\WitnessProgram;
18
use BitWasp\Bitcoin\Signature\TransactionSignature;
19
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
20
use BitWasp\Bitcoin\Transaction\TransactionInputInterface;
21
use BitWasp\Buffertools\Buffer;
22
use BitWasp\Buffertools\BufferInterface;
23
24
class Interpreter implements InterpreterInterface
25
{
26
27
    /**
28
     * @var \BitWasp\Bitcoin\Math\Math
29
     */
30
    private $math;
31
32
    /**
33
     * @var BufferInterface
34
     */
35
    private $vchFalse;
36
37
    /**
38
     * @var BufferInterface
39
     */
40
    private $vchTrue;
41
42
    /**
43
     * @var array
44
     */
45
    private $disabledOps = [
46
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
47
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
48
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
49
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
50
    ];
51
52
    /**
53
     * @param EcAdapterInterface $ecAdapter
54
     */
55 208
    public function __construct(EcAdapterInterface $ecAdapter = null)
56
    {
57 208
        $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter();
58 208
        $this->math = $ecAdapter->getMath();
59 208
        $this->vchFalse = new Buffer("", 0, $this->math);
60 208
        $this->vchTrue = new Buffer("\x01", 1, $this->math);
61 208
    }
62
63
    /**
64
     * Cast the value to a boolean
65
     *
66
     * @param BufferInterface $value
67
     * @return bool
68
     */
69 1896
    public function castToBool(BufferInterface $value)
70
    {
71 1896
        $val = $value->getBinary();
72 1896
        for ($i = 0, $size = strlen($val); $i < $size; $i++) {
73 1756
            $chr = ord($val[$i]);
74 1756
            if ($chr !== 0) {
75 1754
                if (($i === ($size - 1)) && $chr === 0x80) {
76
                    return false;
77
                }
78 1754
                return true;
79
            }
80
        }
81 446
        return false;
82
    }
83
84
    /**
85
     * @param BufferInterface $signature
86
     * @return bool
87
     */
88
    public function isValidSignatureEncoding(BufferInterface $signature)
89
    {
90
        try {
91
            TransactionSignature::isDERSignature($signature);
92
            return true;
93
        } catch (SignatureNotCanonical $e) {
94
            /* In any case, we will return false outside this block */
95
        }
96
97
        return false;
98
    }
99
100
    /**
101
     * @param int $opCode
102
     * @param BufferInterface $pushData
103
     * @return bool
104
     * @throws \Exception
105
     */
106 232
    public function checkMinimalPush($opCode, BufferInterface $pushData)
107
    {
108 232
        $pushSize = $pushData->getSize();
109 232
        $binary = $pushData->getBinary();
110
111 232
        if ($pushSize === 0) {
112 110
            return $opCode === Opcodes::OP_0;
113 190
        } elseif ($pushSize === 1) {
114 46
            $first = ord($binary[0]);
115
116 46
            if ($first >= 1 && $first <= 16) {
117 34
                return $opCode === (Opcodes::OP_1 + ($first - 1));
118 14
            } elseif ($first === 0x81) {
119 14
                return $opCode === Opcodes::OP_1NEGATE;
120
            }
121 146
        } elseif ($pushSize <= 75) {
122 142
            return $opCode === $pushSize;
123 6
        } elseif ($pushSize <= 255) {
124 4
            return $opCode === Opcodes::OP_PUSHDATA1;
125 4
        } elseif ($pushSize <= 65535) {
126 4
            return $opCode === Opcodes::OP_PUSHDATA2;
127
        }
128
129 12
        return true;
130
    }
131
132
    /**
133
     * @param int $count
134
     * @return $this
135
     */
136 2350
    private function checkOpcodeCount($count)
137
    {
138 2350
        if ($count > 201) {
139 10
            throw new \RuntimeException('Error: Script op code count');
140
        }
141
142 2350
        return $this;
143
    }
144
145
    /**
146
     * @param WitnessProgram $witnessProgram
147
     * @param ScriptWitnessInterface $scriptWitness
148
     * @param int $flags
149
     * @param Checker $checker
150
     * @return bool
151
     */
152 166
    private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitnessInterface $scriptWitness, $flags, Checker $checker)
153
    {
154 166
        $witnessCount = count($scriptWitness);
155
156 166
        if ($witnessProgram->getVersion() === 0) {
157 164
            $buffer = $witnessProgram->getProgram();
158 164
            if ($buffer->getSize() === 32) {
159
                // Version 0 segregated witness program: SHA256(Script) in program, Script + inputs in witness
160 140
                if ($witnessCount === 0) {
161
                    // Must contain script at least
162 2
                    return false;
163
                }
164
165 138
                $scriptPubKey = new Script($scriptWitness[$witnessCount - 1]);
166 138
                $stackValues = $scriptWitness->slice(0, -1);
167 138
                if (!$buffer->equals($scriptPubKey->getWitnessScriptHash())) {
168 138
                    return false;
169
                }
170 24
            } elseif ($buffer->getSize() === 20) {
171
                // Version 0 special case for pay-to-pubkeyhash
172 22
                if ($witnessCount !== 2) {
173
                    // 2 items in witness - <signature> <pubkey>
174 10
                    return false;
175
                }
176
177 20
                $scriptPubKey = ScriptFactory::scriptPubKey()->payToPubKeyHash($buffer);
178 20
                $stackValues = $scriptWitness;
179
            } else {
180 156
                return false;
181
            }
182 2
        } elseif ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
183 2
            return false;
184
        } else {
185
            return false;
186
        }
187
188 154
        $mainStack = new Stack();
189 154
        foreach ($stackValues as $value) {
190 136
            $mainStack->push($value);
191
        }
192
193 154
        if (!$this->evaluate($scriptPubKey, $mainStack, SigHash::V1, $flags, $checker)) {
194 54
            return false;
195
        }
196
197 114
        if ($mainStack->count() !== 1) {
198 28
            return false;
199
        }
200
201 86
        if (!$this->castToBool($mainStack->bottom())) {
202 18
            return false;
203
        }
204
205 68
        return true;
206
    }
207
208
    /**
209
     * @param ScriptInterface $scriptSig
210
     * @param ScriptInterface $scriptPubKey
211
     * @param int $flags
212
     * @param Checker $checker
213
     * @param ScriptWitnessInterface|null $witness
214
     * @return bool
215
     */
216 2482
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $flags, Checker $checker, ScriptWitnessInterface $witness = null)
217
    {
218 2482
        static $emptyWitness = null;
219 2482
        if ($emptyWitness === null) {
220 2
            $emptyWitness = new ScriptWitness([]);
221
        }
222
223 2482
        $witness = is_null($witness) ? $emptyWitness : $witness;
224
225 2482
        if (($flags & self::VERIFY_SIGPUSHONLY) !== 0 && !$scriptSig->isPushOnly()) {
226 4
            return false;
227
        }
228
229 2478
        $stack = new Stack();
230 2478
        if (!$this->evaluate($scriptSig, $stack, SigHash::V0, $flags, $checker)) {
231 112
            return false;
232
        }
233
234 2366
        $backup = [];
235 2366
        if ($flags & self::VERIFY_P2SH) {
236 1758
            foreach ($stack as $s) {
237 1414
                $backup[] = $s;
238
            }
239
        }
240
241 2366
        if (!$this->evaluate($scriptPubKey, $stack, SigHash::V0, $flags, $checker)) {
242 698
            return false;
243
        }
244
245 1692
        if ($stack->isEmpty()) {
246 32
            return false;
247
        }
248
249 1660
        if (false === $this->castToBool($stack[-1])) {
250 96
            return false;
251
        }
252
253 1564
        $program = null;
254 1564
        if ($flags & self::VERIFY_WITNESS) {
255 270
            if ($scriptPubKey->isWitness($program)) {
256
                /** @var WitnessProgram $program */
257 86
                if ($scriptSig->getBuffer()->getSize() !== 0) {
258 2
                    return false;
259
                }
260
261 84
                if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
262 66
                    return false;
263
                }
264
265 28
                $stack->resize(1);
266
            }
267
        }
268
269 1506
        if ($flags & self::VERIFY_P2SH && (new OutputClassifier())->isPayToScriptHash($scriptPubKey)) {
270 154
            if (!$scriptSig->isPushOnly()) {
271 8
                return false;
272
            }
273
274 146
            $stack = new Stack();
275 146
            foreach ($backup as $i) {
276 146
                $stack->push($i);
277
            }
278
279
            // Restore mainStack to how it was after evaluating scriptSig
280 146
            if ($stack->isEmpty()) {
281
                return false;
282
            }
283
284
            // Load redeemscript as the scriptPubKey
285 146
            $scriptPubKey = new Script($stack->bottom());
286 146
            $stack->pop();
287
288 146
            if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
289 20
                return false;
290
            }
291
292 132
            if ($stack->isEmpty()) {
293 10
                return false;
294
            }
295
296 122
            if (!$this->castToBool($stack->bottom())) {
297 2
                return false;
298
            }
299
300 120
            if ($flags & self::VERIFY_WITNESS) {
301 100
                if ($scriptPubKey->isWitness($program)) {
302
                    /** @var WitnessProgram $program */
303 84
                    if (!$scriptSig->equals(ScriptFactory::sequence([$scriptPubKey->getBuffer()]))) {
304 2
                        return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
305
                    }
306
307 82
                    if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
308 54
                        return false;
309
                    }
310
311 40
                    $stack->resize(1);
312
                }
313
            }
314
        }
315
316 1428
        if ($flags & self::VERIFY_CLEAN_STACK) {
317 6
            if (!($flags & self::VERIFY_P2SH !== 0) && ($flags & self::VERIFY_WITNESS !== 0)) {
318
                return false; // implied flags required
319
            }
320
321 6
            if (count($stack) !== 1) {
322 4
                return false; // Cleanstack
323
            }
324
        }
325
326 1424
        if ($flags & self::VERIFY_WITNESS) {
327 154
            if (!$flags & self::VERIFY_P2SH) {
328
                return false; //
329
            }
330
331 154
            if ($program === null && !$witness->isNull()) {
332 2
                return false; // SCRIPT_ERR_WITNESS_UNEXPECTED
333
            }
334
        }
335
336 1422
        return true;
337
    }
338
339
    /**
340
     * @param Stack $vfStack
341
     * @param bool $value
342
     * @return bool
343
     */
344 2508
    public function checkExec(Stack $vfStack, $value)
345
    {
346 2508
        $ret = 0;
347 2508
        foreach ($vfStack as $item) {
348 602
            if ($item === $value) {
349 602
                $ret++;
350
            }
351
        }
352
353 2508
        return $ret;
354
    }
355
356
    /**
357
     * @param ScriptInterface $script
358
     * @param Stack $mainStack
359
     * @param int $sigVersion
360
     * @param int $flags
361
     * @param Checker $checker
362
     * @return bool
363
     */
364 2528
    public function evaluate(ScriptInterface $script, Stack $mainStack, $sigVersion, $flags, Checker $checker)
365
    {
366 2528
        $hashStartPos = 0;
367 2528
        $opCount = 0;
368 2528
        $zero = gmp_init(0, 10);
369 2528
        $altStack = new Stack();
370 2528
        $vfStack = new Stack();
371 2528
        $minimal = ($flags & self::VERIFY_MINIMALDATA) !== 0;
372 2528
        $parser = $script->getScriptParser();
373
374 2528
        if ($script->getBuffer()->getSize() > 10000) {
375 2
            return false;
376
        }
377
378
        try {
379 2528
            foreach ($parser as $operation) {
380 2478
                $opCode = $operation->getOp();
381 2478
                $pushData = $operation->getData();
382 2478
                $fExec = !$this->checkExec($vfStack, false);
383
384
                // If pushdata was written to
385 2478
                if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
386 6
                    throw new \RuntimeException('Error - push size');
387
                }
388
389
                // OP_RESERVED should not count towards opCount
390 2476
                if ($opCode > Opcodes::OP_16 && ++$opCount) {
391 2350
                    $this->checkOpcodeCount($opCount);
392
                }
393
394 2476
                if (in_array($opCode, $this->disabledOps, true)) {
395 48
                    throw new \RuntimeException('Disabled Opcode');
396
                }
397
398 2476
                if ($fExec && $operation->isPush()) {
399
                    // In range of a pushdata opcode
400 1876
                    if ($minimal && !$this->checkMinimalPush($opCode, $pushData)) {
401 42
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
402
                    }
403
404 1834
                    $mainStack->push($pushData);
405
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
406 2396
                } elseif ($fExec || (Opcodes::OP_IF <= $opCode && $opCode <= Opcodes::OP_ENDIF)) {
407
                    // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n";
408
                    switch ($opCode) {
409 2396
                        case Opcodes::OP_1NEGATE:
410 2396
                        case Opcodes::OP_1:
411 2352
                        case Opcodes::OP_2:
412 2344
                        case Opcodes::OP_3:
413 2344
                        case Opcodes::OP_4:
414 2344
                        case Opcodes::OP_5:
415 2344
                        case Opcodes::OP_6:
416 2344
                        case Opcodes::OP_7:
417 2342
                        case Opcodes::OP_8:
418 2342
                        case Opcodes::OP_9:
419 2342
                        case Opcodes::OP_10:
420 2342
                        case Opcodes::OP_11:
421 2340
                        case Opcodes::OP_12:
422 2340
                        case Opcodes::OP_13:
423 2340
                        case Opcodes::OP_14:
424 2340
                        case Opcodes::OP_15:
425 2340
                        case Opcodes::OP_16:
426 1416
                            $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode);
427 1416
                            $mainStack->push(Number::int($num)->getBuffer());
428 1416
                            break;
429
430 2340
                        case Opcodes::OP_CHECKLOCKTIMEVERIFY:
431 18
                            if (!($flags & self::VERIFY_CHECKLOCKTIMEVERIFY)) {
432 12
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
433 2
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
434
                                }
435 10
                                break;
436
                            }
437
438 6
                            if ($mainStack->isEmpty()) {
439
                                throw new \RuntimeException('Invalid stack operation - CLTV');
440
                            }
441
442 6
                            $lockTime = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
443 6
                            if (!$checker->checkLockTime($lockTime)) {
444
                                throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
445
                            }
446
447 6
                            break;
448
449 2338
                        case Opcodes::OP_CHECKSEQUENCEVERIFY:
450 28
                            if (!($flags & self::VERIFY_CHECKSEQUENCEVERIFY)) {
451 12
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
452 2
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
453
                                }
454 10
                                break;
455
                            }
456
457 16
                            if ($mainStack->isEmpty()) {
458 2
                                throw new \RuntimeException('Invalid stack operation - CSV');
459
                            }
460
461 14
                            $sequence = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
462 12
                            $nSequence = $sequence->getGmp();
463 12
                            if ($this->math->cmp($nSequence, $zero) < 0) {
464 2
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
465
                            }
466
467 10
                            if ($this->math->cmp($this->math->bitwiseAnd($nSequence, gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) {
468 2
                                break;
469
                            }
470
471 8
                            if (!$checker->checkSequence($sequence)) {
472 4
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied sequence');
473
                            }
474 4
                            break;
475
476 2324
                        case Opcodes::OP_NOP1:
477 2322
                        case Opcodes::OP_NOP4:
478 2320
                        case Opcodes::OP_NOP5:
479 2318
                        case Opcodes::OP_NOP6:
480 2316
                        case Opcodes::OP_NOP7:
481 2314
                        case Opcodes::OP_NOP8:
482 2312
                        case Opcodes::OP_NOP9:
483 2310
                        case Opcodes::OP_NOP10:
484 54
                            if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
485 20
                                throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
486
                            }
487 34
                            break;
488
489 2304
                        case Opcodes::OP_NOP:
490 168
                            break;
491
492 2258
                        case Opcodes::OP_IF:
493 2204
                        case Opcodes::OP_NOTIF:
494
                            // <expression> if [statements] [else [statements]] endif
495 656
                            $value = false;
496 656
                            if ($fExec) {
497 656
                                if ($mainStack->isEmpty()) {
498 24
                                    throw new \RuntimeException('Unbalanced conditional');
499
                                }
500 632
                                $vch = $mainStack[-1];
501
502 632
                                if ($sigVersion === SigHash::V1 && ($flags & self::VERIFY_MINIMALIF)) {
503 40
                                    if ($vch->getSize() > 1) {
504 8
                                        throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded');
505
                                    }
506
507 32
                                    if ($vch->getSize() === 1 && $vch->getBinary() !== "\x01") {
508 16
                                        throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded');
509
                                    }
510
                                }
511
512 608
                                $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...
513 608
                                $value = $this->castToBool($buffer);
514 608
                                if ($opCode === Opcodes::OP_NOTIF) {
515 90
                                    $value = !$value;
516
                                }
517
                            }
518 608
                            $vfStack->push($value);
519 608
                            break;
520
521 2190
                        case Opcodes::OP_ELSE:
522 248
                            if ($vfStack->isEmpty()) {
523 8
                                throw new \RuntimeException('Unbalanced conditional');
524
                            }
525 244
                            $vfStack->push(!$vfStack->pop());
526 244
                            break;
527
528 2184
                        case Opcodes::OP_ENDIF:
529 412
                            if ($vfStack->isEmpty()) {
530 14
                                throw new \RuntimeException('Unbalanced conditional');
531
                            }
532 404
                            $vfStack->pop();
533 404
                            break;
534
535 1864
                        case Opcodes::OP_VERIFY:
536 60
                            if ($mainStack->isEmpty()) {
537 2
                                throw new \RuntimeException('Invalid stack operation');
538
                            }
539 58
                            $value = $this->castToBool($mainStack[-1]);
540 58
                            if (!$value) {
541 2
                                throw new \RuntimeException('Error: verify');
542
                            }
543 56
                            $mainStack->pop();
544 56
                            break;
545
546 1850
                        case Opcodes::OP_TOALTSTACK:
547 16
                            if ($mainStack->isEmpty()) {
548 2
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
549
                            }
550 14
                            $altStack->push($mainStack->pop());
551 14
                            break;
552
553 1846
                        case Opcodes::OP_FROMALTSTACK:
554 10
                            if ($altStack->isEmpty()) {
555 4
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
556
                            }
557 6
                            $mainStack->push($altStack->pop());
558 6
                            break;
559
560 1840
                        case Opcodes::OP_IFDUP:
561
                            // If top value not zero, duplicate it.
562 12
                            if ($mainStack->isEmpty()) {
563 4
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
564
                            }
565 8
                            $vch = $mainStack[-1];
566 8
                            if ($this->castToBool($vch)) {
567 6
                                $mainStack->push($vch);
568
                            }
569 8
                            break;
570
571 1834
                        case Opcodes::OP_DEPTH:
572 152
                            $num = count($mainStack);
573 152
                            $depth = Number::int($num)->getBuffer();
574 152
                            $mainStack->push($depth);
575 152
                            break;
576
577 1816
                        case Opcodes::OP_DROP:
578 106
                            if ($mainStack->isEmpty()) {
579 4
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
580
                            }
581 102
                            $mainStack->pop();
582 102
                            break;
583
584 1810
                        case Opcodes::OP_DUP:
585 92
                            if ($mainStack->isEmpty()) {
586 28
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
587
                            }
588 88
                            $vch = $mainStack[-1];
589 88
                            $mainStack->push($vch);
590 88
                            break;
591
592 1792
                        case Opcodes::OP_NIP:
593 12
                            if (count($mainStack) < 2) {
594 6
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
595
                            }
596 6
                            unset($mainStack[-2]);
597 6
                            break;
598
599 1780
                        case Opcodes::OP_OVER:
600 12
                            if (count($mainStack) < 2) {
601 6
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
602
                            }
603 6
                            $vch = $mainStack[-2];
604 6
                            $mainStack->push($vch);
605 6
                            break;
606
607 1772
                        case Opcodes::OP_ROT:
608 24
                            if (count($mainStack) < 3) {
609 8
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
610
                            }
611 16
                            $mainStack->swap(-3, -2);
612 16
                            $mainStack->swap(-2, -1);
613 16
                            break;
614
615 1760
                        case Opcodes::OP_SWAP:
616 20
                            if (count($mainStack) < 2) {
617 6
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
618
                            }
619 14
                            $mainStack->swap(-2, -1);
620 14
                            break;
621
622 1752
                        case Opcodes::OP_TUCK:
623 12
                            if (count($mainStack) < 2) {
624 6
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
625
                            }
626 6
                            $vch = $mainStack[-1];
627 6
                            $mainStack->add(- 2, $vch);
628 6
                            break;
629
630 1744
                        case Opcodes::OP_PICK:
631 1726
                        case Opcodes::OP_ROLL:
632 58
                            if (count($mainStack) < 2) {
633 8
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
634
                            }
635
636 50
                            $n = Number::buffer($mainStack[-1], $minimal, 4)->getGmp();
637 46
                            $mainStack->pop();
638 46
                            if ($this->math->cmp($n, $zero) < 0 || $this->math->cmp($n, gmp_init(count($mainStack))) >= 0) {
639 10
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
640
                            }
641
642 36
                            $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), gmp_init(1)), 10);
643 36
                            $vch = $mainStack[$pos];
644 36
                            if ($opCode === Opcodes::OP_ROLL) {
645 18
                                unset($mainStack[$pos]);
646
                            }
647 36
                            $mainStack->push($vch);
648 36
                            break;
649
650 1710
                        case Opcodes::OP_2DROP:
651 18
                            if (count($mainStack) < 2) {
652 2
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
653
                            }
654 16
                            $mainStack->pop();
655 16
                            $mainStack->pop();
656 16
                            break;
657
658 1706
                        case Opcodes::OP_2DUP:
659 12
                            if (count($mainStack) < 2) {
660 6
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
661
                            }
662 6
                            $string1 = $mainStack[-2];
663 6
                            $string2 = $mainStack[-1];
664 6
                            $mainStack->push($string1);
665 6
                            $mainStack->push($string2);
666 6
                            break;
667
668 1698
                        case Opcodes::OP_3DUP:
669 22
                            if (count($mainStack) < 3) {
670 8
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
671
                            }
672 14
                            $string1 = $mainStack[-3];
673 14
                            $string2 = $mainStack[-2];
674 14
                            $string3 = $mainStack[-1];
675 14
                            $mainStack->push($string1);
676 14
                            $mainStack->push($string2);
677 14
                            $mainStack->push($string3);
678 14
                            break;
679
680 1678
                        case Opcodes::OP_2OVER:
681 10
                            if (count($mainStack) < 4) {
682 6
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
683
                            }
684 4
                            $string1 = $mainStack[-4];
685 4
                            $string2 = $mainStack[-3];
686 4
                            $mainStack->push($string1);
687 4
                            $mainStack->push($string2);
688 4
                            break;
689
690 1670
                        case Opcodes::OP_2ROT:
691 20
                            if (count($mainStack) < 6) {
692 2
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
693
                            }
694 18
                            $string1 = $mainStack[-6];
695 18
                            $string2 = $mainStack[-5];
696 18
                            unset($mainStack[-6], $mainStack[-5]);
697 18
                            $mainStack->push($string1);
698 18
                            $mainStack->push($string2);
699 18
                            break;
700
701 1666
                        case Opcodes::OP_2SWAP:
702 10
                            if (count($mainStack) < 4) {
703 6
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
704
                            }
705 4
                            $mainStack->swap(-3, -1);
706 4
                            $mainStack->swap(-4, -2);
707 4
                            break;
708
709 1658
                        case Opcodes::OP_SIZE:
710 62
                            if ($mainStack->isEmpty()) {
711 4
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
712
                            }
713 58
                            $size = Number::int($mainStack[-1]->getSize());
714 58
                            $mainStack->push($size->getBuffer());
715 58
                            break;
716
717 1652
                        case Opcodes::OP_EQUAL:
718 1430
                        case Opcodes::OP_EQUALVERIFY:
719 730
                            if (count($mainStack) < 2) {
720 8
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
721
                            }
722
723 722
                            $equal = $mainStack[-2]->equals($mainStack[-1]);
724 722
                            $mainStack->pop();
725 722
                            $mainStack->pop();
726 722
                            $mainStack->push($equal ? $this->vchTrue : $this->vchFalse);
727 722
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
728 126
                                if ($equal) {
729 108
                                    $mainStack->pop();
730
                                } else {
731 18
                                    throw new \RuntimeException('Error EQUALVERIFY');
732
                                }
733
                            }
734
735 706
                            break;
736
737
                        // Arithmetic operations
738 1368
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
739 222
                            if ($mainStack->isEmpty()) {
740 12
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
741
                            }
742
743 210
                            $num = Number::buffer($mainStack[-1], $minimal)->getGmp();
744
745 164
                            if ($opCode === Opcodes::OP_1ADD) {
746 14
                                $num = $this->math->add($num, gmp_init(1));
747 150
                            } elseif ($opCode === Opcodes::OP_1SUB) {
748 6
                                $num = $this->math->sub($num, gmp_init(1));
749 144
                            } elseif ($opCode === Opcodes::OP_2MUL) {
750
                                $num = $this->math->mul(gmp_init(2), $num);
751 144
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
752 8
                                $num = $this->math->sub($zero, $num);
753 138
                            } elseif ($opCode === Opcodes::OP_ABS) {
754 10
                                if ($this->math->cmp($num, $zero) < 0) {
755 10
                                    $num = $this->math->sub($zero, $num);
756
                                }
757 128
                            } elseif ($opCode === Opcodes::OP_NOT) {
758 116
                                $num = gmp_init($this->math->cmp($num, $zero) === 0 ? 1 : 0);
759
                            } else {
760
                                // is OP_0NOTEQUAL
761 12
                                $num = gmp_init($this->math->cmp($num, $zero) !== 0 ? 1 : 0);
762
                            }
763
764 164
                            $mainStack->pop();
765
766 164
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
767
768 164
                            $mainStack->push($buffer);
769 164
                            break;
770
771 1250
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
772 328
                            if (count($mainStack) < 2) {
773 26
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
774
                            }
775
776 302
                            $num1 = Number::buffer($mainStack[-2], $minimal)->getGmp();
777 268
                            $num2 = Number::buffer($mainStack[-1], $minimal)->getGmp();
778
779 242
                            if ($opCode === Opcodes::OP_ADD) {
780 60
                                $num = $this->math->add($num1, $num2);
781 194
                            } else if ($opCode === Opcodes::OP_SUB) {
782 12
                                $num = $this->math->sub($num1, $num2);
783 182
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
784 16
                                $num = $this->math->cmp($num1, $zero) !== 0 && $this->math->cmp($num2, $zero) !== 0;
785 166
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
786 16
                                $num = $this->math->cmp($num1, $zero) !== 0 || $this->math->cmp($num2, $zero) !== 0;
787 150
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
788 56
                                $num = $this->math->cmp($num1, $num2) === 0;
789 110
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
790 8
                                $num = $this->math->cmp($num1, $num2) === 0;
791 102
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
792 10
                                $num = $this->math->cmp($num1, $num2) !== 0;
793 92
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) {
794 16
                                $num = $this->math->cmp($num1, $num2) < 0;
795 76
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
796 16
                                $num = $this->math->cmp($num1, $num2) > 0;
797 60
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
798 16
                                $num = $this->math->cmp($num1, $num2) <= 0;
799 44
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
800 16
                                $num = $this->math->cmp($num1, $num2) >= 0;
801 28
                            } elseif ($opCode === Opcodes::OP_MIN) {
802 14
                                $num = ($this->math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
803
                            } else {
804 14
                                $num = ($this->math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
805
                            }
806
807 242
                            $mainStack->pop();
808 242
                            $mainStack->pop();
809 242
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
810 242
                            $mainStack->push($buffer);
811
812 242
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
813 8
                                if ($this->castToBool($mainStack[-1])) {
814 8
                                    $mainStack->pop();
815
                                } else {
816
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
817
                                }
818
                            }
819 242
                            break;
820
821 922
                        case Opcodes::OP_WITHIN:
822 30
                            if (count($mainStack) < 3) {
823 2
                                throw new \RuntimeException('Invalid stack operation');
824
                            }
825
826 28
                            $num1 = Number::buffer($mainStack[-3], $minimal)->getGmp();
827 26
                            $num2 = Number::buffer($mainStack[-2], $minimal)->getGmp();
828 24
                            $num3 = Number::buffer($mainStack[-1], $minimal)->getGmp();
829
830 22
                            $value = $this->math->cmp($num2, $num1) <= 0 && $this->math->cmp($num1, $num3) < 0;
831 22
                            $mainStack->pop();
832 22
                            $mainStack->pop();
833 22
                            $mainStack->pop();
834 22
                            $mainStack->push($value ? $this->vchTrue : $this->vchFalse);
835 22
                            break;
836
837
                        // Hash operation
838 892
                        case Opcodes::OP_RIPEMD160:
839 880
                        case Opcodes::OP_SHA1:
840 864
                        case Opcodes::OP_SHA256:
841 852
                        case Opcodes::OP_HASH160:
842 692
                        case Opcodes::OP_HASH256:
843 310
                            if ($mainStack->isEmpty()) {
844 22
                                throw new \RuntimeException('Invalid stack operation');
845
                            }
846
847 288
                            $buffer = $mainStack[-1];
848 288
                            if ($opCode === Opcodes::OP_RIPEMD160) {
849 10
                                $hash = Hash::ripemd160($buffer);
850 280
                            } elseif ($opCode === Opcodes::OP_SHA1) {
851 12
                                $hash = Hash::sha1($buffer);
852 268
                            } elseif ($opCode === Opcodes::OP_SHA256) {
853 12
                                $hash = Hash::sha256($buffer);
854 260
                            } elseif ($opCode === Opcodes::OP_HASH160) {
855 250
                                $hash = Hash::sha256ripe160($buffer);
856
                            } else {
857 10
                                $hash = Hash::sha256d($buffer);
858
                            }
859
860 288
                            $mainStack->pop();
861 288
                            $mainStack->push($hash);
862 288
                            break;
863
864 678
                        case Opcodes::OP_CODESEPARATOR:
865 2
                            $hashStartPos = $parser->getPosition();
866 2
                            break;
867
868 676
                        case Opcodes::OP_CHECKSIG:
869 442
                        case Opcodes::OP_CHECKSIGVERIFY:
870 252
                            if (count($mainStack) < 2) {
871 14
                                throw new \RuntimeException('Invalid stack operation');
872
                            }
873
874 248
                            $vchPubKey = $mainStack[-1];
875 248
                            $vchSig = $mainStack[-2];
876
877 248
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
878
879 248
                            $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags);
880
881 200
                            $mainStack->pop();
882 200
                            $mainStack->pop();
883 200
                            $mainStack->push($success ? $this->vchTrue : $this->vchFalse);
884
885 200
                            if (!$success && ($flags & self::VERIFY_NULLFAIL) && $vchSig->getSize() > 0) {
886 2
                                throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Signature must be zero for failed OP_CHECK(MULTIS)SIG operation');
887
                            }
888
889 198
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
890 8
                                if ($success) {
891 8
                                    $mainStack->pop();
892
                                } else {
893
                                    throw new \RuntimeException('Checksig verify');
894
                                }
895
                            }
896 198
                            break;
897
898 438
                        case Opcodes::OP_CHECKMULTISIG:
899 262
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
900 250
                            $i = 1;
901 250
                            if (count($mainStack) < $i) {
902 2
                                throw new \RuntimeException('Invalid stack operation');
903
                            }
904
905 248
                            $keyCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
906 244
                            if ($keyCount < 0 || $keyCount > 20) {
907 4
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
908
                            }
909
910 240
                            $opCount += $keyCount;
911 240
                            $this->checkOpcodeCount($opCount);
912
913
                            // Extract positions of the keys, and signatures, from the stack.
914 240
                            $ikey = ++$i;
915 240
                            $ikey2 = $keyCount + 2;
916 240
                            $i += $keyCount;
917 240
                            if (count($mainStack) < $i) {
918 2
                                throw new \RuntimeException('Invalid stack operation');
919
                            }
920
921 238
                            $sigCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
922 232
                            if ($sigCount < 0 || $sigCount > $keyCount) {
923 4
                                throw new \RuntimeException('Invalid Signature count');
924
                            }
925
926 228
                            $isig = ++$i;
927 228
                            $i += $sigCount;
928
929
                            // Extract the script since the last OP_CODESEPARATOR
930 228
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
931
932 228
                            $fSuccess = true;
933 228
                            while ($fSuccess && $sigCount > 0) {
934
                                // Fetch the signature and public key
935 108
                                $sig = $mainStack[-$isig];
936 106
                                $pubkey = $mainStack[-$ikey];
937
938 106
                                if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) {
939 52
                                    $isig++;
940 52
                                    $sigCount--;
941
                                }
942
943 94
                                $ikey++;
944 94
                                $keyCount--;
945
946
                                // If there are more signatures left than keys left,
947
                                // then too many signatures have failed. Exit early,
948
                                // without checking any further signatures.
949 94
                                if ($sigCount > $keyCount) {
950 46
                                    $fSuccess = false;
951
                                }
952
                            }
953
954 210
                            while ($i-- > 1) {
955
                                // If the operation failed, we require that all signatures must be empty vector
956 210
                                if (!$fSuccess && ($flags & self::VERIFY_NULLFAIL) && !$ikey2 && $mainStack[-1]->getSize() > 0) {
957 4
                                    throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Bad signature must be empty vector');
958
                                }
959
960 210
                                if ($ikey2 > 0) {
961 210
                                    $ikey2--;
962
                                }
963
964 210
                                $mainStack->pop();
965
                            }
966
967
                            // A bug causes CHECKMULTISIG to consume one extra argument
968
                            // whose contents were not checked in any way.
969
                            //
970
                            // Unfortunately this is a potential source of mutability,
971
                            // so optionally verify it is exactly equal to zero prior
972
                            // to removing it from the stack.
973 206
                            if ($mainStack->isEmpty()) {
974 2
                                throw new \RuntimeException('Invalid stack operation');
975
                            }
976
977 206
                            if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) {
978 6
                                throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
979
                            }
980
981 200
                            $mainStack->pop();
982 200
                            $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
983
984 200
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
985 70
                                if ($fSuccess) {
986 70
                                    $mainStack->pop();
987
                                } else {
988
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
989
                                }
990
                            }
991 200
                            break;
992
993
                        default:
994 188
                            throw new \RuntimeException('Opcode not found');
995
                    }
996
997 2168
                    if (count($mainStack) + count($altStack) > 1000) {
998 2412
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
999
                    }
1000
                }
1001
            }
1002
1003 2426
            if (count($vfStack) !== 0) {
1004 12
                throw new \RuntimeException('Unbalanced conditional at script end');
1005
            }
1006
1007 2416
            return true;
1008 884
        } catch (ScriptRuntimeException $e) {
1009
            // echo "\n Runtime: " . $e->getMessage() . "\n" . $e->getTraceAsString() . PHP_EOL;
1010
            // Failure due to script tags, can access flag: $e->getFailureFlag()
1011 172
            return false;
1012 712
        } catch (\Exception $e) {
1013
            // echo "\n General: " . $e->getMessage()  . PHP_EOL . $e->getTraceAsString() . PHP_EOL;
1014 712
            return false;
1015
        }
1016
    }
1017
}
1018