Completed
Pull Request — master (#241)
by thomas
133:23 queued 63:06
created

Interpreter::verify()   F

Complexity

Conditions 33
Paths 1176

Size

Total Lines 120
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 211.4292

Importance

Changes 11
Bugs 1 Features 2
Metric Value
c 11
b 1
f 2
dl 0
loc 120
ccs 24
cts 53
cp 0.4528
rs 2
cc 33
eloc 59
nc 1176
nop 5
crap 211.4292

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Interpreter;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
6
use BitWasp\Bitcoin\Crypto\Hash;
7
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
8
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey;
10
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
11
use BitWasp\Bitcoin\Script\Opcodes;
12
use BitWasp\Bitcoin\Script\Script;
13
use BitWasp\Bitcoin\Script\ScriptFactory;
14
use BitWasp\Bitcoin\Script\ScriptInterface;
15
use BitWasp\Bitcoin\Script\ScriptWitness;
16
use BitWasp\Bitcoin\Script\WitnessProgram;
17
use BitWasp\Bitcoin\Signature\TransactionSignature;
18
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
19
use BitWasp\Bitcoin\Transaction\TransactionInputInterface;
20
use BitWasp\Bitcoin\Transaction\TransactionInterface;
21
use BitWasp\Buffertools\Buffer;
22
use BitWasp\Buffertools\BufferInterface;
23
24
class Interpreter implements InterpreterInterface
25
{
26
    /**
27
     * Position of OP_CODESEPARATOR, for calculating SigHash
28
     * @var int
29
     */
30
    private $hashStartPos;
31
32
    /**
33
     * @var int
34
     */
35
    private $opCount;
36
37
    /**
38
     * @var EcAdapterInterface
39
     */
40
    private $ecAdapter;
41
42
    /**
43
     * @var \BitWasp\Bitcoin\Math\Math
44
     */
45
    private $math;
46
47
    /**
48
     * @var Buffer
49
     */
50
    private $vchFalse;
51
52
    /**
53
     * @var Buffer
54
     */
55
    private $vchTrue;
56
57
    /**
58
     * @var Buffer
59
     */
60
    private $int0;
61
62
    /**
63
     * @var Buffer
64
     */
65
    private $int1;
66
67
    /**
68
     * @var array
69
     */
70
    private $disabledOps = [
71
        Opcodes::OP_CAT,    Opcodes::OP_SUBSTR, Opcodes::OP_LEFT,  Opcodes::OP_RIGHT,
72
        Opcodes::OP_INVERT, Opcodes::OP_AND,    Opcodes::OP_OR,    Opcodes::OP_XOR,
73
        Opcodes::OP_2MUL,   Opcodes::OP_2DIV,   Opcodes::OP_MUL,   Opcodes::OP_DIV,
74
        Opcodes::OP_MOD,    Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT
75
    ];
76
77
    /**
78
     * @param EcAdapterInterface $ecAdapter
79
     * @param TransactionInterface $transaction
80
     */
81
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $transaction)
0 ignored issues
show
Unused Code introduced by
The parameter $transaction is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

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