Completed
Push — master ( 73b5ee...282ca9 )
by thomas
10s
created

Interpreter   F

Complexity

Total Complexity 261

Size/Duplication

Total Lines 971
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 21

Test Coverage

Coverage 93.54%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 971
ccs 565
cts 604
cp 0.9354
rs 1.263
c 2
b 0
f 0
wmc 261
lcom 1
cbo 21

9 Methods

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

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\SignatureNotCanonical;
9
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
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 138
    public function __construct(EcAdapterInterface $ecAdapter = null)
56
    {
57 138
        $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter();
58 138
        $this->math = $ecAdapter->getMath();
59 138
        $this->vchFalse = new Buffer("", 0, $this->math);
60 138
        $this->vchTrue = new Buffer("\x01", 1, $this->math);
61 138
    }
62
63
    /**
64
     * Cast the value to a boolean
65
     *
66
     * @param BufferInterface $value
67
     * @return bool
68
     */
69 1692
    public function castToBool(BufferInterface $value)
70
    {
71 1692
        $val = $value->getBinary();
72 1692
        for ($i = 0, $size = strlen($val); $i < $size; $i++) {
73 1550
            $chr = ord($val[$i]);
74 1550
            if ($chr !== 0) {
75 1544
                if (($i === ($size - 1)) && $chr === 0x80) {
76
                    return false;
77
                }
78 1544
                return true;
79
            }
80 2
        }
81 404
        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 236
    public function checkMinimalPush($opCode, BufferInterface $pushData)
107
    {
108 236
        $pushSize = $pushData->getSize();
109 236
        $binary = $pushData->getBinary();
110
111 236
        if ($pushSize === 0) {
112 114
            return $opCode === Opcodes::OP_0;
113 194
        } elseif ($pushSize === 1) {
114 50
            $first = ord($binary[0]);
115
116 50
            if ($first >= 1 && $first <= 16) {
117 38
                return $opCode === (Opcodes::OP_1 + ($first - 1));
118 18
            } elseif ($first === 0x81) {
119 18
                return $opCode === Opcodes::OP_1NEGATE;
120
            }
121 150
        } elseif ($pushSize <= 75) {
122 146
            return $opCode === $pushSize;
123 10
        } elseif ($pushSize <= 255) {
124 8
            return $opCode === Opcodes::OP_PUSHDATA1;
125 8
        } elseif ($pushSize <= 65535) {
126 8
            return $opCode === Opcodes::OP_PUSHDATA2;
127
        }
128
129 16
        return true;
130
    }
131
132
    /**
133
     * @param int $count
134
     * @return $this
135
     */
136 2246
    private function checkOpcodeCount($count)
137
    {
138 2246
        if ($count > 201) {
139 10
            throw new \RuntimeException('Error: Script op code count');
140
        }
141
142 2246
        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 54
    private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitnessInterface $scriptWitness, $flags, Checker $checker)
153
    {
154 53
        $witnessCount = count($scriptWitness);
155
156 53
        if ($witnessProgram->getVersion() === 0) {
157 51
            $buffer = $witnessProgram->getProgram();
158 51
            if ($buffer->getSize() === 32) {
159
                // Version 0 segregated witness program: SHA256(Script) in program, Script + inputs in witness
160 29
                if ($witnessCount === 0) {
161
                    // Must contain script at least
162 2
                    return false;
163
                }
164
165 27
                $scriptPubKey = new Script($scriptWitness[$witnessCount - 1]);
166 27
                $stackValues = $scriptWitness->slice(0, -1);
167 27
                $hashScriptPubKey = Hash::sha256($scriptPubKey->getBuffer());
168
169 27
                if (!$hashScriptPubKey->equals($buffer)) {
0 ignored issues
show
Documentation introduced by
$buffer is of type object<BitWasp\Buffertools\BufferInterface>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
170 24
                    return false;
171
                }
172 25
            } elseif ($buffer->getSize() === 20) {
173
                // Version 0 special case for pay-to-pubkeyhash
174 20
                if ($witnessCount !== 2) {
175
                    // 2 items in witness - <signature> <pubkey>
176 4
                    return false;
177
                }
178
179 18
                $scriptPubKey = ScriptFactory::scriptPubKey()->payToPubKeyHash($buffer);
180 18
                $stackValues = $scriptWitness;
181 2
            } else {
182 38
                return false;
183
            }
184 7
        } elseif ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
185 2
            return false;
186
        } else {
187
            return false;
188
        }
189
190 41
        $mainStack = new Stack();
191 41
        foreach ($stackValues as $value) {
192 39
            $mainStack->push($value);
193 5
        }
194
195 41
        if (!$this->evaluate($scriptPubKey, $mainStack, SigHash::V1, $flags, $checker)) {
196
            return false;
197
        }
198
199 41
        if ($mainStack->count() !== 1) {
200
            return false;
201
        }
202
203 41
        if (!$this->castToBool($mainStack->bottom())) {
204 18
            return false;
205
        }
206
207 23
        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 2374
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $flags, Checker $checker, ScriptWitnessInterface $witness = null)
219
    {
220 2374
        static $emptyWitness = null;
221 2374
        if ($emptyWitness === null) {
222 6
            $emptyWitness = new ScriptWitness([]);
223 2
        }
224
225 2374
        $witness = is_null($witness) ? $emptyWitness : $witness;
226
227 2374
        if (($flags & self::VERIFY_SIGPUSHONLY) !== 0 && !$scriptSig->isPushOnly()) {
228 4
            return false;
229
        }
230
231 2370
        $stack = new Stack();
232 2370
        if (!$this->evaluate($scriptSig, $stack, SigHash::V0, $flags, $checker)) {
233 116
            return false;
234
        }
235
236 2254
        $backup = [];
237 2254
        if ($flags & self::VERIFY_P2SH) {
238 1579
            foreach ($stack as $s) {
239 1278
                $backup[] = $s;
240 21
            }
241 21
        }
242
243 2254
        if (!$this->evaluate($scriptPubKey, $stack, SigHash::V0, $flags, $checker)) {
244 762
            return false;
245
        }
246
247 1492
        if ($stack->isEmpty()) {
248 26
            return false;
249
        }
250
251 1466
        if (false === $this->castToBool($stack[-1])) {
252 106
            return false;
253
        }
254
255 1360
        $program = null;
256 1360
        if ($flags & self::VERIFY_WITNESS) {
257 89
            if ($scriptPubKey->isWitness($program)) {
258
                /** @var WitnessProgram $program */
259 37
                if ($scriptSig->getBuffer()->getSize() !== 0) {
260 2
                    return false;
261
                }
262
263 35
                if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
264 22
                    return false;
265
                }
266
267 13
                $stack->resize(1);
268 3
            }
269 15
        }
