Completed
Push — master ( 053212...f5f5a5 )
by thomas
46:11 queued 29:33
created

Interpreter::setScript()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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