Completed
Pull Request — master (#226)
by thomas
118:20 queued 115:01
created

Interpreter   F

Complexity

Total Complexity 251

Size/Duplication

Total Lines 1058
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 21

Test Coverage

Coverage 75.38%

Importance

Changes 26
Bugs 3 Features 6
Metric Value
wmc 251
c 26
b 3
f 6
lcom 1
cbo 21
dl 0
loc 1058
ccs 450
cts 597
cp 0.7538
rs 0.8968

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
A castToBool() 0 9 2
A isValidSignatureEncoding() 0 11 2
A isLowDerSignature() 0 13 2
A isDefinedHashtypeSignature() 0 12 3
B checkSignatureEncoding() 0 16 8
A checkPublicKeyEncoding() 0 8 3
C checkMinimalPush() 0 24 9
A checkOpcodeCount() 0 8 2
A checkSig() 0 19 2
B verifyLockTime() 0 11 5
A checkLockTime() 0 8 2
A checkSequence() 0 18 3
C verify() 0 46 11
A checkExec() 0 11 3
F evaluate() 0 640 193

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