270
271 1336
        if ($flags & self::VERIFY_P2SH && (new OutputClassifier())->isPayToScriptHash($scriptPubKey)) {
272 77
            if (!$scriptSig->isPushOnly()) {
273 12
                return false;
274
            }
275
276 65
            $stack = new Stack();
277 65
            foreach ($backup as $i) {
278 65
                $stack->push($i);
279 7
            }
280
281
            // Restore mainStack to how it was after evaluating scriptSig
282 65
            if ($stack->isEmpty()) {
283
                return false;
284
            }
285
286
            // Load redeemscript as the scriptPubKey
287 65
            $scriptPubKey = new Script($stack->bottom());
288 65
            $stack->pop();
289
290 65
            if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
291 14
                return false;
292
            }
293
294 51
            if ($stack->isEmpty()) {
295
                return false;
296
            }
297
298 51
            if (!$this->castToBool($stack->bottom())) {
299 2
                return false;
300
            }
301
302 49
            if ($flags & self::VERIFY_WITNESS) {
303 29
                if ($scriptPubKey->isWitness($program)) {
304
                    /** @var WitnessProgram $program */
305 20
                    if (!$scriptSig->equals(ScriptFactory::sequence([$scriptPubKey->getBuffer()]))) {
306 2
                        return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
307
                    }
308
309 18
                    if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
310 8
                        return false;
311
                    }
312
313 10
                    $stack->resize(1);
314 2
                }
315 5
            }
316 5
        }
