Completed
Pull Request — master (#191)
by thomas
74:56 queued 71:16
created

Interpreter::run()   F

Complexity

Conditions 176
Paths 4684

Size

Total Lines 598
Code Lines 412

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 388
CRAP Score 199.5742

Importance

Changes 14
Bugs 3 Features 2
Metric Value
c 14
b 3
f 2
dl 0
loc 598
ccs 388
cts 427
cp 0.9087
rs 2
cc 176
eloc 412
nc 4684
nop 0
crap 199.5742

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\Interpreter\Stack;
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\TransactionInterface;
21
use BitWasp\Buffertools\Buffer;
22
23
class Interpreter implements InterpreterInterface
24
{
25
    /**
26
     * @var int|string
27
     */
28
    private $inputToSign;
29
30
    /**
31
     * @var ScriptInterface
32
     */
33
    private $script;
34
35
    /**
36
     * @var TransactionInterface
37
     */
38
    private $transaction;
39
40
    /**
41
     * Position of OP_CODESEPARATOR, for calculating SigHash
42
     * @var int
43
     */
44
    private $hashStartPos;
45
46
    /**
47
     * @var int
48
     */
49
    private $opCount;
50
51
    /**
52
     * @var \BitWasp\Bitcoin\Flags
53
     */
54
    private $flags;
55
56
    /**
57
     * @var EcAdapterInterface
58
     */
59
    private $ecAdapter;
60
61
    /**
62
     * @var \BitWasp\Bitcoin\Math\Math
63
     */
64
    private $math;
65
66
    /**
67
     * @var State
68
     */
69
    private $state;
70
71
    /**
72
     * @var array
73
     */
74
    private $disabledOps = [
75
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
76
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
77
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
78
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
79
    ];
80
81
    public $checkDisabledOpcodes = true;
82
83
    /**
84
     * @param EcAdapterInterface $ecAdapter
85
     * @param TransactionInterface $transaction
86
     * @param \BitWasp\Bitcoin\Flags $flags
87
     */
88 1488
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction, Flags $flags)
89
    {
90 1488
        $this->ecAdapter = $ecAdapter;
91 1488
        $this->math = $ecAdapter->getMath();
92 1488
        $this->transaction = $transaction;
93 1488
        $this->flags = $flags;
94 1488
        $this->script = new Script();
95 1488
        $this->state = new State();
96 1488
    }
97
98
    /**
99
     * @return State
100
     */
101 6
    public function getStackState()
102
    {
103 6
        return $this->state;
104
    }
105
106
    /**
107
     * @param ScriptInterface $script
108
     * @return $this
109
     */
110 1392
    public function setScript(ScriptInterface $script)
111
    {
112 1392
        $this->script = $script;
113 1392
        return $this;
114
    }
115
116
    /**
117
     * @return array
118
     */
119
    public function getDisabledOps()
120
    {
121
        return $this->disabledOps;
122
    }
123
124
    /**
125
     * @param int $op
126
     * @return bool
127
     */
128 1386
    public function isDisabledOp($op)
129
    {
130 1386
        return in_array($op, $this->disabledOps, true);
131
    }
132
133
    /**
134
     * Cast the value to a boolean
135
     *
136
     * @param $value
137
     * @return bool
138
     */
139 450
    public function castToBool(Buffer $value)
140
    {
141 450
        if ($value->getSize() === 0) {
142 66
            return true;
143
        }
144
145
        // Since we're using buffers, lets try ensuring the contents are not 0.
146 384
        return $this->math->cmp($value->getInt(), 0) > 0; // cscriptNum or edge case.
147
    }
148
149
    /**
150
     * @param Buffer $signature
151
     * @return bool
152
     */
153 60
    public function isValidSignatureEncoding(Buffer $signature)
154
    {
155
        try {
156 60
            TransactionSignature::isDERSignature($signature);
157 42
            return true;
158 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...
159
            /* In any case, we will return false outside this block */
160
        }
161
162 18
        return false;
163
    }
164
165
    /**
166
     * @param Buffer $signature
167
     * @return bool
168
     * @throws ScriptRuntimeException
169
     * @throws \Exception
170
     */
171 36
    public function isLowDerSignature(Buffer $signature)
172
    {
173 36
        if (!$this->isValidSignatureEncoding($signature)) {
174 6
            throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DERSIG, 'Signature with incorrect encoding');
175
        }
176
177 30
        $binary = $signature->getBinary();
178 30
        $nLenR = ord($binary[3]);
179 30
        $nLenS = ord($binary[5 + $nLenR]);
180 30
        $s = $signature->slice(6 + $nLenR, $nLenS)->getInt();
181
182 30
        return $this->ecAdapter->validateSignatureElement($s, true);
183
    }
