Completed
Pull Request — master (#248)
by thomas
58:32 queued 33:01
created

Interpreter   F

Complexity

Total Complexity 261

Size/Duplication

Total Lines 1005
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Test Coverage

Coverage 84.64%

Importance

Changes 28
Bugs 1 Features 5
Metric Value
wmc 261
c 28
b 1
f 5
lcom 1
cbo 19
dl 0
loc 1005
ccs 518
cts 612
cp 0.8464
rs 1.2315

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
B castToBool() 0 16 5
A isValidSignatureEncoding() 0 11 2
C checkMinimalPush() 0 24 9
A checkOpcodeCount() 0 8 2
C verifyWitnessProgram() 0 57 12
F verify() 0 123 35
A checkExec() 0 11 3
F evaluate() 0 639 192

How to fix   Complexity   

Complex Class

Complex classes like Interpreter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Interpreter, and based on these observations, apply Extract Interface, too.

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