Completed
Pull Request — master (#240)
by thomas
73:04
created

Interpreter::verifyWitnessProgram()   C

Complexity

Conditions 12
Paths 22

Size

Total Lines 57
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 12

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 57
rs 6.62
ccs 24
cts 24
cp 1
cc 12
eloc 33
nc 22
nop 4
crap 12

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