Completed
Pull Request — master (#221)
by thomas
51:04 queued 47:55
created

Interpreter::verify()   C

Complexity

Conditions 11
Paths 17

Size

Total Lines 46
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 12.4876

Importance

Changes 8
Bugs 1 Features 1
Metric Value
c 8
b 1
f 1
dl 0
loc 46
ccs 20
cts 26
cp 0.7692
rs 5.2653
cc 11
eloc 24
nc 17
nop 3
crap 12.4876

How to fix   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 750
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction, Flags $flags)
103
    {
104 750
        $this->ecAdapter = $ecAdapter;
105 750
        $this->math = $ecAdapter->getMath();
106 750
        $this->transaction = $transaction;
107 750
        $this->flags = $flags;
108 750
        $this->script = new Script();
109 750
        $this->minimalPush = $this->flags->checkFlags(self::VERIFY_MINIMALDATA) === true;
110 750
        $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 750
        $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 750
        $this->mainStack = new Stack();
113 750
        $this->altStack = new Stack();
114 750
        $this->vfStack = new Stack();
115
116 750
        $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 750
        $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 750
    }
119
120
    /**
121
     * @param ScriptInterface $script
122
     * @return $this
123
     */
124 678
    public function setScript(ScriptInterface $script)
125
    {
126 678
        $this->script = $script;
127 678
        return $this;
128
    }
129
130
    /**
131
     * Cast the value to a boolean
132
     *
133
     * @param $value
134
     * @return bool
135
     */
136 426
    public function castToBool(Buffer $value)
137
    {
138 426
        if ($value->getSize() === 0) {
139 69
            return true;
140
        }
141
142
        // Since we're using buffers, lets try ensuring the contents are not 0.
143 357
        return $this->math->cmp($value->getInt(), 0) > 0;
144
    }
145
146
    /**
147
     * @param Buffer $signature
148
     * @return bool
149
     */
150 51
    public function isValidSignatureEncoding(Buffer $signature)
