Completed
Pull Request — master (#244)
by thomas
201:55 queued 131:14
created

Interpreter::verifyWitnessProgram()   C

Complexity

Conditions 11
Paths 21

Size

Total Lines 49
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 85.3091

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 49
rs 5.2653
ccs 3
cts 20
cp 0.15
cc 11
eloc 31
nc 21
nop 3
crap 85.3091

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Interpreter;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
6
use BitWasp\Bitcoin\Crypto\Hash;
7
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
8
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
9
use BitWasp\Bitcoin\Flags;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey;
11
use BitWasp\Bitcoin\Key\PublicKeyFactory;
12
use BitWasp\Bitcoin\Locktime;
13
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
14
use BitWasp\Bitcoin\Script\Opcodes;
15
use BitWasp\Bitcoin\Script\Script;
16
use BitWasp\Bitcoin\Script\ScriptFactory;
17
use BitWasp\Bitcoin\Script\ScriptInterface;
18
use BitWasp\Bitcoin\Script\ScriptWitness;
19
use BitWasp\Bitcoin\Script\WitnessProgram;
20
use BitWasp\Bitcoin\Signature\TransactionSignature;
21
use BitWasp\Bitcoin\Signature\TransactionSignatureFactory;
22
use BitWasp\Bitcoin\Transaction\SignatureHash\SignatureHashInterface;
23
use BitWasp\Bitcoin\Transaction\TransactionInputInterface;
24
use BitWasp\Bitcoin\Transaction\TransactionInterface;
25
use BitWasp\Buffertools\Buffer;
26
use BitWasp\Buffertools\BufferInterface;
27
28
class Interpreter implements InterpreterInterface
29
{
30
    /**
31
     * @var int|string
32
     */
33
    private $inputToSign;
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;
0 ignored issues
show
Unused Code introduced by
The property $flags is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
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 Buffer
68
     */
69
    private $vchFalse;
70
71
    /**
72
     * @var Buffer
73
     */
74
    private $vchTrue;
75
76
    /**
77
     * @var Buffer
78
     */
79
    private $int0;
80
81
    /**
82
     * @var Buffer
83
     */
84
    private $int1;
85
86
    /**
87
     * @var array
88
     */
89
    private $disabledOps = [
90
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
91
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
92
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
93
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
94
    ];
95
96
    /**
97
     * @param EcAdapterInterface $ecAdapter
98
     * @param TransactionInterface $transaction
99
     */
100
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction)
101
    {
102
        $this->ecAdapter = $ecAdapter;
103
        $this->math = $ecAdapter->getMath();
104
        $this->transaction = $transaction;
105
106
        $this->vchFalse = new Buffer("", 0, $this->math);
107
        $this->vchTrue = new Buffer("\x01", 1, $this->math);
108
        $this->int0 = Number::buffer($this->vchFalse, false, 4, $this->math)->getBuffer();
109
        $this->int1 = Number::buffer($this->vchTrue, false, 1, $this->math)->getBuffer();
110
    }
111
112
    /**
113
     * Cast the value to a boolean
114
     *
115
     * @param BufferInterface $value
116
     * @return bool
117
     */
118 756
    public function castToBool(BufferInterface $value)
119
    {
120 756
        if ($value->getSize() === 0) {
121 756
            return true;
122 756
        }
123 756
124 756
        // Since we're using buffers, lets try ensuring the contents are not 0.
125 756
        return $this->math->cmp($value->getInt(), 0) > 0;
126 756
    }
127 756
128
    /**
129 756
     * @param BufferInterface $signature
130 756
     * @return bool
131 756
     */
132 756
    public function isValidSignatureEncoding(BufferInterface $signature)
133 756
    {
134
        try {
135
            TransactionSignature::isDERSignature($signature);
136
            return true;
137
        } catch (SignatureNotCanonical $e) {
138
            /* In any case, we will return false outside this block */
139
        }
140
141 432
        return false;
142
    }
143 432
144 66
    /**
145
     * @param BufferInterface $signature
146
     * @return bool
147
     * @throws ScriptRuntimeException
148 366
     * @throws \Exception
149
     */
150
    public function isLowDerSignature(BufferInterface $signature)
151
    {
152
        if (!$this->isValidSignatureEncoding($signature)) {
153
            throw new ScriptRuntimeException(self::VERIFY_DERSIG, 'Signature with incorrect encoding');
154
        }
155 54
156
        $binary = $signature->getBinary();
157
        $nLenR = ord($binary[3]);
158 54
        $nLenS = ord($binary[5 + $nLenR]);
159 36
        $s = $signature->slice(6 + $nLenR, $nLenS)->getInt();
160 18
161
        return $this->ecAdapter->validateSignatureElement($s, true);
162
    }
163
164 18
    /**
165
     * Determine whether the sighash byte appended to the signature encodes
166
     * a valid sighash type.
167
     *
168
     * @param BufferInterface $signature
169
     * @return bool
170
     */
171
    public function isDefinedHashtypeSignature(BufferInterface $signature)
172
    {
173 30
        if ($signature->getSize() === 0) {
174
            return false;
175 30
        }
176 6
177
        $binary = $signature->getBinary();
178
        $nHashType = ord(substr($binary, -1)) & (~(SignatureHashInterface::SIGHASH_ANYONECANPAY));
179 24
180 24
        $math = $this->math;
181 24
        return ! ($math->cmp($nHashType, SignatureHashInterface::SIGHASH_ALL) < 0 || $math->cmp($nHashType, SignatureHashInterface::SIGHASH_SINGLE) > 0);
182 24
    }
183
184 24
    /**
185
     * @param BufferInterface $signature
186
     * @param int $flags
187
     * @return $this
188
     * @throws \BitWasp\Bitcoin\Exceptions\ScriptRuntimeException
189
     */
190
    public function checkSignatureEncoding(BufferInterface $signature, $flags)
191
    {
192
        if ($signature->getSize() === 0) {
193
            return $this;
194 36
        }
195
196 36
        if ($flags & (self::VERIFY_DERSIG | self::VERIFY_LOW_S | self::VERIFY_STRICTENC) && !$this->isValidSignatureEncoding($signature)) {
197 6
            throw new ScriptRuntimeException(self::VERIFY_DERSIG, 'Signature with incorrect encoding');
198
        } else if ($flags & self::VERIFY_LOW_S && !$this->isLowDerSignature($signature)) {
199
            throw new ScriptRuntimeException(self::VERIFY_LOW_S, 'Signature s element was not low');
200 30
        } else if ($flags & self::VERIFY_STRICTENC && !$this->isDefinedHashtypeSignature($signature)) {
201 30
            throw new ScriptRuntimeException(self::VERIFY_STRICTENC, 'Signature with invalid hashtype');
202
        }
203 30
204 30
        return $this;
205
    }
206
207
    /**
208
     * @param BufferInterface $publicKey
209
     * @param int $flags
210
     * @return $this
211
     * @throws \Exception
212 60
     */
213
    public function checkPublicKeyEncoding(BufferInterface $publicKey, $flags)
214 60
    {
215 6
        if ($flags & self::VERIFY_STRICTENC && !PublicKey::isCompressedOrUncompressed($publicKey)) {
216
            throw new ScriptRuntimeException(self::VERIFY_STRICTENC, 'Public key with incorrect encoding');
217
        }
218 54
219 12
        return $this;
220 42
    }
221 6
222 36
    /**
223 6
     * @param int $opCode
224
     * @param BufferInterface $pushData
225
     * @return bool
226 30
     * @throws \Exception
227
     */
228
    public function checkMinimalPush($opCode, BufferInterface $pushData)
229
    {
230
        $pushSize = $pushData->getSize();
231
        $binary = $pushData->getBinary();
232
233
        if ($pushSize === 0) {
234 24
            return $opCode === Opcodes::OP_0;
235
        } elseif ($pushSize === 1) {
236 24
            $first = ord($binary[0]);
237 6
            if ($first >= 1 && $first <= 16) {
238
                return $opCode === (Opcodes::OP_1 + ($first - 1));
239
            } elseif ($first === 0x81) {
240 18
                return $opCode === Opcodes::OP_1NEGATE;
241
            }
242
        } elseif ($pushSize <= 75) {
243
            return $opCode === $pushSize;
244
        } elseif ($pushSize <= 255) {
245
            return $opCode === Opcodes::OP_PUSHDATA1;
246
        } elseif ($pushSize <= 65535) {
247
            return $opCode === Opcodes::OP_PUSHDATA2;
248
        }
249 6
250
        return true;
251 6
    }
252 6
253
    /**
254 6
     * @return $this
255
     * @throws \Exception
256 6
     */
257
    private function checkOpcodeCount()
258
    {
259
        if ($this->math->cmp($this->opCount, 201) > 0) {
260
            throw new \RuntimeException('Error: Script op code count');
261
        }
262
263 6
        return $this;
264 6
    }
265
266
    /**
267
     * @param ScriptInterface $script
268
     * @param BufferInterface $sigBuf
269
     * @param BufferInterface $keyBuf
270
     * @param int $flags
271
     * @return bool
272
     * @throws ScriptRuntimeException
273
     * @throws \Exception
274
     */
275
    private function checkSig(ScriptInterface $script, BufferInterface $sigBuf, BufferInterface $keyBuf, $flags)
276
    {
277
        $this
278 672
            ->checkSignatureEncoding($sigBuf, $flags)
279
            ->checkPublicKeyEncoding($keyBuf, $flags);
280 672
281 6
        try {
282
            $txSignature = TransactionSignatureFactory::fromHex($sigBuf->getHex());
283
            $publicKey = PublicKeyFactory::fromHex($keyBuf->getHex());
284 672
285
            return $this->ecAdapter->verify(
286
                $this->transaction->getSignatureHash()->calculate($script, $this->inputToSign, $txSignature->getHashType()),
287
                $publicKey,
288
                $txSignature->getSignature()
289
            );
290
        } catch (\Exception $e) {
291
            return false;
292
        }
293
    }
294
295 18
    /**
296
     * @param int $txLockTime
297 18
     * @param int $nThreshold
298 18
     * @param \BitWasp\Bitcoin\Script\Interpreter\Number $lockTime
299 12
     * @return bool
300
     */
301
    private function verifyLockTime($txLockTime, $nThreshold, \BitWasp\Bitcoin\Script\Interpreter\Number $lockTime)
302 12
    {
303 12
        $nTime = $lockTime->getInt();
304
        if (($this->math->cmp($txLockTime, $nThreshold) < 0 && $this->math->cmp($nTime, $nThreshold) < 0) ||
305 12
            ($this->math->cmp($txLockTime, $nThreshold) >= 0 && $this->math->cmp($nTime, $nThreshold) >= 0)
306 12
        ) {
307 12
            return false;
308 12
        }
309 12
310
        return $this->math->cmp($nTime, $txLockTime) >= 0;
311
    }
312
313
    /**
314
     * @param \BitWasp\Bitcoin\Script\Interpreter\Number $lockTime
315
     * @return bool
316
     */
317
    private function checkLockTime(\BitWasp\Bitcoin\Script\Interpreter\Number $lockTime)
318
    {
319
        if ($this->transaction->getInput($this->inputToSign)->isFinal()) {
320
            return false;
321
        }
322
323
        return $this->verifyLockTime($this->transaction->getLockTime(), Locktime::BLOCK_MAX, $lockTime);
324
    }
325
326
    /**
327
     * @param WitnessProgram $witnessProgram
328
     * @param ScriptWitness $witness
329
     * @param int $flags
330
     * @return bool
331
     */
332
    private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitness $witness, $flags)