317
318 1298
        if ($flags & self::VERIFY_CLEAN_STACK) {
319 24
            if (!($flags & self::VERIFY_P2SH !== 0) && ($flags & self::VERIFY_WITNESS !== 0)) {
320
                return false; // implied flags required
321
            }
322
323 24
            if (count($stack) !== 1) {
324 4
                return false; // Cleanstack
325
            }
326 6
        }
327
328 1294
        if ($flags & self::VERIFY_WITNESS) {
329 55
            if (!$flags & self::VERIFY_P2SH) {
330
                return false; //
331
            }
332
333 55
            if ($program === null && !$witness->isNull()) {
334 2
                return false; // SCRIPT_ERR_WITNESS_UNEXPECTED
335
            }
336 15
        }
337
338 1292
        return true;
339
    }
340
341
    /**
342
     * @param Stack $vfStack
343
     * @param bool $value
344
     * @return bool
345
     */
346 2374
    private function checkExec(Stack $vfStack, $value)
347
    {
348 2374
        $ret = 0;
349 2374
        foreach ($vfStack as $item) {
350 484
            if ($item === $value) {
351 484
                $ret++;
352
            }
353 90
        }
354
355 2374
        return $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 2400
    public function evaluate(ScriptInterface $script, Stack $mainStack, $sigVersion, $flags, Checker $checker)
367
    {
368 2400
        $hashStartPos = 0;
369 2400
        $opCount = 0;
370 2400
        $zero = gmp_init(0, 10);
371 2400
        $altStack = new Stack();
372 2400
        $vfStack = new Stack();
373 2400
        $minimal = ($flags & self::VERIFY_MINIMALDATA) !== 0;
374 2400
        $parser = $script->getScriptParser();
375
376 2400
        if ($script->getBuffer()->getSize() > 10000) {
377 2
            return false;
378
        }
379
380
        try {
381 2400
            foreach ($parser as $operation) {
382 2374
                $opCode = $operation->getOp();
383 2374
                $pushData = $operation->getData();
384 2374
                $fExec = !$this->checkExec($vfStack, false);
385
386
                // If pushdata was written to
387 2374
                if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
388 10
                    throw new \RuntimeException('Error - push size');
389
                }
390
391
                // OP_RESERVED should not count towards opCount
392 2368
                if ($opCode > Opcodes::OP_16 && ++$opCount) {
393 2246
                    $this->checkOpcodeCount($opCount);
394 86
                }
395
396 2368
                if (in_array($opCode, $this->disabledOps, true)) {
397 48
                    throw new \RuntimeException('Disabled Opcode');
398
                }
399
400 2368
                if ($fExec && $operation->isPush()) {
401
                    // In range of a pushdata opcode
402 1768
                    if ($minimal && !$this->checkMinimalPush($opCode, $pushData)) {
403 42
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
404
                    }
405
406 1726
                    $mainStack->push($pushData);
407
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
408 2294
                } elseif ($fExec || (Opcodes::OP_IF <= $opCode && $opCode <= Opcodes::OP_ENDIF)) {
409
                    // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n";
410
                    switch ($opCode) {
411 2292
                        case Opcodes::OP_1NEGATE:
412 2292
                        case Opcodes::OP_1:
413 2248
                        case Opcodes::OP_2:
414 2240
                        case Opcodes::OP_3:
415 2240
                        case Opcodes::OP_4:
416 2240
                        case Opcodes::OP_5:
417 2240
                        case Opcodes::OP_6:
418 2240
                        case Opcodes::OP_7:
419 2238
                        case Opcodes::OP_8:
420 2238
                        case Opcodes::OP_9:
421 2238
                        case Opcodes::OP_10:
422 2238
                        case Opcodes::OP_11:
423 2236
                        case Opcodes::OP_12:
424 2236
                        case Opcodes::OP_13:
425 2236
                        case Opcodes::OP_14:
426 2236
                        case Opcodes::OP_15:
427 2236
                        case Opcodes::OP_16:
428 1374
                            $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode);
429 1374
                            $mainStack->push(Number::int($num)->getBuffer());
430 1374
                            break;
431
432 2236
                        case Opcodes::OP_CHECKLOCKTIMEVERIFY:
433 12
                            if (!($flags & self::VERIFY_CHECKLOCKTIMEVERIFY)) {
434 12
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
435 2
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
436
                                }
437 10
                                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 2234
                        case Opcodes::OP_CHECKSEQUENCEVERIFY:
452 24
                            if (!($flags & self::VERIFY_CHECKSEQUENCEVERIFY)) {
453 12
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
454 2
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
455
                                }
456 10
                                break;
457
                            }
458
459 12
                            if ($mainStack->isEmpty()) {
460 2
                                throw new \RuntimeException('Invalid stack operation - CSV');
461
                            }
462
463 10
                            $sequence = Number::buffer($mainStack[-1], $minimal, 5, $this->math);
464 8
                            $nSequence = $sequence->getGmp();
465 8
                            if ($this->math->cmp($nSequence, $zero) < 0) {
466 2
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
467
                            }
468
469 6
                            if ($this->math->cmp($this->math->bitwiseAnd($nSequence, gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) {
470 2
                                break;
471
                            }
472
473 4
                            if (!$checker->checkSequence($sequence)) {
474 4
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied sequence');
475
                            }
476
                            break;
477
478 2220
                        case Opcodes::OP_NOP1:
479 2218
                        case Opcodes::OP_NOP4:
480 2216
                        case Opcodes::OP_NOP5:
481 2214
                        case Opcodes::OP_NOP6:
482 2212
                        case Opcodes::OP_NOP7:
483 2210
                        case Opcodes::OP_NOP8:
484 2208
                        case Opcodes::OP_NOP9:
485 2206
                        case Opcodes::OP_NOP10:
486 54
                            if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
487 20
                                throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
488
                            }
489 34
                            break;
490
491 2200
                        case Opcodes::OP_NOP:
492 168
                            break;
493
494 2154
                        case Opcodes::OP_IF:
495 2110
                        case Opcodes::OP_NOTIF:
496
                            // <expression> if [statements] [else [statements]] endif
497 494
                            $value = false;
498 494
                            if ($fExec) {
499 494
                                if ($mainStack->isEmpty()) {
500 4
                                    throw new \RuntimeException('Unbalanced conditional');
501
                                }
502
503 490
                                $buffer = Number::buffer($mainStack->pop(), $minimal)->getBuffer();
504 490
                                $value = $this->castToBool($buffer);
505 490
                                if ($opCode === Opcodes::OP_NOTIF) {
506 34
                                    $value = !$value;
507
                                }
508
                            }
509 490
                            $vfStack->push($value);
510 490
                            break;
511
512 2106
                        case Opcodes::OP_ELSE:
513 230
                            if ($vfStack->isEmpty()) {
514 8
                                throw new \RuntimeException('Unbalanced conditional');
515
                            }
516 226
                            $vfStack->push(!$vfStack->pop());
517 226
                            break;
518
519 2100
                        case Opcodes::OP_ENDIF:
520 294
                            if ($vfStack->isEmpty()) {
521 14
                                throw new \RuntimeException('Unbalanced conditional');
522
                            }
523 286
                            $vfStack->pop();
524 286
                            break;
525
526 1828
                        case Opcodes::OP_VERIFY:
527 60
                            if ($mainStack->isEmpty()) {
528 2
                                throw new \RuntimeException('Invalid stack operation');
529
                            }
530 58
                            $value = $this->castToBool($mainStack[-1]);
531 58
                            if (!$value) {
532 2
                                throw new \RuntimeException('Error: verify');
533
                            }
534 56
                            $mainStack->pop();
535 56
                            break;
536
537 1814
                        case Opcodes::OP_TOALTSTACK:
538 16
                            if ($mainStack->isEmpty()) {
539 2
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
540
                            }
541 14
                            $altStack->push($mainStack->pop());
542 14
                            break;
543
544 1810
                        case Opcodes::OP_FROMALTSTACK:
545 10
                            if ($altStack->isEmpty()) {
546 4
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
547
                            }
548 6
                            $mainStack->push($altStack->pop());
549 6
                            break;
550
551 1804
                        case Opcodes::OP_IFDUP:
552
                            // If top value not zero, duplicate it.
553 12
                            if ($mainStack->isEmpty()) {
554 4
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
555
                            }
556 8
                            $vch = $mainStack[-1];
557 8
                            if ($this->castToBool($vch)) {
558 6
                                $mainStack->push($vch);
559
                            }
560 8
                            break;
561
562 1798
                        case Opcodes::OP_DEPTH:
563 156
                            $num = count($mainStack);
564 156
                            $depth = Number::int($num)->getBuffer();
565 156
                            $mainStack->push($depth);
566 156
                            break;
567
568 1780
                        case Opcodes::OP_DROP:
569 96
                            if ($mainStack->isEmpty()) {
570 4
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
571
                            }
572 92
                            $mainStack->pop();
573 92
                            break;
574
575 1774
                        case Opcodes::OP_DUP:
576 86
                            if ($mainStack->isEmpty()) {
577 4
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
578
                            }
579 82
                            $vch = $mainStack[-1];
580 82
                            $mainStack->push($vch);
581 82
                            break;
582
583 1756
                        case Opcodes::OP_NIP:
584 12
                            if (count($mainStack) < 2) {
585 6
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
586
                            }
587 6
                            unset($mainStack[-2]);
588 6
                            break;
589
590 1744
                        case Opcodes::OP_OVER:
591 12
                            if (count($mainStack) < 2) {
592 6
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
593
                            }
594 6
                            $vch = $mainStack[-2];
595 6
                            $mainStack->push($vch);
596 6
                            break;
597
598 1736
                        case Opcodes::OP_ROT:
599 24
                            if (count($mainStack) < 3) {
600 8
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
601
                            }
602 16
                            $mainStack->swap(-3, -2);
603 16
                            $mainStack->swap(-2, -1);
604 16
                            break;
605
606 1724
                        case Opcodes::OP_SWAP:
607 20
                            if (count($mainStack) < 2) {
608 6
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
609
                            }
610 14
                            $mainStack->swap(-2, -1);
611 14
                            break;
612
613 1716
                        case Opcodes::OP_TUCK:
614 12
                            if (count($mainStack) < 2) {
615 6
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
616
                            }
617 6
                            $vch = $mainStack[-1];
618 6
                            $mainStack->add(- 2, $vch);
619 6
                            break;
620
621 1708
                        case Opcodes::OP_PICK:
622 1690
                        case Opcodes::OP_ROLL:
623 58
                            if (count($mainStack) < 2) {
624 8
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
625
                            }
626
627 50
                            $n = Number::buffer($mainStack[-1], $minimal, 4)->getGmp();
628 46
                            $mainStack->pop();
629 46
                            if ($this->math->cmp($n, $zero) < 0 || $this->math->cmp($n, gmp_init(count($mainStack))) >= 0) {
630 10
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
631
                            }
632
633 36
                            $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), gmp_init(1)), 10);
634 36
                            $vch = $mainStack[$pos];
635 36
                            if ($opCode === Opcodes::OP_ROLL) {
636 18
                                unset($mainStack[$pos]);
637
                            }
638 36
                            $mainStack->push($vch);
639 36
                            break;
640
641 1674
                        case Opcodes::OP_2DROP:
642 18
                            if (count($mainStack) < 2) {
643 2
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
644
                            }
645 16
                            $mainStack->pop();
646 16
                            $mainStack->pop();
647 16
                            break;
648
649 1670
                        case Opcodes::OP_2DUP:
650 12
                            if (count($mainStack) < 2) {
651 6
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
652
                            }
653 6
                            $string1 = $mainStack[-2];
654 6
                            $string2 = $mainStack[-1];
655 6
                            $mainStack->push($string1);
656 6
                            $mainStack->push($string2);
657 6
                            break;
658
659 1662
                        case Opcodes::OP_3DUP:
660 22
                            if (count($mainStack) < 3) {
661 8
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
662
                            }
663 14
                            $string1 = $mainStack[-3];
664 14
                            $string2 = $mainStack[-2];
665 14
                            $string3 = $mainStack[-1];
666 14
                            $mainStack->push($string1);
667 14
                            $mainStack->push($string2);
668 14
                            $mainStack->push($string3);
669 14
                            break;
670
671 1642
                        case Opcodes::OP_2OVER:
672 10
                            if (count($mainStack) < 4) {
673 6
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
674
                            }
675 4
                            $string1 = $mainStack[-4];
676 4
                            $string2 = $mainStack[-3];
677 4
                            $mainStack->push($string1);
678 4
                            $mainStack->push($string2);
679 4
                            break;
680
681 1634
                        case Opcodes::OP_2ROT:
682 20
                            if (count($mainStack) < 6) {
683 2
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
684
                            }
685 18
                            $string1 = $mainStack[-6];
686 18
                            $string2 = $mainStack[-5];
687 18
                            unset($mainStack[-6], $mainStack[-5]);
688 18
                            $mainStack->push($string1);
689 18
                            $mainStack->push($string2);
690 18
                            break;
691
692 1630
                        case Opcodes::OP_2SWAP:
693 10
                            if (count($mainStack) < 4) {
694 6
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
695
                            }
696 4
                            $mainStack->swap(-3, -1);
697 4
                            $mainStack->swap(-4, -2);
698 4
                            break;
699
700 1622
                        case Opcodes::OP_SIZE:
701 62
                            if ($mainStack->isEmpty()) {
702 4
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
703
                            }
704 58
                            $size = Number::int($mainStack[-1]->getSize());
705 58
                            $mainStack->push($size->getBuffer());
706 58
                            break;
707
708 1616
                        case Opcodes::OP_EQUAL:
709 1394
                        case Opcodes::OP_EQUALVERIFY:
710 616
                            if (count($mainStack) < 2) {
711 8
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
712
                            }
713
714 608
                            $equal = $mainStack[-2]->equals($mainStack[-1]);
715 608
                            $mainStack->pop();
716 608
                            $mainStack->pop();
717 608
                            $mainStack->push($equal ? $this->vchTrue : $this->vchFalse);
718 608
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
719 120
                                if ($equal) {
720 102
                                    $mainStack->pop();
721 10
                                } else {
722 18
                                    throw new \RuntimeException('Error EQUALVERIFY');
723
                                }
724 10
                            }
