Completed
Pull Request — master (#195)
by thomas
26:54 queued 01:40
created

Interpreter::run()   F

Complexity

Conditions 175
Paths 4676

Size

Total Lines 592
Code Lines 406

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 345
CRAP Score 355.0975

Importance

Changes 17
Bugs 3 Features 3
Metric Value
c 17
b 3
f 3
dl 0
loc 592
ccs 345
cts 421
cp 0.8195
rs 2
cc 175
eloc 406
nc 4676
nop 0
crap 355.0975

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\Script\Classifier\OutputClassifier;
13
use BitWasp\Bitcoin\Script\Opcodes;
14
use BitWasp\Bitcoin\Script\Script;
15
use BitWasp\Bitcoin\Script\ScriptInterface;
16
use BitWasp\Bitcoin\Signature\TransactionSignature;
17
use BitWasp\Bitcoin\Signature\TransactionSignatureFactory;
18
use BitWasp\Bitcoin\Transaction\SignatureHash\SignatureHashInterface;
19
use BitWasp\Bitcoin\Transaction\TransactionInterface;
20
use BitWasp\Buffertools\Buffer;
21
22
class Interpreter implements InterpreterInterface
23
{
24
    /**
25
     * @var int|string
26
     */
27
    private $inputToSign;
28
29
    /**
30
     * @var ScriptInterface
31
     */
32
    private $script;
33
34
    /**
35
     * @var TransactionInterface
36
     */
37
    private $transaction;
38
39
    /**
40
     * Position of OP_CODESEPARATOR, for calculating SigHash
41
     * @var int
42
     */
43
    private $hashStartPos;
44
45
    /**
46
     * @var int
47
     */
48
    private $opCount;
49
50
    /**
51
     * @var \BitWasp\Bitcoin\Flags
52
     */
53
    private $flags;
54
55
    /**
56
     * @var EcAdapterInterface
57
     */
58
    private $ecAdapter;
59
60
    /**
61
     * @var \BitWasp\Bitcoin\Math\Math
62
     */
63
    private $math;
64
65
    /**
66
     * @var State
67
     */
68
    private $state;
69
70
    /**
71
     * @var bool
72
     */
73
    private $minimalPush;
74
75
    /**
76
     * @var array
77
     */
78
    private $disabledOps = [
79
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
80
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
81
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
82
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
83
    ];
84
85
    public $checkDisabledOpcodes = true;
86
87
    /**
88
     * @param EcAdapterInterface $ecAdapter
89
     * @param TransactionInterface $transaction
90
     * @param \BitWasp\Bitcoin\Flags $flags
91
     */
92 762
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction, Flags $flags)
93
    {
94 762
        $this->ecAdapter = $ecAdapter;
95 762
        $this->math = $ecAdapter->getMath();
96 762
        $this->transaction = $transaction;
97 762
        $this->flags = $flags;
98 762
        $this->script = new Script();
99 762
        $this->minimalPush = $this->flags->checkFlags(InterpreterInterface::VERIFY_MINIMALDATA) === true;
100 762
        $this->state = new State();
101 762
        $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...
102 762
        $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...
103
104 762
        $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...
105 762
        $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...
106 762
    }
107
108
    /**
109
     * @return State
110
     */
111 6
    public function getStackState()
112
    {
113 6
        return $this->state;
114
    }
115
116
    /**
117
     * @param ScriptInterface $script
118
     * @return $this
119
     */
120 684
    public function setScript(ScriptInterface $script)
121
    {
122 684
        $this->script = $script;
123 684
        return $this;
124
    }
125
126
    /**
127
     * @return array
128
     */
129
    public function getDisabledOps()
130
    {
131
        return $this->disabledOps;
132
    }
133
134
    /**
135
     * @param int $op
136
     * @return bool
137
     */
138 684
    public function isDisabledOp($op)
139
    {
140 684
        return in_array($op, $this->disabledOps, true);
141
    }
142
143
    /**
144
     * Cast the value to a boolean
145
     *
146
     * @param $value
147
     * @return bool
148
     */
149 432
    public function castToBool(Buffer $value)
150
    {
151 432
        if ($value->getSize() === 0) {
152 72
            return true;
153
        }
154
155
        // Since we're using buffers, lets try ensuring the contents are not 0.
156 360
        return $this->math->cmp($value->getInt(), 0) > 0;
157
    }
158
159
    /**
160
     * @param Buffer $signature
161
     * @return bool
162
     */
163 54
    public function isValidSignatureEncoding(Buffer $signature)
164
    {
165
        try {
166 54
            TransactionSignature::isDERSignature($signature);
167 36
            return true;
168 18
        } catch (SignatureNotCanonical $e) {
1 ignored issue
show
Unused Code introduced by
This catchblock is empty and will swallow any caught exception.

This check looks for ``catch` blocks that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Empty catch blocks will swallow any caught exception, sometimes causing bugs in your code that are very hard to debug. Consider logging the exception to a debug log or re-throwing it in some way, shape or form.

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