151
    {
152
        try {
153 51
            TransactionSignature::isDERSignature($signature);
154 33
            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 27
    public function isLowDerSignature(Buffer $signature)
169
    {
170 27
        if (!$this->isValidSignatureEncoding($signature)) {
171 6
            throw new ScriptRuntimeException(self::VERIFY_DERSIG, 'Signature with incorrect encoding');
172
        }
173
174 21
        $binary = $signature->getBinary();
175 21
        $nLenR = ord($binary[3]);
176 21
        $nLenS = ord($binary[5 + $nLenR]);
177 21
        $s = $signature->slice(6 + $nLenR, $nLenS)->getInt();
178
179 21
        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 33
    public function isDefinedHashtypeSignature(Buffer $signature)
190
    {
191 33
        if ($signature->getSize() === 0) {
192 6
            return false;
193
        }
194
195 27
        $binary = $signature->getBinary();
196 27
        $nHashType = ord(substr($binary, -1)) & (~(SignatureHashInterface::SIGHASH_ANYONECANPAY));
197
198 27
        $math = $this->math;
199 27
        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 57
    public function checkSignatureEncoding(Buffer $signature)
208
    {
209 57
        if ($signature->getSize() === 0) {
210 6
            return $this;
211
        }
212
213 51
        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 39
        } 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 33
        } 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 27
        return $this;
222
    }
223
224
    /**
225
     * @param Buffer $publicKey
226
     * @return $this
227
     * @throws \Exception
228
     */
229 21
    public function checkPublicKeyEncoding(Buffer $publicKey)
230
    {
231 21
        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 15
        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 666
    private function checkOpcodeCount()
274
    {
275 666
        if ($this->math->cmp($this->opCount, 201) > 0) {
276 6
            throw new \RuntimeException('Error: Script op code count');
277
        }
278
279 666
        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 15
    private function checkSig(ScriptInterface $script, Buffer $sigBuf, Buffer $keyBuf)
291
    {
292 15
        $this
293 15
            ->checkSignatureEncoding($sigBuf)
294 9
            ->checkPublicKeyEncoding($keyBuf);
295
296
        try {
297 9
            $txSignature = TransactionSignatureFactory::fromHex($sigBuf->getHex());
298 9
            $publicKey = PublicKeyFactory::fromHex($keyBuf->getHex());
299
300 6
            return $this->ecAdapter->verify(
301 6
                $this
302
                    ->transaction
303 6
                    ->getSignatureHash()
304 6
                    ->calculate($script, $this->inputToSign, $txSignature->getHashType()),
305 6
                $publicKey,
306 6
                $txSignature->getSignature()
307 6
            );
308 3
        } catch (\Exception $e) {
309 3
            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 678
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $nInputToSign)
375
    {
376 678
        $this->inputToSign = $nInputToSign;
377 678
        if (!$this->setScript($scriptSig)->run()) {
378
            return false;
379
        }
380
381 678
        $stackCopy = new Stack;
382 678
        if ($this->flags->checkFlags(self::VERIFY_P2SH)) {
383 150
            $stackCopy = clone $this->mainStack;
384 150
        }
385
386 678
        if (!$this->setScript($scriptPubKey)->run()) {
387 258
            return false;
388
        }
389
390 420
        if ($this->mainStack->isEmpty()) {
391
            return false;
392
        }
393
394 420
        if (false === $this->castToBool($this->mainStack[-1])) {
395
            return false;
396
        }
397
398 420
        if ($this->flags->checkFlags(self::VERIFY_P2SH) && (new OutputClassifier($scriptPubKey))->isPayToScriptHash()) {
399 3
            if (!$scriptSig->isPushOnly()) {
400
                return false;
401
            }
402
403
            // Restore mainStack to how it was after evaluating scriptSig
404 3
            $this->mainStack = $this->mainStack = $stackCopy;
405 3
            if ($this->mainStack->isEmpty()) {
406
                return false;
407
            }
408
409
            // Load redeemscript as the scriptPubKey
410 3
            $scriptPubKey = new Script($this->mainStack->bottom());
411 3
            $this->mainStack->pop();
412
413 3
            if (!$this->setScript($scriptPubKey)->run()) {
414
                return false;
415
            }
416 3
        }
417
418 420
        return true;
419
    }
420
421
    /**
422
     * @return bool
423
     */
424 678
    private function checkExec()
425
    {
426 678
        $c = 0;
427 678
        $len = $this->vfStack->end();
428 678
        for ($i = 0; $i < $len; $i++) {
429
            if ($this->vfStack[0 - $len - $i] === true) {
430
                $c++;
431
            }
432
        }
433 678
        return !(bool)$c;
434
    }
435
436
    /**
437
     * @return bool
438
     */
439 678
    private function run()
440
    {
441 678
        $math = $this->math;
442 678
        $this->hashStartPos = 0;
443 678
        $this->opCount = 0;
444 678
        $parser = $this->script->getScriptParser();
445
446 678
        if ($this->script->getBuffer()->getSize() > 10000) {
447
            return false;
448
        }
449
450
        try {
451 678
            foreach ($parser as $operation) {
452 678
                $opCode = $operation->getOp();
453 678
                $pushData = $operation->getData();
454 678
                $fExec = $this->checkExec();
455
456
                // If pushdata was written to,
457 678
                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 678
                if ($opCode > Opcodes::OP_16 && ++$this->opCount) {
463 666
                    $this->checkOpcodeCount();
464 666
                }
465
466 678
                if (in_array($opCode, $this->disabledOps, true)) {
467 12
                    throw new \RuntimeException('Disabled Opcode');
468
                }
469
470 678
                if ($fExec && $operation->isPush()) {
471
                    // In range of a pushdata opcode
472 420
                    if ($this->minimalPush && !$this->checkMinimalPush($opCode, $pushData)) {
473 6
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
474
                    }
475
476 414
                    $this->mainStack->push($pushData);
477
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
478 672
                } elseif ($fExec || ($opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF)) {
479
                    // echo "OPCODE - " . $this->script->getOpCodes()->getOp($opCode) . "\n";
480
                    switch ($opCode) {
481 654
                        case Opcodes::OP_1NEGATE:
482 654
                        case Opcodes::OP_1:
483 654
                        case Opcodes::OP_2:
484 654
                        case Opcodes::OP_3:
485 654
                        case Opcodes::OP_4:
486 654
                        case Opcodes::OP_5:
487 654
                        case Opcodes::OP_6:
488 654
                        case Opcodes::OP_7:
489 654
                        case Opcodes::OP_8:
490 654
                        case Opcodes::OP_9:
491 654
                        case Opcodes::OP_10:
492 654
                        case Opcodes::OP_11:
493 654
                        case Opcodes::OP_12:
494 654
                        case Opcodes::OP_13:
495 654
                        case Opcodes::OP_14:
496 654
                        case Opcodes::OP_15:
497 654
                        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 654
                        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 654
                        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 654
                        case Opcodes::OP_NOP1:
549 654
                        case Opcodes::OP_NOP4:
550 654
                        case Opcodes::OP_NOP5:
551 654
                        case Opcodes::OP_NOP6:
552 654
                        case Opcodes::OP_NOP7:
553 654
                        case Opcodes::OP_NOP8:
554 654
                        case Opcodes::OP_NOP9:
555 654
                        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 648
                        case Opcodes::OP_NOP:
562 12
                            break;
563
564 636
                        case Opcodes::OP_IF:
565 636
                        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 630
                        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 624
                        case Opcodes::OP_ENDIF:
590 18
                            if ($this->vfStack->isEmpty()) {
591 6
                                throw new \RuntimeException('Unbalanced conditional');
592
                            }
593 12
                            break;
594
595 618
                        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 594
                        case Opcodes::OP_RESERVED:
607
                            // todo
608 12
                            break;
609
610 582
                        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 576
                        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 570
                        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 564
                        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 564
                        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 558
                        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 552
                        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 546
                        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 540
                        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 534
                        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 528
                        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 516
                        case Opcodes::OP_PICK:
700 516
                        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 504
                        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 498
                        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 492
                        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 486
                        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 480
                        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 474
                        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 468
                        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 462
                        case Opcodes::OP_EQUAL:
788 462
                        case Opcodes::OP_EQUALVERIFY:
789 393
                            if (count($this->mainStack) < 2) {
790 6
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
791
                            }
792 387
                            $vch1 = $this->mainStack[-2];
793 387
                            $vch2 = $this->mainStack[-1];
794
795 387
                            $equal = ($vch1->getBinary() === $vch2->getBinary());
796
797 387
                            $this->mainStack->pop();
798 387
                            $this->mainStack->pop();
799 387
                            $this->mainStack->push(($equal ? $this->vchTrue : $this->vchFalse));
800 387
                            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 375
                            break;
809
810
                        // Arithmetic operations
811 276
                        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 228
                        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 78
                        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 66
                        case Opcodes::OP_RIPEMD160:
912 66
                        case Opcodes::OP_SHA1:
913 66
                        case Opcodes::OP_SHA256:
914 66
                        case Opcodes::OP_HASH160:
915 66
                        case Opcodes::OP_HASH256:
916 45
                            if ($this->mainStack->isEmpty()) {
917 6
                                throw new \RuntimeException('Invalid stack operation');
918
                            }
919
920 39
                            $buffer = $this->mainStack[-1];
921 39
                            if ($opCode === Opcodes::OP_RIPEMD160) {
922 6
                                $hash = Hash::ripemd160($buffer);
923 39
                            } elseif ($opCode === Opcodes::OP_SHA1) {
924 6
                                $hash = Hash::sha1($buffer);
925 33
                            } elseif ($opCode === Opcodes::OP_SHA256) {
926 6
                                $hash = Hash::sha256($buffer);
927 27
                            } elseif ($opCode === Opcodes::OP_HASH160) {
928 15
                                $hash = Hash::sha256ripe160($buffer);
929 15
                            } else {
930 6
                                $hash = Hash::sha256d($buffer);
931
                            }
932
933 39
                            $this->mainStack->pop();
934 39
                            $this->mainStack->push($hash);
935 39
                            break;
936
937 27
                        case Opcodes::OP_CODESEPARATOR:
938
                            $this->hashStartPos = $parser->getPosition();
939
                            break;
940
941 27
                        case Opcodes::OP_CHECKSIG:
942 27
                        case Opcodes::OP_CHECKSIGVERIFY:
943 21
                            if (count($this->mainStack) < 2) {
944 6
                                throw new \RuntimeException('Invalid stack operation');
945
                            }
946
947 15
                            $vchPubKey = $this->mainStack[-1];
948 15
                            $vchSig = $this->mainStack[-2];
949
950 15
                            $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
951 15
                            $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
952
953 9
                            $this->mainStack->pop();
954 9
                            $this->mainStack->pop();
955 9
                            $this->mainStack->push($success ? $this->vchTrue : $this->vchFalse);
956
957 9
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
958
                                if ($success) {
959
                                    $this->mainStack->pop();
960
                                } else {
961
                                    throw new \RuntimeException('Checksig verify');
962
                                }
963
                            }
964 9
                            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 420
                    if (count($this->mainStack) + count($this->altStack) > 1000) {
1060
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
1061
                    }
1062 420
                }
1063 678
            }
1064
1065 678
            if (!$this->vfStack->end() === 0) {
1066
                throw new \RuntimeException('Unbalanced conditional at script end');
1067
            }
1068
1069 678
            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