725
726 592
                            break;
727
728
                        // Arithmetic operations
729 1332
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
730 216
                            if ($mainStack->isEmpty()) {
731 12
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
732
                            }
733
734 204
                            $num = Number::buffer($mainStack[-1], $minimal)->getGmp();
735
736 158
                            if ($opCode === Opcodes::OP_1ADD) {
737 14
                                $num = $this->math->add($num, gmp_init(1));
738 144
                            } elseif ($opCode === Opcodes::OP_1SUB) {
739 6
                                $num = $this->math->sub($num, gmp_init(1));
740 138
                            } elseif ($opCode === Opcodes::OP_2MUL) {
741
                                $num = $this->math->mul(gmp_init(2), $num);
742 138
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
743 8
                                $num = $this->math->sub($zero, $num);
744 132
                            } elseif ($opCode === Opcodes::OP_ABS) {
745 10
                                if ($this->math->cmp($num, $zero) < 0) {
746 10
                                    $num = $this->math->sub($zero, $num);
747
                                }
748 122
                            } elseif ($opCode === Opcodes::OP_NOT) {
749 110
                                $num = gmp_init($this->math->cmp($num, $zero) === 0 ? 1 : 0);
750 4
                            } else {
751
                                // is OP_0NOTEQUAL
752 12
                                $num = gmp_init($this->math->cmp($num, $zero) !== 0 ? 1 : 0);
753
                            }