333
    {
334
        $version = $witnessProgram->getVersion();
335
        if ($version === 0) {
336
            $scriptPubKey = new Script($witnessProgram->getProgram());
337
            $stackValues = $witness->all();
338
339
        } elseif ($version === 1) {
340
            $program = $witnessProgram->getProgram();
341
            if ($program->getSize() !== 32) { // SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH
342
                return false;
343
            }
344
345
            $count = count($witness);
346
            if ($count === 0) { // SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY
347
                return false;
348
            }
349
350
            $scriptPubKey = new Script($witness[$count - 1]);
351
            $stackValues = $witness->slice(0, -1);
352
            $hashScriptPubKey = Hash::sha256($witness[$count - 1]);
353
            if ($hashScriptPubKey->getBinary() == $program) {
354
                return false;
355
            }
356
        } elseif ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
357
            return false;
358
        } else {
359
            return false;
360
        }
361
362
        $mainStack = new Stack();
363
        foreach ($stackValues as $value) {
364
            $mainStack->push($value);
365
        }
366
367
        if (!$this->evaluate($scriptPubKey, $mainStack, $flags)) {
368
            return false;
369
        }
370
371
        if ($mainStack->count() !== 1) {
372
            return false;
373
        }
374
375
        if (!$this->castToBool($mainStack[count($mainStack) - 1])) {
376 684
            return false;
377
        }
