Completed
Push — master ( ef7efa...b4d73d )
by thomas
24:48
created

Interpreter::run()   F

Complexity

Conditions 193
Paths 5050

Size

Total Lines 640
Code Lines 437

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 351
CRAP Score 608.86

Importance

Changes 20
Bugs 2 Features 6
Metric Value
c 20
b 2
f 6
dl 0
loc 640
ccs 351
cts 452
cp 0.7765
rs 2
cc 193
eloc 437
nc 5050
nop 0
crap 608.86

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
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 ScriptInterface
33
     */
34
    private $script;
35
36
    /**
37
     * @var TransactionInterface
38
     */
39
    private $transaction;
40
41
    /**
42
     * @var StackInterface
43
     */
44
    private $mainStack;
45
46
    /**
47
     * @var StackInterface
48
     */
49
    private $altStack;
50
51
    /**
52
     * @var StackInterface
53
     */
54
    private $vfStack;
55
56
    /**
57
     * Position of OP_CODESEPARATOR, for calculating SigHash
58
     * @var int
59
     */
60
    private $hashStartPos;
61
62
    /**
63
     * @var int
64
     */
65
    private $opCount;
66
67
    /**
68
     * @var \BitWasp\Bitcoin\Flags
69
     */
70
    private $flags;
71
72
    /**
73
     * @var EcAdapterInterface
74
     */
75
    private $ecAdapter;
76
77
    /**
78
     * @var \BitWasp\Bitcoin\Math\Math
79
     */
80
    private $math;
81
82
    /**
83
     * @var bool
84
     */
85
    private $minimalPush;
86
87
    /**
88
     * @var array
89
     */
90
    private $disabledOps = [
91
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
92
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
93
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
94
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
95
    ];
96
97
    /**
98
     * @param EcAdapterInterface $ecAdapter
99
     * @param TransactionInterface $transaction
100
     * @param \BitWasp\Bitcoin\Flags $flags
101
     */
102 756
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction, Flags $flags)
103
    {
104 756
        $this->ecAdapter = $ecAdapter;
105 756
        $this->math = $ecAdapter->getMath();
106 756
        $this->transaction = $transaction;
107 756
        $this->flags = $flags;
108 756
        $this->script = new Script();
109 756
        $this->minimalPush = $this->flags->checkFlags(self::VERIFY_MINIMALDATA) === true;
110 756
        $this->vchFalse = new Buffer("", 0, $this->math);
0 ignored issues
show
Bug introduced by
The property vchFalse does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
111 756
        $this->vchTrue = new Buffer("\x01", 1, $this->math);
0 ignored issues
show
Bug introduced by
The property vchTrue does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
112 756
        $this->mainStack = new Stack();
113 756
        $this->altStack = new Stack();
114 756
        $this->vfStack = new Stack();
115
116 756
        $this->int0 = Number::buffer($this->vchFalse, false, 4, $this->math)->getBuffer();
0 ignored issues
show
Bug introduced by
The property int0 does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
117 756
        $this->int1 = Number::buffer($this->vchTrue, false, 1, $this->math)->getBuffer();
0 ignored issues
show
Bug introduced by
The property int1 does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

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