754
755 158
                            $mainStack->pop();
756
757 158
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
758
759 158
                            $mainStack->push($buffer);
760 158
                            break;
761
762 1214
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
763 328
                            if (count($mainStack) < 2) {
764 26
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
765
                            }
766
767 302
                            $num1 = Number::buffer($mainStack[-2], $minimal)->getGmp();
768 268
                            $num2 = Number::buffer($mainStack[-1], $minimal)->getGmp();
769
770 242
                            if ($opCode === Opcodes::OP_ADD) {
771 60
                                $num = $this->math->add($num1, $num2);
772 194
                            } else if ($opCode === Opcodes::OP_SUB) {
773 12
                                $num = $this->math->sub($num1, $num2);
774 182
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
775 16
                                $num = $this->math->cmp($num1, $zero) !== 0 && $this->math->cmp($num2, $zero) !== 0;
776 166
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
777 16
                                $num = $this->math->cmp($num1, $zero) !== 0 || $this->math->cmp($num2, $zero) !== 0;
778 150
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
779 56
                                $num = $this->math->cmp($num1, $num2) === 0;
780 110
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
781 8
                                $num = $this->math->cmp($num1, $num2) === 0;
782 102
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
783 10
                                $num = $this->math->cmp($num1, $num2) !== 0;
784 92
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) {
785 16
                                $num = $this->math->cmp($num1, $num2) < 0;
786 76
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
787 16
                                $num = $this->math->cmp($num1, $num2) > 0;
788 60
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
789 16
                                $num = $this->math->cmp($num1, $num2) <= 0;
790 44
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
791 16
                                $num = $this->math->cmp($num1, $num2) >= 0;
792 28
                            } elseif ($opCode === Opcodes::OP_MIN) {
793 14
                                $num = ($this->math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
794
                            } else {
795 14
                                $num = ($this->math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
796
                            }
797
798 242
                            $mainStack->pop();
799 242
                            $mainStack->pop();
800 242
                            $buffer = Number::int(gmp_strval($num, 10))->getBuffer();
801 242
                            $mainStack->push($buffer);
802
803 242
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
804 8
                                if ($this->castToBool($mainStack[-1])) {
805 8
                                    $mainStack->pop();
806
                                } else {
807
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
808
                                }
809
                            }
810 242
                            break;
811
812 886
                        case Opcodes::OP_WITHIN:
813 30
                            if (count($mainStack) < 3) {
814 2
                                throw new \RuntimeException('Invalid stack operation');
815
                            }
816
817 28
                            $num1 = Number::buffer($mainStack[-3], $minimal)->getGmp();
818 26
                            $num2 = Number::buffer($mainStack[-2], $minimal)->getGmp();
819 24
                            $num3 = Number::buffer($mainStack[-1], $minimal)->getGmp();
820
821 22
                            $value = $this->math->cmp($num2, $num1) <= 0 && $this->math->cmp($num1, $num3) < 0;
822 22
                            $mainStack->pop();
823 22
                            $mainStack->pop();
824 22
                            $mainStack->pop();
825 22
                            $mainStack->push($value ? $this->vchTrue : $this->vchFalse);
826 22
                            break;
827
828
                        // Hash operation
829 856
                        case Opcodes::OP_RIPEMD160:
830 844
                        case Opcodes::OP_SHA1:
831 828
                        case Opcodes::OP_SHA256:
832 816
                        case Opcodes::OP_HASH160:
833 766
                        case Opcodes::OP_HASH256:
834 200
                            if ($mainStack->isEmpty()) {
835 26
                                throw new \RuntimeException('Invalid stack operation');
836
                            }
837
838 174
                            $buffer = $mainStack[-1];
839 174
                            if ($opCode === Opcodes::OP_RIPEMD160) {
840 10
                                $hash = Hash::ripemd160($buffer);
841 166
                            } elseif ($opCode === Opcodes::OP_SHA1) {
842 12
                                $hash = Hash::sha1($buffer);
843 154
                            } elseif ($opCode === Opcodes::OP_SHA256) {
844 12
                                $hash = Hash::sha256($buffer);
845 146
                            } elseif ($opCode === Opcodes::OP_HASH160) {
846 136
                                $hash = Hash::sha256ripe160($buffer);
847 20
                            } else {
848 10
                                $hash = Hash::sha256d($buffer);
849
                            }
850
851 174
                            $mainStack->pop();
852 174
                            $mainStack->push($hash);
853 174
                            break;
854
855 748
                        case Opcodes::OP_CODESEPARATOR:
856 2
                            $hashStartPos = $parser->getPosition();
857 2
                            break;
858
859 746
                        case Opcodes::OP_CHECKSIG:
860 516
                        case Opcodes::OP_CHECKSIGVERIFY:
861 284
                            if (count($mainStack) < 2) {
862 4
                                throw new \RuntimeException('Invalid stack operation');
863
                            }
864
865 280
                            $vchPubKey = $mainStack[-1];
866 280
                            $vchSig = $mainStack[-2];
867
868 280
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
869
870 280
                            $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags);