378 684
379 684
        return true;
380
    }
381
382
    /**
383 684
     * @param \BitWasp\Bitcoin\Script\Interpreter\Number $sequence
384 684
     * @return bool
385 156
     */
386 156
    private function checkSequence(\BitWasp\Bitcoin\Script\Interpreter\Number $sequence)
387
    {
388 684
        $txSequence = $this->transaction->getInput($this->inputToSign)->getSequence();
389 258
        if ($this->transaction->getVersion() < 2) {
390
            return false;
391
        }
392 426
393
        if ($this->math->cmp($this->math->bitwiseAnd($txSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), 0) !== 0) {
394
            return 0;
395
        }
396 426
397
        $mask = $this->math->bitwiseOr(TransactionInputInterface::SEQUENCE_LOCKTIME_TYPE_FLAG, TransactionInputInterface::SEQUENCE_LOCKTIME_MASK);
398
        return $this->verifyLockTime(
399
            $this->math->bitwiseAnd($txSequence, $mask),
400 426
            TransactionInputInterface::SEQUENCE_LOCKTIME_TYPE_FLAG,
401 6
            Number::int($this->math->bitwiseAnd($sequence->getInt(), $mask))
402
        );
403
    }
404
405
    /**
406 6
     * @param ScriptInterface $scriptSig
407 6
     * @param ScriptInterface $scriptPubKey
408
     * @param int $flags
409
     * @param ScriptWitness $witness
410
     * @param int $nInputToSign
411
     * @return bool
412 6
     */