184
185
    /**
186
     * Determine whether the sighash byte appended to the signature encodes
187
     * a valid sighash type.
188
     *
189
     * @param Buffer $signature
190
     * @return bool
191
     */
192 42
    public function isDefinedHashtypeSignature(Buffer $signature)
193
    {
194 42
        if ($signature->getSize() === 0) {
195 6
            return false;
196
        }
197
198 36
        $binary = $signature->getBinary();
199 36
        $nHashType = ord(substr($binary, -1)) & (~(SignatureHashInterface::SIGHASH_ANYONECANPAY));
200
201 36
        $math = $this->math;
202 36
        return ! ($math->cmp($nHashType, SignatureHashInterface::SIGHASH_ALL) < 0 || $math->cmp($nHashType, SignatureHashInterface::SIGHASH_SINGLE) > 0);
203
    }
204
205
    /**
206
     * @param Buffer $signature
207
     * @return $this
208
     * @throws \BitWasp\Bitcoin\Exceptions\ScriptRuntimeException
209
     */
210 66
    public function checkSignatureEncoding(Buffer $signature)
211
    {
212 66
        if ($signature->getSize() === 0) {
213 6
            return $this;
214
        }
215
216 60
        if ($this->flags->checkFlags(InterpreterInterface::VERIFY_DERSIG | InterpreterInterface::VERIFY_LOW_S | InterpreterInterface::VERIFY_STRICTENC) && !$this->isValidSignatureEncoding($signature)) {
217 12
            throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DERSIG, 'Signature with incorrect encoding');
218 48
        } else if ($this->flags->checkFlags(InterpreterInterface::VERIFY_LOW_S) && !$this->isLowDerSignature($signature)) {
219 6
            throw new ScriptRuntimeException(InterpreterInterface::VERIFY_LOW_S, 'Signature s element was not low');
220 42
        } else if ($this->flags->checkFlags(InterpreterInterface::VERIFY_STRICTENC) && !$this->isDefinedHashtypeSignature($signature)) {
221 6
            throw new ScriptRuntimeException(InterpreterInterface::VERIFY_STRICTENC, 'Signature with invalid hashtype');
222
        }
223
224 36
        return $this;
225
    }
226
227
    /**
228
     * @param Buffer $publicKey
229
     * @return $this
230
     * @throws \Exception
231
     */
232 30
    public function checkPublicKeyEncoding(Buffer $publicKey)
233
    {
234 30
        if ($this->flags->checkFlags(InterpreterInterface::VERIFY_STRICTENC) && !PublicKey::isCompressedOrUncompressed($publicKey)) {
235 6
            throw new ScriptRuntimeException(InterpreterInterface::VERIFY_STRICTENC, 'Public key with incorrect encoding');
236
        }
237
238 24
        return $this;
239
    }
240
241
    /**
242
     * @param $opCode
243
     * @param Buffer $pushData
244
     * @return bool
245
     * @throws \Exception
246
     */
247 12
    public function checkMinimalPush($opCode, Buffer $pushData)
248
    {
249 12
        $pushSize = $pushData->getSize();
250 12
        $binary = $pushData->getBinary();
251
252 12
        if ($pushSize === 0) {
253 6
            return $opCode === Opcodes::OP_0;
254 12
        } elseif ($pushSize === 1) {
255 6
            $first = ord($binary[0]);
256 6
            if ($first >= 1 && $first <= 16) {
257 6
                return $opCode === (Opcodes::OP_1 + ($first - 1));
258 6
            } elseif ($first === 0x81) {
259 6
                return $opCode === Opcodes::OP_1NEGATE;
260
            }
261 12
        } elseif ($pushSize <= 75) {
262 12
            return $opCode === $pushSize;
263 6
        } elseif ($pushSize <= 255) {
264 6
            return $opCode === Opcodes::OP_PUSHDATA1;
265 6
        } elseif ($pushSize <= 65535) {
266 6
            return $opCode === Opcodes::OP_PUSHDATA2;
267
        }
268
269 6
        return true;
270
    }
271
272
    /**
273
     * @return $this
274
     * @throws \Exception
275
     */
276 1368
    private function checkOpcodeCount()
277
    {
278 1368
        if ($this->math->cmp($this->opCount, 201) > 0) {
279 6
            throw new \RuntimeException('Error: Script op code count');
280
        }
281
282 1368
        return $this;
283
    }
284
285
    /**
286
     * @param ScriptInterface $script
287
     * @param Buffer $sigBuf
288
     * @param Buffer $keyBuf
289
     * @return bool
290
     * @throws ScriptRuntimeException
291
     * @throws \Exception
292
     */