871
872 160
                            $mainStack->pop();
873 160
                            $mainStack->pop();
874 160
                            $mainStack->push($success ? $this->vchTrue : $this->vchFalse);
875
876 160
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
877
                                if ($success) {
878
                                    $mainStack->pop();
879
                                } else {
880
                                    throw new \RuntimeException('Checksig verify');
881
                                }
882
                            }
883 160
                            break;
884
885 462
                        case Opcodes::OP_CHECKMULTISIG:
886 286
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
887 262
                            $i = 1;
888 262
                            if (count($mainStack) < $i) {
889 2
                                throw new \RuntimeException('Invalid stack operation');
890
                            }
891
892 260
                            $keyCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
893 256
                            if ($keyCount < 0 || $keyCount > 20) {
894 4
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
895
                            }
896
897 252
                            $opCount += $keyCount;
898 252
                            $this->checkOpcodeCount($opCount);
899
900
                            // Extract positions of the keys, and signatures, from the stack.
901 252
                            $ikey = ++$i;
902 252
                            $i += $keyCount;
903 252
                            if (count($mainStack) < $i) {
904 2
                                throw new \RuntimeException('Invalid stack operation');
905
                            }
906
907 250
                            $sigCount = Number::buffer($mainStack[-$i], $minimal)->getInt();