413 6
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $nInputToSign, $flags, ScriptWitness $witness = null)
414
    {
415 6
        static $emptyWitness = null;
416
        if ($emptyWitness === null) {
417
            $emptyWitness = new ScriptWitness([]);
418 6
        }
419
420 426
        $witness = $witness ?: $emptyWitness;
421
422
        $this->inputToSign = $nInputToSign;
423
424
        $stack = new Stack();
425
        if (!$this->evaluate($scriptSig, $stack, $flags)) {
426 684
            return false;
427
        }
428 684
429 684
        $stackCopy = new Stack;
430 684
        if ($flags & self::VERIFY_P2SH) {
431
            $stackCopy = clone $stack;
432
        }
433
434
        if (!$this->evaluate($scriptPubKey, $stack, $flags)) {
435 684
            return false;
436
        }
437
438
        if ($stack->isEmpty()) {
439
            return false;
440
        }
441
442 684
        if (false === $this->castToBool($stack[-1])) {
443
            return false;
444 684
        }
445 684
446 684
        $program = null;
447 684
        if ($flags & self::VERIFY_WITNESS) {
448
            if ($scriptPubKey->isWitness($program)) {
0 ignored issues
show
Documentation introduced by
$program is of type null, but the function expects a object<BitWasp\Bitcoin\Script\WitnessProgram>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
449 684
                /** @var WitnessProgram $program */
450
                if ($scriptSig->getBuffer()->getSize() !== 0) {
451
                    return false;
452
                }
453
454 684
                if (!$this->verifyWitnessProgram($program, $witness, $flags)) {
0 ignored issues
show
Bug introduced by
It seems like $witness defined by $witness ?: $emptyWitness on line 420 can be null; however, BitWasp\Bitcoin\Script\I...:verifyWitnessProgram() 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...
455 684
                    return false;
456 684
                }
457 684
458
                $stack->resize(1);
459
            }
460 684
        }
461
462
        if ($flags & self::VERIFY_P2SH && (new OutputClassifier($scriptPubKey))->isPayToScriptHash()) {
463
            if (!$scriptSig->isPushOnly()) {
464
                return false;
465 684
            }
466 672
467 672
            // Restore mainStack to how it was after evaluating scriptSig
468
            $stack = $stackCopy;
469 684
            if ($stack->isEmpty()) {
470 12
                return false;
471
            }
472
473 684
            // Load redeemscript as the scriptPubKey
474
            $scriptPubKey = new Script($stack->bottom());
475 426
            $stack->pop();
476 6
477
            if (!$this->evaluate($scriptPubKey, $stack, $flags)) {
478
                return false;
479 420
            }
480
481 678
            if ($stack->isEmpty()) {
482
                return false;
483
            }
484 660
485 660
            if (!$this->castToBool($stack->bottom())) {
486 660
                return false;
487 660
            }
488 660
489 660
            if ($flags & self::VERIFY_WITNESS) {
490 660
                if ($scriptPubKey->isWitness($program)) {
491 660
                    if ($scriptSig != (ScriptFactory::create()->push($scriptPubKey->getBuffer())->getScript())) {
492 660
                        return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
493 660
                    }
494 660
495 660
                    if (!$this->verifyWitnessProgram($program, $witness, $flags)) {
0 ignored issues
show
Bug introduced by
It seems like $program defined by null on line 446 can be null; however, BitWasp\Bitcoin\Script\I...:verifyWitnessProgram() 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...
Bug introduced by
It seems like $witness defined by $witness ?: $emptyWitness on line 420 can be null; however, BitWasp\Bitcoin\Script\I...:verifyWitnessProgram() 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...
496 660
                        return false;
497 660
                    }
498 660
499 660
                    $stack->resize(1);
500 660
                }
501 192
502 192
            }
503 192
        }
504
505 660
        if ($flags & self::VERIFY_CLEAN_STACK != 0) {
506
            if (!($flags & self::VERIFY_P2SH != 0 && $flags & self::VERIFY_WITNESS != 0)) {
507
                return false; // implied flags required
508
            }
509
510
            if (count($stack) != 1) {
511
                return false; // Cleanstack
512
            }
513
        }
514
515
        if ($flags & self::VERIFY_WITNESS) {
516
            if (!$flags & self::VERIFY_P2SH) {
517
                return false; //
518
            }
519
520
            if ($program === null && !$witness->isNull()) {
521
                return false; // SCRIPT_ERR_WITNESS_UNEXPECTED
522
            }
523
        }
524 660
525
        return true;
526
    }
527
528
    /**
529
     * @param Stack $vfStack
530
     * @return bool
531
     */
532
    private function checkExec(Stack $vfStack)
533
    {
534
        $c = 0;
535
        $len = $vfStack->end();
536
        for ($i = 0; $i < $len; $i++) {
537
            if ($vfStack[0 - $len - $i] === true) {
538
                $c++;
539
            }
540
        }
541
        return !(bool)$c;
542
    }
543
544
    /**
545
     * @param ScriptInterface $script
546
     * @param Stack $mainStack
547
     * @param int $flags
548
     * @return bool
549
     */
550
    public function evaluate(ScriptInterface $script, Stack $mainStack, $flags)