293 24
    private function checkSig(ScriptInterface $script, Buffer $sigBuf, Buffer $keyBuf)
294
    {
295 24
        $this
296 24
            ->checkSignatureEncoding($sigBuf)
297 18
            ->checkPublicKeyEncoding($keyBuf);
298
299
        try {
300 18
            $txSignature = TransactionSignatureFactory::fromHex($sigBuf->getHex());
301 18
            $publicKey = PublicKeyFactory::fromHex($keyBuf->getHex());
302
303 18
            return $this->ecAdapter->verify(
304 18
                $this
305
                    ->transaction
306 18
                    ->getSignatureHash()
307 18
                    ->calculate($script, $this->inputToSign, $txSignature->getHashType()),
308 18
                $publicKey,
309 18
                $txSignature->getSignature()
310 18
            );
311
        } catch (\Exception $e) {
312
            return false;
313
        }
314
    }
315
316
    /**
317
     * @param ScriptInterface $scriptSig
318
     * @param ScriptInterface $scriptPubKey
319
     * @param int $nInputToSign
320
     * @return bool
321
     * @throws \Exception
322
     */
323 1392
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $nInputToSign)
324
    {
325 1392
        $this->inputToSign = $nInputToSign;
326 1392
        if (!$this->setScript($scriptSig)->run()) {
327 672
            return false;
328
        }
329
330 720
        $mainStack = $this->state->getMainStack();
331 720
        $stackCopy = new Stack;
332 720
        if ($this->flags->checkFlags(InterpreterInterface::VERIFY_P2SH)) {
333 174
            $stackCopy = $this->state->cloneMainStack();
334 174
        }
335
336 720
        if (!$this->setScript($scriptPubKey)->run()) {
337 270
            return false;
338
        }
339
340 450
        if (count($mainStack) === 0) {
341 6
            return false;
342
        }
343
344 444
        if (false === $this->castToBool($mainStack[-1])) {
345 6
            return false;
346
        }
347
348 438
        $verifier = new OutputClassifier($scriptPubKey);
349
350 438
        if ($this->flags->checkFlags(InterpreterInterface::VERIFY_P2SH) && $verifier->isPayToScriptHash()) {
351 18
            if (!$scriptSig->isPushOnly()) {
352 6
                return false;
353
            }
354
355
            // Restore mainStack to how it was after evaluating scriptSig
356 12
            $mainStack = $this->state->restoreMainStack($stackCopy)->getMainStack();
357 12
            if (count($mainStack) === 0) {
358
                return false;
359
            }
360
361
            // Load redeemscript as the scriptPubKey
362 12
            $scriptPubKey = new Script($mainStack[-1]);
363 12
            $mainStack->pop();
364 12
            if (!$this->setScript($scriptPubKey)->run()) {
365 6
                return false;
366
            }
367 6
        }
368
369 426
        return true;
370
    }
371
372
    /**
373
     * @return bool
374
     */
375 1386
    private function checkExec()
376
    {
377 1386
        $vfStack = $this->state->getVfStack();
378 1386
        $c = 0;
379 1386
        $len = $vfStack->end();
380 1386
        for ($i = 0; $i < $len; $i++) {
381
            if ($vfStack->top(0 - $len - $i) === true) {
382
                $c++;
383
            }
384
        }
385 1386
        return !(bool)$c;
386
    }
387
388
    /**
389
     * @return bool
390
     */
391 1392
    public function run()