908 244
                            if ($sigCount < 0 || $sigCount > $keyCount) {
909 4
                                throw new \RuntimeException('Invalid Signature count');
910
                            }
911
912 240
                            $isig = ++$i;
913 240
                            $i += $sigCount;
914
915
                            // Extract the script since the last OP_CODESEPARATOR
916 240
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
917
918 240
                            $fSuccess = true;
919 240
                            while ($fSuccess && $sigCount > 0) {
920
                                // Fetch the signature and public key
921 120
                                $sig = $mainStack[-$isig];
922 118
                                $pubkey = $mainStack[-$ikey];
923
924 118
                                if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) {
925 66
                                    $isig++;
926 66
                                    $sigCount--;
927 14
                                }
928
929 98
                                $ikey++;
930 98
                                $keyCount--;
931
932
                                // If there are more signatures left than keys left,
933
                                // then too many signatures have failed. Exit early,
934
                                // without checking any further signatures.
935 98
                                if ($sigCount > $keyCount) {
936 36
                                    $fSuccess = false;
937 4
                                }
938 18
                            }
939
940 206
                            while ($i-- > 1) {
941 206
                                $mainStack->pop();
942 14
                            }
943
944
                            // A bug causes CHECKMULTISIG to consume one extra argument
945
                            // whose contents were not checked in any way.