551 660
    {
552 660
        $math = $this->math;
553 660
        $this->hashStartPos = 0;
554 660
        $this->opCount = 0;
555 660
        $altStack = new Stack();
556 660
        $vfStack = new Stack();
557 660
        $parser = $script->getScriptParser();
558 660
559 6
        if ($script->getBuffer()->getSize() > 10000) {
560 6
            return false;
561
        }
562
563
        try {
564 654
            foreach ($parser as $operation) {
565 12
                $opCode = $operation->getOp();
566
                $pushData = $operation->getData();
567 642
                $fExec = $this->checkExec($vfStack);
568 642
569
                // If pushdata was written to,
570 18
                if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
571 18
                    throw new \RuntimeException('Error - push size');
572 18
                }
573 6
574
                // OP_RESERVED should not count towards opCount
575
                if ($opCode > Opcodes::OP_16 && ++$this->opCount) {
576 12
                    $this->checkOpcodeCount();
577 12
                }
578 12
579 6
                if (in_array($opCode, $this->disabledOps, true)) {
580 6
                    throw new \RuntimeException('Disabled Opcode');
581 12
                }
582 12
583 12
                if ($fExec && $operation->isPush()) {
584
                    // In range of a pushdata opcode
585 636
                    if ($flags & self::VERIFY_MINIMALDATA && !$this->checkMinimalPush($opCode, $pushData)) {
586 18
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
587 6
                    }
588
589 12
                    $mainStack->push($pushData);
590 12
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
591
                } elseif ($fExec || ($opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF)) {
592 630
                    // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n";
593 18
                    switch ($opCode) {
594 6
                        case Opcodes::OP_1NEGATE:
595
                        case Opcodes::OP_1:
596 12
                        case Opcodes::OP_2:
597
                        case Opcodes::OP_3:
598 624
                        case Opcodes::OP_4:
599 24
                        case Opcodes::OP_5:
600 6
                        case Opcodes::OP_6:
601
                        case Opcodes::OP_7:
602 18
                        case Opcodes::OP_8:
603 18
                        case Opcodes::OP_9:
604 6
                        case Opcodes::OP_10:
605
                        case Opcodes::OP_11:
606 12
                        case Opcodes::OP_12:
607 12
                        case Opcodes::OP_13:
608
                        case Opcodes::OP_14:
609 600
                        case Opcodes::OP_15:
610
                        case Opcodes::OP_16:
611 12
                            $num = decodeOpN($opCode);
612
                            $mainStack->push(Number::int($num)->getBuffer());
613 588
                            break;
614 18
615 6
                        case Opcodes::OP_CHECKLOCKTIMEVERIFY:
616
                            if (!$flags & self::VERIFY_CHECKLOCKTIMEVERIFY) {
617 12
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
618 12
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
619
                                }
620 582
                                break;
621 12
                            }
622 6
623
                            if ($mainStack->isEmpty()) {
624 6
                                throw new \RuntimeException('Invalid stack operation - CLTV');
625 6
                            }
626
627 576
                            $lockTime = Number::buffer($mainStack[-1], $flags & self::VERIFY_MINIMALDATA, 5, $math);
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
628
                            if (!$this->checkLockTime($lockTime)) {
629 12
                                throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
630 6
                            }
631
632 6
                            break;
633 6
634 6
                        case Opcodes::OP_CHECKSEQUENCEVERIFY:
635 6
                            if (!$flags & self::VERIFY_CHECKSEQUENCEVERIFY) {
636 6
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
637
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
638 570
                                }
639 72
                                break;
640 72
                            }
641 24
642 24
                            if ($mainStack->isEmpty()) {
643 54
                                throw new \RuntimeException('Invalid stack operation - CSV');
644
                            }
645
646 72
                            $sequence = Number::buffer($mainStack[-1], $flags & self::VERIFY_MINIMALDATA, 5, $math);
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
647 72
                            $nSequence = $sequence->getInt();
648
                            if ($math->cmp($nSequence, 0) < 0) {
649 570
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
650 6
                            }
651 6
652
                            if ($math->cmp($math->bitwiseAnd($nSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), '0') !== 0) {
653
                                break;
654
                            }
655
656 564
                            if (!$this->checkSequence($sequence)) {
657 18
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied locktime');
658 6
                            }
659
                            break;
660 12
661 12
                        case Opcodes::OP_NOP1:
662 12
                        case Opcodes::OP_NOP4:
663
                        case Opcodes::OP_NOP5:
664 558
                        case Opcodes::OP_NOP6:
665 18
                        case Opcodes::OP_NOP7:
666 6
                        case Opcodes::OP_NOP8:
667
                        case Opcodes::OP_NOP9:
668 12
                        case Opcodes::OP_NOP10:
669 12
                            if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
670
                                throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
671 552
                            }
672 18
                            break;
673 6
674
                        case Opcodes::OP_NOP:
675 12
                            break;
676 12
677 12
                        case Opcodes::OP_IF:
678
                        case Opcodes::OP_NOTIF:
679 546
                            // <expression> if [statements] [else [statements]] endif
680 12
                            $value = false;
681 6
                            if ($fExec) {
682
                                if ($mainStack->isEmpty()) {
683 6
                                    throw new \RuntimeException('Unbalanced conditional');
684 6
                                }
685 6
                                // todo
686
                                $buffer = Number::buffer($mainStack->pop(), $flags & self::VERIFY_MINIMALDATA)->getBuffer();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
687 540
                                $value = $this->castToBool($buffer);
688 12
                                if ($opCode === Opcodes::OP_NOTIF) {
689 6
                                    $value = !$value;
690
                                }
691 6
                            }
692 6
                            $vfStack->push($value ? $this->vchTrue : $this->vchFalse);
693
                            break;
694 534
695 12
                        case Opcodes::OP_ELSE:
696 6
                            if ($vfStack->isEmpty()) {
697
                                throw new \RuntimeException('Unbalanced conditional');
698 6
                            }
699 6
                            $vfStack[-1] = !$vfStack->end() ? $this->vchTrue : $this->vchFalse;
700
                            break;
701
702 522
                        case Opcodes::OP_ENDIF:
703 522
                            if ($vfStack->isEmpty()) {
704 24
                                throw new \RuntimeException('Unbalanced conditional');
705 6
                            }
706
                            break;
707
708 18
                        case Opcodes::OP_VERIFY:
709 18
                            if ($mainStack->isEmpty()) {
710 18
                                throw new \RuntimeException('Invalid stack operation');
711 6
                            }
712
                            $value = $this->castToBool($mainStack[-1]);
713
                            if (!$value) {
714 12
                                throw new \RuntimeException('Error: verify');
715 12
                            }
716 12
                            $mainStack->pop();
717 6
                            break;
718 6
719 12
                        case Opcodes::OP_RESERVED:
720 12
                            // todo
721
                            break;
722 510
723 12
                        case Opcodes::OP_TOALTSTACK:
724 6
                            if ($mainStack->isEmpty()) {
725
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
726 6
                            }
727 6
                            $altStack->push($mainStack->pop());
728 6
                            break;
729
730 504
                        case Opcodes::OP_FROMALTSTACK:
731 18
                            if ($altStack->isEmpty()) {
732 6
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
733
                            }
734 12
                            $mainStack->push($altStack->pop());
735 12
                            break;
736 12
737 12
                        case Opcodes::OP_IFDUP:
738 12
                            // If top value not zero, duplicate it.
739
                            if ($mainStack->isEmpty()) {
740 498
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
741 18
                            }
742 6
                            $vch = $mainStack[-1];
743
                            if ($this->castToBool($vch)) {
744 12
                                $mainStack->push($vch);
745 12
                            }
746 12
                            break;
747 12
748 12
                        case Opcodes::OP_DEPTH:
749 12
                            $num = count($mainStack);
750 12
                            if ($num === 0) {
751
                                $depth = $this->vchFalse;
752 492
                            } else {
753 18
                                $depth = Number::int($num)->getBuffer();
754 6
                            }
755
756 12
                            $mainStack->push($depth);
757 12
                            break;
758 12
759 12
                        case Opcodes::OP_DROP:
760 12
                            if ($mainStack->isEmpty()) {
761
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
762 486
                            }
763 12
                            $mainStack->pop();
764 6
                            break;
765
766 6
                        case Opcodes::OP_DUP:
767 6
                            if ($mainStack->isEmpty()) {
768 6
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
769 6
                            }
770 6
                            $vch = $mainStack[-1];
771 6
                            $mainStack->push($vch);
772
                            break;
773 480
774 12
                        case Opcodes::OP_NIP:
775 6
                            if (count($mainStack) < 2) {
776
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
777 6
                            }
778 6
                            unset($mainStack[-2]);
779 6
                            break;
780
781 474
                        case Opcodes::OP_OVER:
782 12
                            if (count($mainStack) < 2) {
783 6
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
784
                            }
785
                            $vch = $mainStack[-2];
786 6
                            $mainStack->push($vch);
787 6
                            break;
788 6
789
                        case Opcodes::OP_ROT:
790 468
                            if (count($mainStack) < 3) {
791 468
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
792 396
                            }
793 6
                            $mainStack->swap(-3, -2);
794
                            $mainStack->swap(-2, -1);
795 390
                            break;
796 390
797
                        case Opcodes::OP_SWAP:
798 390
                            if (count($mainStack) < 2) {
799
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
800 390
                            }
801 390
                            $mainStack->swap(-2, -1);
802 390
                            break;
803 390
804 114
                        case Opcodes::OP_TUCK:
805 102
                            if (count($mainStack) < 2) {
806 102
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
807 12
                            }
808
                            $vch = $mainStack[-1];
809 102
                            $mainStack->add(count($mainStack) - 1 - 2, $vch);
810
                            break;
811 378
812
                        case Opcodes::OP_PICK:
813
                        case Opcodes::OP_ROLL:
814 282
                            if (count($mainStack) < 2) {
815 48
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
816 6
                            }
817
818
                            $n = Number::buffer($mainStack[-1], $flags & self::VERIFY_MINIMALDATA, 4)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
819 42
                            $mainStack->pop();
820
                            if ($math->cmp($n, 0) < 0 || $math->cmp($n, count($mainStack)) >= 0) {
821 42
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
822 6
                            }
823 42
824 6
                            $pos = (int) $math->sub($math->sub(0, $n), 1);
825 36
                            $vch = $mainStack[$pos];
826
                            if ($opCode === Opcodes::OP_ROLL) {
827 30
                                unset($mainStack[$pos]);
828
                            }
829 30
                            $mainStack->push($vch);
830
                            break;
831
832
                        case Opcodes::OP_2DROP:
833 30
                            if (count($mainStack) < 2) {
834 18
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
835 18
                            }
836
                            $mainStack->pop();
837 12
                            $mainStack->pop();
838
                            break;
839
840 42
                        case Opcodes::OP_2DUP:
841
                            if (count($mainStack) < 2) {
842 42
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
843
                            }
844 42
                            $string1 = $mainStack[-2];
845 42
                            $string2 = $mainStack[-1];
846
                            $mainStack->push($string1);
847 234
                            $mainStack->push($string2);
848 150
                            break;
849 24
850
                        case Opcodes::OP_3DUP:
851
                            if (count($mainStack) < 3) {
852 126
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
853 126
                            }
854
                            $string1 = $mainStack[-3];
855 126
                            $string2 = $mainStack[-2];
856 6
                            $string3 = $mainStack[-1];
857 126
                            $mainStack->push($string1);
858 6
                            $mainStack->push($string2);
859 120
                            $mainStack->push($string3);
860 12
                            break;
861 114
862 18
                        case Opcodes::OP_2OVER:
863 102
                            if (count($mainStack) < 4) {
864 18
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
865 84
                            }
866
                            $string1 = $mainStack[-4];
867 66
                            $string2 = $mainStack[-3];
868 6
                            $mainStack->push($string1);
869 66
                            $mainStack->push($string2);
870 12
                            break;
871 60
872 12
                        case Opcodes::OP_2ROT:
873 48
                            if (count($mainStack) < 6) {
874 12
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
875 36
                            }
876 12
                            $string1 = $mainStack[-6];
877 24
                            $string2 = $mainStack[-5];
878 6
                            unset($mainStack[-6], $mainStack[-5]);
879 6
                            $mainStack->push($string1);
880 6
                            $mainStack->push($string2);
881
                            break;
882
883 126
                        case Opcodes::OP_2SWAP:
884 126
                            if (count($mainStack) < 4) {
885 126
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
886 126
                            }
887
                            $mainStack->swap(-3, -1);
888 126
                            $mainStack->swap(-4, -2);
889
                            break;
890
891
                        case Opcodes::OP_SIZE:
892
                            if ($mainStack->isEmpty()) {
893
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
894
                            }
895 126
                            // todo: Int sizes?
896
                            $vch = $mainStack[-1];
897 84
                            $mainStack->push(Number::int($vch->getSize())->getBuffer());
898 12
                            break;
899 6
900
                        case Opcodes::OP_EQUAL:
901
                        case Opcodes::OP_EQUALVERIFY:
902 6
                            if (count($mainStack) < 2) {
903 6
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
904 6
                            }
905
                            $vch1 = $mainStack[-2];
906 6
                            $vch2 = $mainStack[-1];
907 6
908 6
                            $equal = ($vch1->getBinary() === $vch2->getBinary());
909 6
910 6
                            $mainStack->pop();
911 6
                            $mainStack->pop();
912
                            $mainStack->push(($equal ? $this->vchTrue : $this->vchFalse));
913
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
914 72
                                if ($equal) {
915 72
                                    $mainStack->pop();
916 72
                                } else {
917 72
                                    throw new \RuntimeException('Error EQUALVERIFY');
918 72
                                }
919 48
                            }
