Completed
Pull Request — master (#239)
by thomas
31:39 queued 14:06
created

Interpreter::verify()   F

Complexity

Conditions 33
Paths 1176

Size

Total Lines 118
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 56
CRAP Score 38.9876

Importance

Changes 13
Bugs 1 Features 2
Metric Value
c 13
b 1
f 2
dl 0
loc 118
ccs 56
cts 68
cp 0.8235
rs 2
cc 33
eloc 59
nc 1176
nop 5
crap 38.9876

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