946
                            //
947
                            // Unfortunately this is a potential source of mutability,
948
                            // so optionally verify it is exactly equal to zero prior
949
                            // to removing it from the stack.
950 206
                            if ($mainStack->isEmpty()) {
951 2
                                throw new \RuntimeException('Invalid stack operation');
952
                            }
953
954 206
                            if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) {
955 4
                                throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
956
                            }
957
958 202
                            $mainStack->pop();
959 202
                            $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
960
961 202
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
962 60
                                if ($fSuccess) {
963 60
                                    $mainStack->pop();
964
                                } else {
965
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
966
                                }
967
                            }
968 202
                            break;
969
970 6
                        default:
971 200
                            throw new \RuntimeException('Opcode not found');
972 6
                    }
973
974 2010
                    if (count($mainStack) + count($altStack) > 1000) {
975 2212
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
976
                    }
977 48
                }
978 94
            }
979
980 2294
            if (count($vfStack) !== 0) {
981 12
                throw new \RuntimeException('Unbalanced conditional at script end');
982
            }
983
984 2284
            return true;
985 896
        } catch (ScriptRuntimeException $e) {
986
            // echo "\n Runtime: " . $e->getMessage() . "\n" . $e->getTraceAsString() . PHP_EOL;
987
            // Failure due to script tags, can access flag: $e->getFailureFlag()
988 228
            return false;
989 668
        } catch (\Exception $e) {
990
            // echo "\n General: " . $e->getMessage()  . PHP_EOL . $e->getTraceAsString() . PHP_EOL;
991 668
            return false;
992
        }
993
    }
994
}
995