920 6
921
                            break;
922
923 42
                        // Arithmetic operations
924 42
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
925 6
                            if ($mainStack->isEmpty()) {
926 42
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
927 6
                            }
928 36
929 6
                            $num = Number::buffer($mainStack[-1], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
930 30
931 18
                            if ($opCode === Opcodes::OP_1ADD) {
932 18
                                $num = $math->add($num, '1');
933 6
                            } elseif ($opCode === Opcodes::OP_1SUB) {
934
                                $num = $math->sub($num, '1');
935
                            } elseif ($opCode === Opcodes::OP_2MUL) {
936 42
                                $num = $math->mul(2, $num);
937 42
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
938 42
                                $num = $math->sub(0, $num);
939
                            } elseif ($opCode === Opcodes::OP_ABS) {
940 30
                                if ($math->cmp($num, '0') < 0) {
941
                                    $num = $math->sub(0, $num);
942
                                }
943
                            } elseif ($opCode === Opcodes::OP_NOT) {
944 30
                                $num = (int) $math->cmp($num, '0') === 0;
945 30
                            } else {
946 24
                                // is OP_0NOTEQUAL
947 6
                                $num = (int) ($math->cmp($num, '0') !== 0);
948
                            }
949
950 18
                            $mainStack->pop();
951 18
952
                            $buffer = Number::int($num)->getBuffer();
953 18
954 18
                            $mainStack->push($buffer);
955
                            break;
956 12
957 12
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
958 12
                            if (count($mainStack) < 2) {
959
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
960 12
                            }
961
962
                            $num1 = Number::buffer($mainStack[-2], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
963
                            $num2 = Number::buffer($mainStack[-1], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
964
965
                            if ($opCode === Opcodes::OP_ADD) {
966
                                $num = $math->add($num1, $num2);
967 12
                            } else if ($opCode === Opcodes::OP_SUB) {
968
                                $num = $math->sub($num1, $num2);
969 6
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
970 6
                                $num = $math->cmp($num1, $this->int0->getInt()) !== 0 && $math->cmp($num2, $this->int0->getInt()) !== 0;
971
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
972
                                $num = $math->cmp($num1, $this->int0->getInt()) !== 0 || $math->cmp($num2, $this->int0->getInt()) !== 0;
973
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
974
                                $num = $math->cmp($num1, $num2) === 0;
975
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
976
                                $num = $math->cmp($num1, $num2) === 0;
977
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
978
                                $num = $math->cmp($num1, $num2) !== 0;
979
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) {
980
                                $num = $math->cmp($num1, $num2) < 0;
981
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
982
                                $num = $math->cmp($num1, $num2) > 0;
983
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
984
                                $num = $math->cmp($num1, $num2) <= 0;
985
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
986
                                $num = $math->cmp($num1, $num2) >= 0;
987
                            } elseif ($opCode === Opcodes::OP_MIN) {
988
                                $num = ($math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
989
                            } else {
990
                                $num = ($math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
991
                            }
992
993
                            $mainStack->pop();
994
                            $mainStack->pop();
995
                            $buffer = Number::int($num)->getBuffer();
996
                            $mainStack->push($buffer);
997
998
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
999
                                if ($this->castToBool($mainStack[-1])) {
1000
                                    $mainStack->pop();
1001
                                } else {
1002
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
1003
                                }
1004
                            }
1005
                            break;
1006
1007
                        case Opcodes::OP_WITHIN:
1008
                            if (count($mainStack) < 3) {
1009
                                throw new \RuntimeException('Invalid stack operation');
1010
                            }
1011
1012
                            $num1 = Number::buffer($mainStack[-3], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1013
                            $num2 = Number::buffer($mainStack[-2], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1014
                            $num3 = Number::buffer($mainStack[-1], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1015
1016
                            $value = $math->cmp($num2, $num1) <= 0 && $math->cmp($num1, $num3) < 0;
1017
                            $mainStack->pop();
1018
                            $mainStack->pop();
1019
                            $mainStack->pop();
1020
                            $mainStack->push($value ? $this->vchFalse : $this->vchTrue);
1021
                            break;
1022
1023
                        // Hash operation
1024
                        case Opcodes::OP_RIPEMD160:
1025
                        case Opcodes::OP_SHA1:
1026
                        case Opcodes::OP_SHA256:
1027
                        case Opcodes::OP_HASH160:
1028
                        case Opcodes::OP_HASH256:
1029
                            if ($mainStack->isEmpty()) {
1030
                                throw new \RuntimeException('Invalid stack operation');
1031
                            }
1032
1033
                            $buffer = $mainStack[-1];
1034
                            if ($opCode === Opcodes::OP_RIPEMD160) {
1035
                                $hash = Hash::ripemd160($buffer);
1036
                            } elseif ($opCode === Opcodes::OP_SHA1) {
1037
                                $hash = Hash::sha1($buffer);
1038
                            } elseif ($opCode === Opcodes::OP_SHA256) {
1039
                                $hash = Hash::sha256($buffer);
1040
                            } elseif ($opCode === Opcodes::OP_HASH160) {
1041
                                $hash = Hash::sha256ripe160($buffer);
1042
                            } else {
1043
                                $hash = Hash::sha256d($buffer);
1044
                            }
1045
1046
                            $mainStack->pop();
1047
                            $mainStack->push($hash);
1048
                            break;
1049
1050
                        case Opcodes::OP_CODESEPARATOR:
1051
                            $this->hashStartPos = $parser->getPosition();
1052
                            break;
1053
1054
                        case Opcodes::OP_CHECKSIG:
1055
                        case Opcodes::OP_CHECKSIGVERIFY:
1056
                            if (count($mainStack) < 2) {
1057
                                throw new \RuntimeException('Invalid stack operation');
1058 6
                            }
1059 6
1060 6
                            $vchPubKey = $mainStack[-1];
1061
                            $vchSig = $mainStack[-2];
1062 426
1063
                            $scriptCode = new Script($script->getBuffer()->slice($this->hashStartPos));
1064
                            $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey, $flags);
1065 426
1066 684
                            $mainStack->pop();
1067
                            $mainStack->pop();
1068 684
                            $mainStack->push($success ? $this->vchTrue : $this->vchFalse);
1069
1070
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
1071
                                if ($success) {
1072 684
                                    $mainStack->pop();
1073 258
                                } else {
1074
                                    throw new \RuntimeException('Checksig verify');
1075
                                }
1076 18
                            }
1077 240
                            break;
1078
1079 240
                        case Opcodes::OP_CHECKMULTISIG:
1080
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
1081
                            $i = 1;
1082
                            if (count($mainStack) < $i) {
1083
                                throw new \RuntimeException('Invalid stack operation');
1084
                            }
1085
1086
                            $keyCount = Number::buffer($mainStack[-$i], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1087
                            if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
1088
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
1089
                            }
1090
                            $this->opCount += $keyCount;
1091
                            $this->checkOpcodeCount();
1092
1093
                            // Extract positions of the keys, and signatures, from the stack.
1094
                            $ikey = ++$i;
1095
                            $i += $keyCount; /** @var int $i */
1096
                            if (count($mainStack) < $i) {
1097
                                throw new \RuntimeException('Invalid stack operation');
1098
                            }
1099
1100
                            $sigCount = Number::buffer($mainStack[-$i], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1101
                            if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
1102
                                throw new \RuntimeException('Invalid Signature count');
1103
                            }
1104
                            $isig = ++$i;
1105
                            $i += $sigCount;
1106
1107
                            // Extract the script since the last OP_CODESEPARATOR
1108
                            $scriptCode = new Script($script->getBuffer()->slice($this->hashStartPos));
1109
1110
                            $fSuccess = true;
1111
                            while ($fSuccess && $sigCount > 0) {
1112
                                // Fetch the signature and public key
1113
                                $sig = $mainStack[-$isig];
1114
                                $pubkey = $mainStack[-$ikey];
1115
1116
                                // Erase the signature and public key.
1117
                                unset($mainStack[-$isig], $mainStack[-$ikey]);
1118
1119
                                // Decrement $i, since we are consuming stack values.
1120
                                $i -= 2;
1121
1122
                                if ($this->checkSig($scriptCode, $sig, $pubkey, $flags)) {
1123
                                    $isig++;
1124
                                    $sigCount--;
1125
                                }
1126
1127
                                $ikey++;
1128
                                $keyCount--;
1129
1130
                                // If there are more signatures left than keys left,
1131
                                // then too many signatures have failed. Exit early,
1132
                                // without checking any further signatures.
1133
                                if ($sigCount > $keyCount) {
1134
                                    $fSuccess = false;
1135
                                }
1136
                            }
1137
1138
                            while ($i-- > 1) {
1139
                                $mainStack->pop();
1140
                            }
1141
1142
                            // A bug causes CHECKMULTISIG to consume one extra argument
1143
                            // whose contents were not checked in any way.
1144
                            //
1145
                            // Unfortunately this is a potential source of mutability,
1146
                            // so optionally verify it is exactly equal to zero prior
1147
                            // to removing it from the stack.
1148
                            if ($mainStack->isEmpty()) {
1149
                                throw new \RuntimeException('Invalid stack operation');
1150
                            }
1151
1152
                            if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) {
1153
                                throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
1154
                            }
1155
1156
                            $mainStack->pop();
1157
                            $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
1158
1159
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
1160
                                if ($fSuccess) {
1161
                                    $mainStack->pop();
1162
                                } else {
1163
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
1164
                                }
1165
                            }
1166
                            break;
1167
1168
                        default:
1169
                            throw new \RuntimeException('Opcode not found');
1170
                    }
1171
1172
                    if (count($mainStack) + count($altStack) > 1000) {
1173
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
1174
                    }
1175
                }
1176
            }
1177
1178
            if (!$vfStack->end() === 0) {
1179
                throw new \RuntimeException('Unbalanced conditional at script end');
1180
            }
1181
1182
            return true;
1183
        } catch (ScriptRuntimeException $e) {
1184
            // echo "\n Runtime: " . $e->getMessage() . "\n";
1185
            // Failure due to script tags, can access flag: $e->getFailureFlag()
1186
            return false;
1187
        } catch (\Exception $e) {
1188
            // echo "\n General: " . $e->getMessage() ;
1189
            return false;
1190
        }
1191
    }
1192
}
1193