392
    {
393 1392
        $math = $this->math;
394
395 1392
        $flags = $this->flags;
396 1392
        $mainStack = $this->state->getMainStack();
397 1392
        $altStack = $this->state->getAltStack();
398 1392
        $vfStack = $this->state->getVfStack();
399
400 1392
        $this->hashStartPos = 0;
401 1392
        $this->opCount = 0;
402 1392
        $parser = $this->script->getScriptParser();
403 1392
        $vchFalse = new Buffer("", 0, $math);
404 1392
        $vchTrue = new Buffer("\x01", 1, $math);
405 1392
        $int0 = new ScriptNum($this->math, new Flags(0), $vchFalse, 4);
406 1392
        $int1 = new ScriptNum($this->math, new Flags(0), $vchTrue, 1);
0 ignored issues
show
Unused Code introduced by
$int1 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
407
408 1392
        if ($this->script->getBuffer()->getSize() > 10000) {
409
            return false;
410
        }
411
412
        try {
413 1392
            foreach ($parser as $exec) {
414 1386
                $opCode = $exec->getOp();
415 1386
                $pushData = $exec->getData();
416 1386
                $fExec = $this->checkExec();
417
418
                // If pushdata was written to,
419 1386
                if ($pushData instanceof Buffer && $pushData->getSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
420
                    throw new \RuntimeException('Error - push size');
421
                }
422
423
                // OP_RESERVED should not count towards opCount
424 1386
                if ($opCode > Opcodes::OP_16 && ++$this->opCount) {
425 1368
                    $this->checkOpcodeCount();
426 1368
                }
427
428 1386
                if ($this->checkDisabledOpcodes && $this->isDisabledOp($opCode)) {
429 12
                    throw new \RuntimeException('Disabled Opcode');
430
                }
431
432 1386
                if ($fExec && $opCode >= 0 && $opCode <= Opcodes::OP_PUSHDATA4) {
433
                    // In range of a pushdata opcode
434 450
                    if ($flags->checkFlags(InterpreterInterface::VERIFY_MINIMALDATA) && !$this->checkMinimalPush($opCode, $pushData)) {
0 ignored issues
show
Bug introduced by
It seems like $pushData defined by $exec->getData() on line 415 can be null; however, BitWasp\Bitcoin\Script\I...ter::checkMinimalPush() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
435 6
                        throw new ScriptRuntimeException(InterpreterInterface::VERIFY_MINIMALDATA, 'Minimal pushdata required');
436
                    }
437
438 444
                    $mainStack->push($pushData);
439
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
440 1380
                } elseif ($fExec || ($opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF)) {
441
                    // echo "OPCODE - " . $this->script->getOpCodes()->getOp($opCode) . "\n";
442
                    switch ($opCode) {
443 1356
                        case Opcodes::OP_1:
444 1356
                        case Opcodes::OP_2:
445 1356
                        case Opcodes::OP_3:
446 1356
                        case Opcodes::OP_4:
447 1356
                        case Opcodes::OP_5:
448 1356
                        case Opcodes::OP_6:
449 1356
                        case Opcodes::OP_7:
450 1356
                        case Opcodes::OP_8:
451 1356
                        case Opcodes::OP_9:
452 1356
                        case Opcodes::OP_10:
453 1356
                        case Opcodes::OP_11:
454 1356
                        case Opcodes::OP_12:
455 1356
                        case Opcodes::OP_13:
456 1356
                        case Opcodes::OP_14:
457 1356
                        case Opcodes::OP_15:
458 1356
                        case Opcodes::OP_16:
459 192
                            $num = $opCode - (Opcodes::OP_1 - 1);
460 192
                            $mainStack->push(new ScriptNum($this->math, new Flags(0), new Buffer(chr($num), 4), 4));
461 192
                            break;
462
463 1356
                        case $opCode >= Opcodes::OP_NOP1 && $opCode <= Opcodes::OP_NOP10:
464 6
                            if ($flags->checkFlags(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
465 6
                                throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOPS found - this is discouraged');
466
                            }
467
                            break;
468
469 1350
                        case Opcodes::OP_NOP:
470 12
                            break;
471
472 1338
                        case Opcodes::OP_IF:
473 1338
                        case Opcodes::OP_NOTIF:
474
                            // <expression> if [statements] [else [statements]] endif
475 18
                            $value = false;
476 18
                            if ($fExec) {
477 18
                                if (count($mainStack) < 1) {
478 6
                                    throw new \RuntimeException('Unbalanced conditional');
479
                                }
480
                                // todo
481 12
                                $buffer = new ScriptNum($math, $this->flags, $mainStack->pop(), 4);
482 12
                                $value = $this->castToBool($buffer);
483 12
                                if ($opCode === Opcodes::OP_NOTIF) {
484 6
                                    $value = !$value;
485 6
                                }
486 12
                            }
487 12
                            $vfStack->push($value ? $vchTrue : $vchFalse);
488 12
                            break;
489
490 1332
                        case Opcodes::OP_ELSE:
491 18
                            if (count($vfStack) === 0) {
492 6
                                throw new \RuntimeException('Unbalanced conditional');
493
                            }
494 12
                            $vfStack[-1] = !$vfStack->end() ? $vchTrue : $vchFalse;
495 12
                            break;
496
497 1326
                        case Opcodes::OP_ENDIF:
498 18
                            if (count($vfStack) === 0) {
499 6
                                throw new \RuntimeException('Unbalanced conditional');
500
                            }
501 12
                            break;
502
503 1320
                        case Opcodes::OP_VERIFY:
504 24
                            if (count($mainStack) < 1) {
505 6
                                throw new \RuntimeException('Invalid stack operation');
506
                            }
507 18
                            $value = $this->castToBool($mainStack[-1]);
508 18
                            if (!$value) {
509 6
                                throw new \RuntimeException('Error: verify');
510
                            }
511 12
                            $mainStack->pop();
512 12
                            break;
513
514 1296
                        case Opcodes::OP_RESERVED:
515
                            // todo
516 12
                            break;
517
518 1284
                        case Opcodes::OP_TOALTSTACK:
519 18
                            if (count($mainStack) < 1) {
520 6
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
521
                            }
522 12
                            $altStack->push($mainStack->pop());
523 12
                            break;
524
525 1278
                        case Opcodes::OP_FROMALTSTACK:
526 12
                            if (count($altStack) < 1) {
527 6
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
528
                            }
529 6
                            $mainStack->push($altStack->pop());
530 6
                            break;
531
532 1272
                        case Opcodes::OP_IFDUP:
533
                            // If top value not zero, duplicate it.
534 12
                            if (count($mainStack) < 1) {
535 6
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
536
                            }
537 6
                            $vch = $mainStack[-1];
538 6
                            if ($this->castToBool($vch)) {
539 6
                                $mainStack->push($vch);
540 6
                            }
541 6
                            break;
542
543 1266
                        case Opcodes::OP_DEPTH:
544 78
                            $num = count($mainStack);
545 78
                            if ($num === 0) {
546 30
                                $depth = $vchFalse;
547 30
                            } else {
548 54
                                $depth = (new ScriptNum($math, $this->flags, Buffer::int($num), 4));
549
                            }
550
551 78
                            $mainStack->push($depth);
552 78
                            break;
553
554 1266
                        case Opcodes::OP_DROP:
555 6
                            if (count($mainStack) < 1) {
556 6
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
557
                            }
558
                            $mainStack->pop();
559
                            break;
560 1260
                        case Opcodes::OP_DUP:
561 18
                            if (count($mainStack) < 1) {
562 6
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
563
                            }
564 12
                            $vch = $mainStack[-1];
565 12
                            $mainStack->push($vch);
566 12
                            break;
567
568 1254
                        case Opcodes::OP_NIP:
569 18
                            if (count($mainStack) < 2) {
570 6
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
571
                            }
572 12
                            unset($mainStack[-2]);
573 12
                            break;
574
575 1248
                        case Opcodes::OP_OVER:
576 18
                            if (count($mainStack) < 2) {
577 6
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
578
                            }
579 12
                            $vch = $mainStack[-2];
580 12
                            $mainStack->push($vch);
581 12
                            break;
582
    
583 1242
                        case Opcodes::OP_ROT:
584 12
                            if (count($mainStack) < 3) {
585 6
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
586
                            }
587 6
                            $mainStack->swap(-3, -2);
588 6
                            $mainStack->swap(-2, -1);
589 6
                            break;
590
591 1236
                        case Opcodes::OP_SWAP:
592 12
                            if (count($mainStack) < 2) {
593 6
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
594
                            }
595 6
                            $mainStack->swap(-2, -1);
596 6
                            break;
597
598 1230
                        case Opcodes::OP_TUCK:
599 12
                            if (count($mainStack) < 2) {
600 6
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
601
                            }
602 6
                            $vch = $mainStack[-1];
603 6
                            $mainStack->add(count($mainStack) - 1 - 2, $vch);
604
                            break;
605
606 1218
                        case Opcodes::OP_PICK:
607 1218
                        case Opcodes::OP_ROLL:
608 24
                            if (count($mainStack) < 2) {
609 6
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
610
                            }
611 18
                            $top = $mainStack[-1];
612 18
                            $n = (new ScriptNum($math, $this->flags, $top, 4))->getInt();
613 18
                            $mainStack->pop();
614 18
                            if ($math->cmp($n, 0) < 0 || $math->cmp($n, count($mainStack)) >= 0) {
615 6
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
616
                            }
617
618 12
                            $pos = (int) $math->sub($math->sub(0, $n), 1);
619 12
                            $vch = $mainStack[$pos];
620 12
                            if ($opCode === Opcodes::OP_ROLL) {
621 6
                                unset($mainStack[$pos]);
622 6
                            }
623 12
                            $mainStack->push($vch);
624 12
                            break;
625
626 1206
                        case Opcodes::OP_2DROP:
627 12
                            if (count($mainStack) < 2) {
628 6
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
629
                            }
630 6
                            $mainStack->pop();
631 6
                            $mainStack->pop();
632 6
                            break;
633
634 1200
                        case Opcodes::OP_2DUP:
635 18
                            if (count($mainStack) < 2) {
636 6
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
637
                            }
638 12
                            $string1 = $mainStack[-2];
639 12
                            $string2 = $mainStack[-1];
640 12
                            $mainStack->push($string1);
641 12
                            $mainStack->push($string2);
642 12
                            break;
643
644 1194
                        case Opcodes::OP_3DUP:
645 18
                            if (count($mainStack) < 3) {
646 6
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
647
                            }
648 12
                            $string1 = $mainStack[-3];
649 12
                            $string2 = $mainStack[-2];
650 12
                            $string3 = $mainStack[-1];
651 12
                            $mainStack->push($string1);
652 12
                            $mainStack->push($string2);
653 12
                            $mainStack->push($string3);
654 12
                            break;
655
656 1188
                        case Opcodes::OP_2OVER:
657 18
                            if (count($mainStack) < 4) {
658 6
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
659
                            }
660 12
                            $string1 = $mainStack[-4];
661 12
                            $string2 = $mainStack[-3];
662 12
                            $mainStack->push($string1);
663 12
                            $mainStack->push($string2);
664 12
                            break;
665
666 1182
                        case Opcodes::OP_2ROT:
667 12
                            if (count($mainStack) < 6) {
668 6
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
669
                            }
670 6
                            $string1 = $mainStack[-6];
671 6
                            $string2 = $mainStack[-5];
672 6
                            unset($mainStack[-6], $mainStack[-5]);
673 6
                            $mainStack->push($string1);
674 6
                            $mainStack->push($string2);
675 6
                            break;
676
677 1176
                        case Opcodes::OP_2SWAP:
678 12
                            if (count($mainStack) < 4) {
679 6
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
680
                            }
681 6
                            $mainStack->swap(-3, -1);
682 6
                            $mainStack->swap(-4, -2);
683 6
                            break;
684
685 1170
                        case Opcodes::OP_SIZE:
686 12
                            if (count($mainStack) < 1) {
687 6
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
688
                            }
689
                            // todo: Int sizes?
690 6
                            $vch = $mainStack[-1];
691 6
                            $size = Buffer::int($vch->getSize(), null, $math);
692
693 6
                            $mainStack->push($size);
694 6
                            break;
695
696 1164
                        case Opcodes::OP_EQUAL:
697 1164
                        case Opcodes::OP_EQUALVERIFY:
698 408
                            if (count($mainStack) < 2) {
699 6
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
700
                            }
701 402
                            $vch1 = $mainStack[-2];
702 402
                            $vch2 = $mainStack[-1];
703
704 402
                            $equal = ($vch1->getBinary() === $vch2->getBinary());
705
706 402
                            $mainStack->pop();
707 402
                            $mainStack->pop();
708 402
                            $mainStack->push(($equal ? $vchTrue : $vchFalse));
709 402
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
710 114
                                if ($equal) {
711 102
                                    $mainStack->pop();
712 102
                                } else {
713 12
                                    throw new \RuntimeException('Error EQUALVERIFY');
714
                                }
715 102
                            }
716
717 390
                            break;
718
719
                        // Arithmetic operations
720 978
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
721 48
                            if (count($mainStack) < 1) {
722 6
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
723
                            }
724
725 42
                            $num = (new ScriptNum($math, $this->flags, $mainStack[-1], 4))->getInt();
726
727 42
                            if ($opCode === Opcodes::OP_1ADD) { // cscriptnum
728 6
                                $num = $math->add($num, '1');
729 42
                            } elseif ($opCode === Opcodes::OP_1SUB) {
730 6
                                $num = $math->sub($num, '1');
731 36
                            } elseif ($opCode === Opcodes::OP_2MUL) {
732
                                $num = $math->mul(2, $num);
733 30
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
734
                                $num = $math->sub(0, $num);
735 30
                            } elseif ($opCode === Opcodes::OP_ABS) {
736
                                if ($math->cmp($num, '0') < 0) {
737
                                    $num = $math->sub(0, $num);
738
                                }
739 30
                            } elseif ($opCode === Opcodes::OP_NOT) {
740 18
                                $num = ($math->cmp($num, '0') === 0);
741 18
                            } else {
742
                                // is OP_0NOTEQUAL
743 12
                                $num = ($math->cmp($num, '0') !== 0);
744
                            }
745
746 42
                            $mainStack->pop();
747
748 42
                            $buffer = Buffer::int($num, 4, $math);
749 42
                            $mainStack->push($buffer);
750 42
                            break;
751
752 930
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
753 150
                            if (count($mainStack) < 2) {
754 24
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
755
                            }
756
757 126
                            $num1 = (new ScriptNum($math, $this->flags, $mainStack[-2], 4))->getInt();
758 126
                            $num2 = (new ScriptNum($math, $this->flags, $mainStack[-1], 4))->getInt();
759
760 126
                            if ($opCode === Opcodes::OP_ADD) {
761 6
                                $num = $math->add($num1, $num2);
762 126
                            } else if ($opCode === Opcodes::OP_SUB) {
763 6
                                $num = $math->sub($num1, $num2);
764 120
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
765 12
                                $num = $math->cmp($num1, $int0->getInt()) !== 0 && $math->cmp($num2, $int0->getInt()) !== 0;
766 114
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
767 18
                                $num = $math->cmp($num1, $int0->getInt()) !== 0 || $math->cmp($num2, $int0->getInt()) !== 0;
768 102
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
769 18
                                $num = $math->cmp($num1, $num2) === 0;
770 84
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
771
                                $num = $math->cmp($num1, $num2) === 0;
772 66
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
773 6
                                $num = $math->cmp($num1, $num2) !== 0;
774 66
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) { // cscriptnum
775 12
                                $num = $math->cmp($num1, $num2) < 0;
776 60
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
777 12
                                $num = $math->cmp($num1, $num2) > 0;
778 48
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) { // cscriptnum
779 12
                                $num = $math->cmp($num1, $num2) <= 0;
780 36
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
781 12
                                $num = $math->cmp($num1, $num2) >= 0;
782 24
                            } elseif ($opCode === Opcodes::OP_MIN) {
783 6
                                $num = ($math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
784 6
                            } else {
785 6
                                $num = ($math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
786
                            }
787
788 126
                            $mainStack->pop();
789 126
                            $mainStack->pop();
790 126
                            $buffer = Buffer::int($num, 4, $math);
791 126
                            $mainStack->push($buffer);
792
793 126
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
794
                                if ($this->castToBool($mainStack[-1])) {
795
                                    $mainStack->pop();
796
                                } else {
797
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
798
                                }
799
                            }
800 126
                            break;
801
802 780
                        case Opcodes::OP_WITHIN:
803 12
                            if (count($mainStack) < 3) {
804 6
                                throw new \RuntimeException('Invalid stack operation');
805
                            }
806
807 6
                            $num1 = (new ScriptNum($math, $this->flags, $mainStack[-3], 4))->getInt();
808 6
                            $num2 = (new ScriptNum($math, $this->flags, $mainStack[-2], 4))->getInt();
809 6
                            $num3 = (new ScriptNum($math, $this->flags, $mainStack[-1], 4))->getInt();
810
811 6
                            $value = $math->cmp($num2, $num1) <= 0 && $math->cmp($num1, $num3) < 0;
812 6
                            $mainStack->pop();
813 6
                            $mainStack->pop();
814 6
                            $mainStack->pop();
815 6
                            $mainStack->push($value ? $vchFalse : $vchTrue);
816 6
                            break;
817
818
                        // Hash operation
819 768
                        case Opcodes::OP_RIPEMD160:
820 768
                        case Opcodes::OP_SHA1:
821 768
                        case Opcodes::OP_SHA256:
822 768
                        case Opcodes::OP_HASH160:
823 768
                        case Opcodes::OP_HASH256:
824 66
                            if (count($mainStack) < 1) {
825 12
                                throw new \RuntimeException('Invalid stack operation');
826
                            }
827
828 54
                            $buffer = $mainStack[-1];
829 54
                            if ($opCode === Opcodes::OP_RIPEMD160) {
830 6
                                $hash = Hash::ripemd160($buffer);
831 54
                            } elseif ($opCode === Opcodes::OP_SHA1) {
832 6
                                $hash = Hash::sha1($buffer);
833 48
                            } elseif ($opCode === Opcodes::OP_SHA256) {
834 6
                                $hash = Hash::sha256($buffer);
835 42
                            } elseif ($opCode === Opcodes::OP_HASH160) {
836 30
                                $hash = Hash::sha256ripe160($buffer);
837 30
                            } else {
838 6
                                $hash = Hash::sha256d($buffer);
839
                            }
840
841 54
                            $mainStack->pop();
842 54
                            $mainStack->push($hash);
843 54
                            break;
844
845 720
                        case Opcodes::OP_CODESEPARATOR:
846
                            $this->hashStartPos = $parser->getPosition();
847
                            break;
848
849 720
                        case Opcodes::OP_CHECKSIG:
850 720
                        case Opcodes::OP_CHECKSIGVERIFY:
851 24
                            if (count($mainStack) < 2) {
852 6
                                throw new \RuntimeException('Invalid stack operation');
853
                            }
854
855 18
                            $vchPubKey = $mainStack[-1];
856 18
                            $vchSig = $mainStack[-2];
857
858 18
                            $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
859
860 18
                            $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
861
862 12
                            $mainStack->pop();
863 12
                            $mainStack->pop();
864 12
                            $mainStack->push($success ? $vchTrue : $vchFalse);
865
866 12
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
867
                                if ($success) {
868
                                    $mainStack->pop();
869
                                } else {
870
                                    throw new \RuntimeException('Checksig verify');
871
                                }
872
                            }
873
874 12
                            break;
875
876 696
                        case Opcodes::OP_CHECKMULTISIG:
877 696
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
878 6
                            $i = 1;
879 6
                            if (count($mainStack) < $i) {
880
                                throw new \RuntimeException('Invalid stack operation');
881
                            }
882
883 6
                            $keyCount = $mainStack[-$i]->getInt();
884 6
                            if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
885
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
886
                            }
887 6
                            $this->opCount += $keyCount;
888 6
                            $this->checkOpcodeCount();
889
890
                            // Extract positions of the keys, and signatures, from the stack.
891 6
                            $ikey = ++$i;
892 6
                            $i += $keyCount; /** @var int $i */
893 6
                            if (count($mainStack) < $i) {
894
                                throw new \RuntimeException('Invalid stack operation');
895
                            }
896
897 6
                            $sigCount = $mainStack[-$i]->getInt(); // cscriptnum
898 6
                            if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
899
                                throw new \RuntimeException('Invalid Signature count');
900
                            }
901 6
                            $isig = ++$i;
902 6
                            $i += $sigCount;
903
904
                            // Extract the script since the last OP_CODESEPARATOR
905 6
                            $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
906
907 6
                            $fSuccess = true;
908 6
                            while ($fSuccess && $sigCount > 0) {
909
                                // Fetch the signature and public key
910 6
                                $sig = $mainStack[-$isig];
911 6
                                $pubkey = $mainStack[-$ikey];
912
913
                                // Erase the signature and public key.
914 6
                                unset($mainStack[-$isig], $mainStack[-$ikey]);
915
916
                                // Decrement $i, since we are consuming stack values.
917 6
                                $i -= 2;
918
919 6
                                if ($this->checkSig($scriptCode, $sig, $pubkey)) {
920 6
                                    $isig++;
921 6
                                    $sigCount--;
922 6
                                }
923
924 6
                                $ikey++;
925 6
                                $keyCount--;
926
927
                                // If there are more signatures left than keys left,
928
                                // then too many signatures have failed. Exit early,
929
                                // without checking any further signatures.
930 6
                                if ($sigCount > $keyCount) {
931
                                    $fSuccess = false;
932
                                }
933 6
                            }
934
935 6
                            while ($i-- > 1) {
936 6
                                $mainStack->pop();
937 6
                            }
938
939
                            // A bug causes CHECKMULTISIG to consume one extra argument
940
                            // whose contents were not checked in any way.
941
                            //
942
                            // Unfortunately this is a potential source of mutability,
943
                            // so optionally verify it is exactly equal to zero prior
944
                            // to removing it from the stack.
945 6
                            if (count($mainStack) < 1) {
946
                                throw new \RuntimeException('Invalid stack operation');
947
                            }
948
949 6
                            if ($flags->checkFlags(InterpreterInterface::VERIFY_NULL_DUMMY) && $mainStack[-1]->getSize()) {
950
                                throw new ScriptRuntimeException(InterpreterInterface::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
951
                            }
952
953 6
                            $mainStack->pop();
954 6
                            $mainStack->push($fSuccess ? $vchTrue : $vchFalse);
955
956 6
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
957
                                if ($fSuccess) {
958
                                    $mainStack->pop();
959
                                } else {
960
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
961
                                }
962
                            }
963 6
                            break;
964
965 690
                        default:
966 690
                            throw new \RuntimeException('Opcode not found');
967 690
                    }
968
969 438
                    if (count($mainStack) + count($altStack) > 1000) {
970
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
971
                    }
972 438
                }
973 720
            }
974
975 720
            if (!$vfStack->end() === 0) {
976
                throw new \RuntimeException('Unbalanced conditional at script end');
977
            }
978
979 720
            return true;
980 948
        } catch (ScriptRuntimeException $e) {
981
            // echo "\n Runtime: " . $e->getMessage() . "\n";
982
            // Failure due to script tags, can access flag: $e->getFailureFlag()
983 18
            return false;
984 930
        } catch (\Exception $e) {
985
            // echo "\n General: " . $e->getMessage() ;
986 930
            return false;
987
        }
988
    }
989
}
990