Completed
Pull Request — master (#239)
by thomas
49:01 queued 45:29
created

Interpreter::checkPublicKeyEncoding()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

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

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

Loading history...
75
    {
76
        $this->ecAdapter = $ecAdapter;
77
        $this->math = $ecAdapter->getMath();
78
        $this->vchFalse = new Buffer("", 0, $this->math);
79
        $this->vchTrue = new Buffer("\x01", 1, $this->math);
80
        $this->int0 = Number::buffer($this->vchFalse, false, 4, $this->math)->getBuffer();
81
        $this->int1 = Number::buffer($this->vchTrue, false, 1, $this->math)->getBuffer();
82
    }
83
84
    /**
85
     * Cast the value to a boolean
86
     *
87
     * @param BufferInterface $value
88
     * @return bool
89
     */
90
    public function castToBool(BufferInterface $value)
91
    {
92
        $val = $value->getBinary();
93
        for ($i = 0, $size = strlen($val); $i < $size; $i++) {
94
            $chr = ord($val[$i]);
95
            if ($chr != 0) {
96
                if ($i == ($size - 1) && $chr == 0x80) {
97
                    return false;
98
                }
99
100
                return true;
101
            }
102
        }
103
104
        return false;
105
    }
106
107
    /**
108
     * @param BufferInterface $signature
109
     * @return bool
110
     */
111
    public function isValidSignatureEncoding(BufferInterface $signature)
112
    {
113
        try {
114
            TransactionSignature::isDERSignature($signature);
115
            return true;
116
        } catch (SignatureNotCanonical $e) {
117
            /* In any case, we will return false outside this block */
118 756
        }
119
120 756
        return false;
121 756
    }
122 756
123 756
    /**
124 756
     * @param int $opCode
125 756
     * @param BufferInterface $pushData
126 756
     * @return bool
127 756
     * @throws \Exception
128
     */
129 756
    public function checkMinimalPush($opCode, BufferInterface $pushData)
130 756
    {
131 756
        $pushSize = $pushData->getSize();
132 756
        $binary = $pushData->getBinary();
133 756
134
        if ($pushSize === 0) {
135
            return $opCode === Opcodes::OP_0;
136
        } elseif ($pushSize === 1) {
137
            $first = ord($binary[0]);
138
            if ($first >= 1 && $first <= 16) {
139
                return $opCode === (Opcodes::OP_1 + ($first - 1));
140
            } elseif ($first === 0x81) {
141 432
                return $opCode === Opcodes::OP_1NEGATE;
142
            }
143 432
        } elseif ($pushSize <= 75) {
144 66
            return $opCode === $pushSize;
145
        } elseif ($pushSize <= 255) {
146
            return $opCode === Opcodes::OP_PUSHDATA1;
147
        } elseif ($pushSize <= 65535) {
148 366
            return $opCode === Opcodes::OP_PUSHDATA2;
149
        }
150
151
        return true;
152
    }
153
154
    /**
155 54
     * @return $this
156
     * @throws \Exception
157
     */
158 54
    private function checkOpcodeCount()
159 36
    {
160 18
        if ($this->math->cmp($this->opCount, 201) > 0) {
161
            throw new \RuntimeException('Error: Script op code count');
162
        }
163
164 18
        return $this;
165
    }
166
167
    /**
168
     * @param WitnessProgram $witnessProgram
169
     * @param ScriptWitness $scriptWitness
170
     * @param int $flags
171
     * @param Checker $checker
172
     * @return bool
173 30
     */
174
    private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitness $scriptWitness, $flags, Checker $checker)
175 30
    {
176 6
        $witnessCount = count($scriptWitness);
177
178
        if ($witnessProgram->getVersion() == 0) {
179 24
            $buffer = $witnessProgram->getProgram();
180 24
            if ($buffer->getSize() === 32) {
181 24
                // Version 0 segregated witness program: SHA256(Script) in program, Script + inputs in witness
182 24
                if ($witnessCount === 0) {
183
                    // Must contain script at least
184 24
                    return false;
185
                }
186
187
                $scriptPubKey = new Script($scriptWitness[$witnessCount - 1]);
188
                $stackValues = $scriptWitness->slice(0, -1);
189
                $hashScriptPubKey = Hash::sha256($scriptWitness[$witnessCount - 1]);
190
191
                if ($hashScriptPubKey == $buffer) {
192
                    return false;
193
                }
194 36
            } elseif ($buffer->getSize() === 20) {
195
                // Version 0 special case for pay-to-pubkeyhash
196 36
                if ($witnessCount !== 2) {
197 6
                    // 2 items in witness - <signature> <pubkey>
198
                    return false;
199
                }
200 30
201 30
                $scriptPubKey = ScriptFactory::create()->sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $buffer, Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG])->getScript();
202
                $stackValues = $scriptWitness;
203 30
            } else {
204 30
                return false;
205
            }
206
        } elseif ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
207
            return false;
208
        } else {
209
            return false;
210
        }
211
212 60
        $mainStack = new Stack();
213
        foreach ($stackValues as $value) {
214 60
            $mainStack->push($value);
215 6
        }
216
217
        if (!$this->evaluate($scriptPubKey, $mainStack, 1, $flags, $checker)) {
218 54
            return false;
219 12
        }
220 42
221 6
        if ($mainStack->count() !== 1) {
222 36
            return false;
223 6
        }
224
225
        if (!$this->castToBool($mainStack->bottom())) {
226 30
            return false;
227
        }
228
229
        return true;
230
    }
231
232
    /**
233
     * @param ScriptInterface $scriptSig
234 24
     * @param ScriptInterface $scriptPubKey
235
     * @param int $flags
236 24
     * @param Checker $checker
237 6
     * @param ScriptWitness|null $witness
238
     * @return bool
239
     */
240 18
    public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $flags, Checker $checker, ScriptWitness $witness = null)
241
    {
242
        static $emptyWitness = null;
243
        if ($emptyWitness === null) {
244
            $emptyWitness = new ScriptWitness([]);
245
        }
246
247
        $witness = $witness ?: $emptyWitness;
248
249 6
        if (($flags & self::VERIFY_SIGPUSHONLY) != 0 && !$scriptSig->isPushOnly()) {
250
            return false;
251 6
        }
252 6
253
        $stack = new Stack();
254 6
        if (!$this->evaluate($scriptSig, $stack, 0, $flags, $checker)) {
255
            return false;
256 6
        }
257
258
        $backup = [];
259
        if ($flags & self::VERIFY_P2SH) {
260
            foreach ($stack as $s) {
261
                $backup[] = $s;
262
            }
263 6
        }
264 6
265
        if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
266
            return false;
267
        }
268
269
        if ($stack->isEmpty()) {
270
            return false;
271
        }
272
273
        if (false === $this->castToBool($stack[-1])) {
274
            return false;
275
        }
276
277
        $program = null;
278 672
279
        if ($flags & self::VERIFY_WITNESS) {
280 672
            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...
281 6
                /** @var WitnessProgram $program */
282
                if ($scriptSig->getBuffer()->getSize() !== 0) {
283
                    return false;
284 672
                }
285
286
                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...
287
                    return false;
288
                }
289
290
                $stack->resize(1);
291
            }
292
        }
293
294
        if ($flags & self::VERIFY_P2SH && (new OutputClassifier($scriptPubKey))->isPayToScriptHash()) {
295 18
            if (!$scriptSig->isPushOnly()) {
296
                return false;
297 18
            }
298 18
299 12
            $stack = new Stack();
300
            foreach ($backup as $i) {
301
                $stack->push($i);
302 12
            }
303 12
304
            // Restore mainStack to how it was after evaluating scriptSig
305 12
            if ($stack->isEmpty()) {
306 12
                return false;
307 12
            }
308 12
309 12
            // Load redeemscript as the scriptPubKey
310
            $scriptPubKey = new Script($stack->bottom());
311
            $stack->pop();
312
313
            if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
314
                return false;
315
            }
316
317
            if ($stack->isEmpty()) {
318
                return false;
319
            }
320
321
            if (!$this->castToBool($stack->bottom())) {
322
                return false;
323
            }
324
325
            if ($flags & self::VERIFY_WITNESS) {
326
327
                if ($scriptPubKey->isWitness($program)) {
328
                    if ($scriptSig != (ScriptFactory::create()->push($scriptPubKey->getBuffer())->getScript())) {
329
                        return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
330
                    }
331
332
                    if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
0 ignored issues
show
Bug introduced by
It seems like $program defined by null on line 277 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...
333
                        return false;
334
                    }
335
336
                    $stack->resize(1);
337
                }
338
            }
339
        }
340
341
        if ($flags & self::VERIFY_CLEAN_STACK != 0) {
342
            if (!($flags & self::VERIFY_P2SH != 0 && $flags & self::VERIFY_WITNESS != 0)) {
343
                return false; // implied flags required
344
            }
345
346
            if (count($stack) != 1) {
347
                return false; // Cleanstack
348
            }
349
        }
350
351
        if ($flags & self::VERIFY_WITNESS) {
352
            if (!$flags & self::VERIFY_P2SH) {
353
                return false; //
354
            }
355
356
            if ($program === null && !$witness->isNull()) {
357
                return false; // SCRIPT_ERR_WITNESS_UNEXPECTED
358
            }
359
        }
360
361
        return true;
362
    }
363
364
    /**
365
     * @param Stack $vfStack
366
     * @return bool
367
     */
368
    private function checkExec(Stack $vfStack)
369
    {
370
        $c = 0;
371
        $len = $vfStack->end();
372
        for ($i = 0; $i < $len; $i++) {
373
            if ($vfStack[0 - $len - $i] === true) {
374
                $c++;
375
            }
376 684
        }
377
        return !(bool)$c;
378 684
    }
379 684
380
    /**
381
     * @param ScriptInterface $script
382
     * @param Stack $mainStack
383 684
     * @param int $sigVersion
384 684
     * @param int $flags
385 156
     * @param Checker $checker
386 156
     * @return bool
387
     */
388 684
    public function evaluate(ScriptInterface $script, Stack $mainStack, $sigVersion, $flags, Checker $checker)
389 258
    {
390
        $math = $this->math;
391
        $hashStartPos = 0;
392 426
        $this->opCount = 0;
393
        $altStack = new Stack();
394
        $vfStack = new Stack();
395
        $parser = $script->getScriptParser();
396 426
397
        if ($script->getBuffer()->getSize() > 10000) {
398
            return false;
399
        }
400 426
401 6
        try {
402
            foreach ($parser as $operation) {
403
                $opCode = $operation->getOp();
404
                $pushData = $operation->getData();
405
                $fExec = $this->checkExec($vfStack);
406 6
407 6
                // If pushdata was written to,
408
                if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
409
                    throw new \RuntimeException('Error - push size');
410
                }
411
412 6
                // OP_RESERVED should not count towards opCount
413 6
                if ($opCode > Opcodes::OP_16 && ++$this->opCount) {
414
                    $this->checkOpcodeCount();
415 6
                }
416
417
                if (in_array($opCode, $this->disabledOps, true)) {
418 6
                    throw new \RuntimeException('Disabled Opcode');
419
                }
420 426
421
                if ($fExec && $operation->isPush()) {
422
                    // In range of a pushdata opcode
423
                    if ($flags & self::VERIFY_MINIMALDATA && !$this->checkMinimalPush($opCode, $pushData)) {
424
                        throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
425
                    }
426 684
427
                    $mainStack->push($pushData);
428 684
                    // echo " - [pushed '" . $pushData->getHex() . "']\n";
429 684
                } elseif ($fExec || ($opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF)) {
430 684
                    // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n";
431
                    switch ($opCode) {
432
                        case Opcodes::OP_1NEGATE:
433
                        case Opcodes::OP_1:
434
                        case Opcodes::OP_2:
435 684
                        case Opcodes::OP_3:
436
                        case Opcodes::OP_4:
437
                        case Opcodes::OP_5:
438
                        case Opcodes::OP_6:
439
                        case Opcodes::OP_7:
440
                        case Opcodes::OP_8:
441
                        case Opcodes::OP_9:
442 684
                        case Opcodes::OP_10:
443
                        case Opcodes::OP_11:
444 684
                        case Opcodes::OP_12:
445 684
                        case Opcodes::OP_13:
446 684
                        case Opcodes::OP_14:
447 684
                        case Opcodes::OP_15:
448
                        case Opcodes::OP_16:
449 684
                            $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode);
450
                            $mainStack->push(Number::int($num)->getBuffer());
451
                            break;
452
453
                        case Opcodes::OP_CHECKLOCKTIMEVERIFY:
454 684
                            if (!$flags & self::VERIFY_CHECKLOCKTIMEVERIFY) {
455 684
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
456 684
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
457 684
                                }
458
                                break;
459
                            }
460 684
461
                            if ($mainStack->isEmpty()) {
462
                                throw new \RuntimeException('Invalid stack operation - CLTV');
463
                            }
464
465 684
                            $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...
466 672
                            if (!$checker->checkLockTime($lockTime)) {
467 672
                                throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
468
                            }
469 684
470 12
                            break;
471
472
                        case Opcodes::OP_CHECKSEQUENCEVERIFY:
473 684
                            if (!$flags & self::VERIFY_CHECKSEQUENCEVERIFY) {
474
                                if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
475 426
                                    throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
476 6
                                }
477
                                break;
478
                            }
479 420
480
                            if ($mainStack->isEmpty()) {
481 678
                                throw new \RuntimeException('Invalid stack operation - CSV');
482
                            }
483
484 660
                            $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...
485 660
                            $nSequence = $sequence->getInt();
486 660
                            if ($math->cmp($nSequence, 0) < 0) {
487 660
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
488 660
                            }
489 660
490 660
                            if ($math->cmp($math->bitwiseAnd($nSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), '0') !== 0) {
491 660
                                break;
492 660
                            }
493 660
494 660
                            if (!$checker->checkSequence($sequence)) {
495 660
                                throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied locktime');
496 660
                            }
497 660
                            break;
498 660
499 660
                        case Opcodes::OP_NOP1:
500 660
                        case Opcodes::OP_NOP4:
501 192
                        case Opcodes::OP_NOP5:
502 192
                        case Opcodes::OP_NOP6:
503 192
                        case Opcodes::OP_NOP7:
504
                        case Opcodes::OP_NOP8:
505 660
                        case Opcodes::OP_NOP9:
506
                        case Opcodes::OP_NOP10:
507
                            if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
508
                                throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
509
                            }
510
                            break;
511
512
                        case Opcodes::OP_NOP:
513
                            break;
514
515
                        case Opcodes::OP_IF:
516
                        case Opcodes::OP_NOTIF:
517
                            // <expression> if [statements] [else [statements]] endif
518
                            $value = false;
519
                            if ($fExec) {
520
                                if ($mainStack->isEmpty()) {
521
                                    throw new \RuntimeException('Unbalanced conditional');
522
                                }
523
                                // todo
524 660
                                $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...
525
                                $value = $this->castToBool($buffer);
526
                                if ($opCode === Opcodes::OP_NOTIF) {
527
                                    $value = !$value;
528
                                }
529
                            }
530
                            $vfStack->push($value ? $this->vchTrue : $this->vchFalse);
531
                            break;
532
533
                        case Opcodes::OP_ELSE:
534
                            if ($vfStack->isEmpty()) {
535
                                throw new \RuntimeException('Unbalanced conditional');
536
                            }
537
                            $vfStack[-1] = !$vfStack->end() ? $this->vchTrue : $this->vchFalse;
538
                            break;
539
540
                        case Opcodes::OP_ENDIF:
541
                            if ($vfStack->isEmpty()) {
542
                                throw new \RuntimeException('Unbalanced conditional');
543
                            }
544
                            break;
545
546
                        case Opcodes::OP_VERIFY:
547
                            if ($mainStack->isEmpty()) {
548
                                throw new \RuntimeException('Invalid stack operation');
549
                            }
550
                            $value = $this->castToBool($mainStack[-1]);
551 660
                            if (!$value) {
552 660
                                throw new \RuntimeException('Error: verify');
553 660
                            }
554 660
                            $mainStack->pop();
555 660
                            break;
556 660
557 660
                        case Opcodes::OP_RESERVED:
558 660
                            // todo
559 6
                            break;
560 6
561
                        case Opcodes::OP_TOALTSTACK:
562
                            if ($mainStack->isEmpty()) {
563
                                throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
564 654
                            }
565 12
                            $altStack->push($mainStack->pop());
566
                            break;
567 642
568 642
                        case Opcodes::OP_FROMALTSTACK:
569
                            if ($altStack->isEmpty()) {
570 18
                                throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
571 18
                            }
572 18
                            $mainStack->push($altStack->pop());
573 6
                            break;
574
575
                        case Opcodes::OP_IFDUP:
576 12
                            // If top value not zero, duplicate it.
577 12
                            if ($mainStack->isEmpty()) {
578 12
                                throw new \RuntimeException('Invalid stack operation OP_IFDUP');
579 6
                            }
580 6
                            $vch = $mainStack[-1];
581 12
                            if ($this->castToBool($vch)) {
582 12
                                $mainStack->push($vch);
583 12
                            }
584
                            break;
585 636
586 18
                        case Opcodes::OP_DEPTH:
587 6
                            $num = count($mainStack);
588
                            $depth = Number::int($num)->getBuffer();
589 12
                            $mainStack->push($depth);
590 12
                            break;
591
592 630
                        case Opcodes::OP_DROP:
593 18
                            if ($mainStack->isEmpty()) {
594 6
                                throw new \RuntimeException('Invalid stack operation OP_DROP');
595
                            }
596 12
                            $mainStack->pop();
597
                            break;
598 624
599 24
                        case Opcodes::OP_DUP:
600 6
                            if ($mainStack->isEmpty()) {
601
                                throw new \RuntimeException('Invalid stack operation OP_DUP');
602 18
                            }
603 18
                            $vch = $mainStack[-1];
604 6
                            $mainStack->push($vch);
605
                            break;
606 12
607 12
                        case Opcodes::OP_NIP:
608
                            if (count($mainStack) < 2) {
609 600
                                throw new \RuntimeException('Invalid stack operation OP_NIP');
610
                            }
611 12
                            unset($mainStack[-2]);
612
                            break;
613 588
614 18
                        case Opcodes::OP_OVER:
615 6
                            if (count($mainStack) < 2) {
616
                                throw new \RuntimeException('Invalid stack operation OP_OVER');
617 12
                            }
618 12
                            $vch = $mainStack[-2];
619
                            $mainStack->push($vch);
620 582
                            break;
621 12
622 6
                        case Opcodes::OP_ROT:
623
                            if (count($mainStack) < 3) {
624 6
                                throw new \RuntimeException('Invalid stack operation OP_ROT');
625 6
                            }
626
                            $mainStack->swap(-3, -2);
627 576
                            $mainStack->swap(-2, -1);
628
                            break;
629 12
630 6
                        case Opcodes::OP_SWAP:
631
                            if (count($mainStack) < 2) {
632 6
                                throw new \RuntimeException('Invalid stack operation OP_SWAP');
633 6
                            }
634 6
                            $mainStack->swap(-2, -1);
635 6
                            break;
636 6
637
                        case Opcodes::OP_TUCK:
638 570
                            if (count($mainStack) < 2) {
639 72
                                throw new \RuntimeException('Invalid stack operation OP_TUCK');
640 72
                            }
641 24
                            $vch = $mainStack[-1];
642 24
                            $mainStack->add(count($mainStack) - 1 - 2, $vch);
643 54
                            break;
644
645
                        case Opcodes::OP_PICK:
646 72
                        case Opcodes::OP_ROLL:
647 72
                            if (count($mainStack) < 2) {
648
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
649 570
                            }
650 6
651 6
                            $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...
652
                            $mainStack->pop();
653
                            if ($math->cmp($n, 0) < 0 || $math->cmp($n, count($mainStack)) >= 0) {
654
                                throw new \RuntimeException('Invalid stack operation OP_PICK');
655
                            }
656 564
657 18
                            $pos = (int) $math->sub($math->sub(0, $n), 1);
658 6
                            $vch = $mainStack[$pos];
659
                            if ($opCode === Opcodes::OP_ROLL) {
660 12
                                unset($mainStack[$pos]);
661 12
                            }
662 12
                            $mainStack->push($vch);
663
                            break;
664 558
665 18
                        case Opcodes::OP_2DROP:
666 6
                            if (count($mainStack) < 2) {
667
                                throw new \RuntimeException('Invalid stack operation OP_2DROP');
668 12
                            }
669 12
                            $mainStack->pop();
670
                            $mainStack->pop();
671 552
                            break;
672 18
673 6
                        case Opcodes::OP_2DUP:
674
                            if (count($mainStack) < 2) {
675 12
                                throw new \RuntimeException('Invalid stack operation OP_2DUP');
676 12
                            }
677 12
                            $string1 = $mainStack[-2];
678
                            $string2 = $mainStack[-1];
679 546
                            $mainStack->push($string1);
680 12
                            $mainStack->push($string2);
681 6
                            break;
682
683 6
                        case Opcodes::OP_3DUP:
684 6
                            if (count($mainStack) < 3) {
685 6
                                throw new \RuntimeException('Invalid stack operation OP_3DUP');
686
                            }
687 540
                            $string1 = $mainStack[-3];
688 12
                            $string2 = $mainStack[-2];
689 6
                            $string3 = $mainStack[-1];
690
                            $mainStack->push($string1);
691 6
                            $mainStack->push($string2);
692 6
                            $mainStack->push($string3);
693
                            break;
694 534
695 12
                        case Opcodes::OP_2OVER:
696 6
                            if (count($mainStack) < 4) {
697
                                throw new \RuntimeException('Invalid stack operation OP_2OVER');
698 6
                            }
699 6
                            $string1 = $mainStack[-4];
700
                            $string2 = $mainStack[-3];
701
                            $mainStack->push($string1);
702 522
                            $mainStack->push($string2);
703 522
                            break;
704 24
705 6
                        case Opcodes::OP_2ROT:
706
                            if (count($mainStack) < 6) {
707
                                throw new \RuntimeException('Invalid stack operation OP_2ROT');
708 18
                            }
709 18
                            $string1 = $mainStack[-6];
710 18
                            $string2 = $mainStack[-5];
711 6
                            unset($mainStack[-6], $mainStack[-5]);
712
                            $mainStack->push($string1);
713
                            $mainStack->push($string2);
714 12
                            break;
715 12
716 12
                        case Opcodes::OP_2SWAP:
717 6
                            if (count($mainStack) < 4) {
718 6
                                throw new \RuntimeException('Invalid stack operation OP_2SWAP');
719 12
                            }
720 12
                            $mainStack->swap(-3, -1);
721
                            $mainStack->swap(-4, -2);
722 510
                            break;
723 12
724 6
                        case Opcodes::OP_SIZE:
725
                            if ($mainStack->isEmpty()) {
726 6
                                throw new \RuntimeException('Invalid stack operation OP_SIZE');
727 6
                            }
728 6
                            // todo: Int sizes?
729
                            $vch = $mainStack[-1];
730 504
                            $mainStack->push(Number::int($vch->getSize())->getBuffer());
731 18
                            break;
732 6
733
                        case Opcodes::OP_EQUAL:
734 12
                        case Opcodes::OP_EQUALVERIFY:
735 12
                            if (count($mainStack) < 2) {
736 12
                                throw new \RuntimeException('Invalid stack operation OP_EQUAL');
737 12
                            }
738 12
                            $vch1 = $mainStack[-2];
739
                            $vch2 = $mainStack[-1];
740 498
741 18
                            $equal = ($vch1->getBinary() === $vch2->getBinary());
742 6
                            $mainStack->pop();
743
                            $mainStack->pop();
744 12
                            $mainStack->push(($equal ? $this->vchTrue : $this->vchFalse));
745 12
                            if ($opCode === Opcodes::OP_EQUALVERIFY) {
746 12
                                if ($equal) {
747 12
                                    $mainStack->pop();
748 12
                                } else {
749 12
                                    throw new \RuntimeException('Error EQUALVERIFY');
750 12
                                }
751
                            }
752 492
753 18
                            break;
754 6
755
                        // Arithmetic operations
756 12
                        case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
757 12
                            if ($mainStack->isEmpty()) {
758 12
                                throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
759 12
                            }
760 12
761
                            $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...
762 486
763 12
                            if ($opCode === Opcodes::OP_1ADD) {
764 6
                                $num = $math->add($num, '1');
765
                            } elseif ($opCode === Opcodes::OP_1SUB) {
766 6
                                $num = $math->sub($num, '1');
767 6
                            } elseif ($opCode === Opcodes::OP_2MUL) {
768 6
                                $num = $math->mul(2, $num);
769 6
                            } elseif ($opCode === Opcodes::OP_NEGATE) {
770 6
                                $num = $math->sub(0, $num);
771 6
                            } elseif ($opCode === Opcodes::OP_ABS) {
772
                                if ($math->cmp($num, '0') < 0) {
773 480
                                    $num = $math->sub(0, $num);
774 12
                                }
775 6
                            } elseif ($opCode === Opcodes::OP_NOT) {
776
                                $num = (int) $math->cmp($num, '0') === 0;
777 6
                            } else {
778 6
                                // is OP_0NOTEQUAL
779 6
                                $num = (int) ($math->cmp($num, '0') !== 0);
780
                            }
781 474
782 12
                            $mainStack->pop();
783 6
784
                            $buffer = Number::int($num)->getBuffer();
785
786 6
                            $mainStack->push($buffer);
787 6
                            break;
788 6
789
                        case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
790 468
                            if (count($mainStack) < 2) {
791 468
                                throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
792 396
                            }
793 6
794
                            $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...
795 390
                            $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...
796 390
797
                            if ($opCode === Opcodes::OP_ADD) {
798 390
                                $num = $math->add($num1, $num2);
799
                            } else if ($opCode === Opcodes::OP_SUB) {
800 390
                                $num = $math->sub($num1, $num2);
801 390
                            } else if ($opCode === Opcodes::OP_BOOLAND) {
802 390
                                $num = $math->cmp($num1, '0') !== 0 && $math->cmp($num2, '0') !== 0;
803 390
                            } else if ($opCode === Opcodes::OP_BOOLOR) {
804 114
                                $num = $math->cmp($num1, '0') !== 0 || $math->cmp($num2, '0') !== 0;
805 102
                            } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
806 102
                                $num = $math->cmp($num1, $num2) === 0;
807 12
                            } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
808
                                $num = $math->cmp($num1, $num2) === 0;
809 102
                            } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
810
                                $num = $math->cmp($num1, $num2) !== 0;
811 378
                            } elseif ($opCode === Opcodes::OP_LESSTHAN) {
812
                                $num = $math->cmp($num1, $num2) < 0;
813
                            } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
814 282
                                $num = $math->cmp($num1, $num2) > 0;
815 48
                            } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
816 6
                                $num = $math->cmp($num1, $num2) <= 0;
817
                            } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
818
                                $num = $math->cmp($num1, $num2) >= 0;
819 42
                            } elseif ($opCode === Opcodes::OP_MIN) {
820
                                $num = ($math->cmp($num1, $num2) <= 0) ? $num1 : $num2;
821 42
                            } else {
822 6
                                $num = ($math->cmp($num1, $num2) >= 0) ? $num1 : $num2;
823 42
                            }
824 6
825 36
                            $mainStack->pop();
826
                            $mainStack->pop();
827 30
                            $buffer = Number::int($num)->getBuffer();
828
                            $mainStack->push($buffer);
829 30
830
                            if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
831
                                if ($this->castToBool($mainStack[-1])) {
832
                                    $mainStack->pop();
833 30
                                } else {
834 18
                                    throw new \RuntimeException('NUM EQUAL VERIFY error');
835 18
                                }
836
                            }
837 12
                            break;
838
839
                        case Opcodes::OP_WITHIN:
840 42
                            if (count($mainStack) < 3) {
841
                                throw new \RuntimeException('Invalid stack operation');
842 42
                            }
843
844 42
                            $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...
845 42
                            $num2 = Number::buffer($mainStack[-2], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
846
                            $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...
847 234
848 150
                            $value = $math->cmp($num2, $num1) <= 0 && $math->cmp($num1, $num3) < 0;
849 24
                            $mainStack->pop();
850
                            $mainStack->pop();
851
                            $mainStack->pop();
852 126
                            $mainStack->push($value ? $this->vchTrue : $this->vchFalse);
853 126
                            break;
854
855 126
                        // Hash operation
856 6
                        case Opcodes::OP_RIPEMD160:
857 126
                        case Opcodes::OP_SHA1:
858 6
                        case Opcodes::OP_SHA256:
859 120
                        case Opcodes::OP_HASH160:
860 12
                        case Opcodes::OP_HASH256:
861 114
                            if ($mainStack->isEmpty()) {
862 18
                                throw new \RuntimeException('Invalid stack operation');
863 102
                            }
864 18
865 84
                            $buffer = $mainStack[-1];
866
                            if ($opCode === Opcodes::OP_RIPEMD160) {
867 66
                                $hash = Hash::ripemd160($buffer);
868 6
                            } elseif ($opCode === Opcodes::OP_SHA1) {
869 66
                                $hash = Hash::sha1($buffer);
870 12
                            } elseif ($opCode === Opcodes::OP_SHA256) {
871 60
                                $hash = Hash::sha256($buffer);
872 12
                            } elseif ($opCode === Opcodes::OP_HASH160) {
873 48
                                $hash = Hash::sha256ripe160($buffer);
874 12
                            } else {
875 36
                                $hash = Hash::sha256d($buffer);
876 12
                            }
877 24
878 6
                            $mainStack->pop();
879 6
                            $mainStack->push($hash);
880 6
                            break;
881
882
                        case Opcodes::OP_CODESEPARATOR:
883 126
                            $hashStartPos = $parser->getPosition();
884 126
                            break;
885 126
886 126
                        case Opcodes::OP_CHECKSIG:
887
                        case Opcodes::OP_CHECKSIGVERIFY:
888 126
                            if (count($mainStack) < 2) {
889
                                throw new \RuntimeException('Invalid stack operation');
890
                            }
891
892
                            $vchPubKey = $mainStack[-1];
893
                            $vchSig = $mainStack[-2];
894
895 126
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
896
897 84
                            $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags);
898 12
899 6
                            $mainStack->pop();
900
                            $mainStack->pop();
901
                            $mainStack->push($success ? $this->vchTrue : $this->vchFalse);
902 6
903 6
                            if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
904 6
                                if ($success) {
905
                                    $mainStack->pop();
906 6
                                } else {
907 6
                                    throw new \RuntimeException('Checksig verify');
908 6
                                }
909 6
                            }
910 6
                            break;
911 6
912
                        case Opcodes::OP_CHECKMULTISIG:
913
                        case Opcodes::OP_CHECKMULTISIGVERIFY:
914 72
                            $i = 1;
915 72
                            if (count($mainStack) < $i) {
916 72
                                throw new \RuntimeException('Invalid stack operation');
917 72
                            }
918 72
919 48
                            $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...
920 6
                            if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
921
                                throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
922
                            }
923 42
                            $this->opCount += $keyCount;
924 42
                            $this->checkOpcodeCount();
925 6
926 42
                            // Extract positions of the keys, and signatures, from the stack.
927 6
                            $ikey = ++$i;
928 36
                            $i += $keyCount; /** @var int $i */
929 6
                            if (count($mainStack) < $i) {
930 30
                                throw new \RuntimeException('Invalid stack operation');
931 18
                            }
932 18
933 6
                            $sigCount = Number::buffer($mainStack[-$i], $flags & self::VERIFY_MINIMALDATA)->getInt();
0 ignored issues
show
Documentation introduced by
$flags & self::VERIFY_MINIMALDATA is of type integer, but the function expects a boolean.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
934
                            if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
935
                                throw new \RuntimeException('Invalid Signature count');
936 42
                            }
937 42
                            $isig = ++$i;
938 42
                            $i += $sigCount;
939
940 30
                            // Extract the script since the last OP_CODESEPARATOR
941
                            $scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
942
943
                            $fSuccess = true;
944 30
                            while ($fSuccess && $sigCount > 0) {
945 30
946 24
                                // Fetch the signature and public key
947 6
                                $sig = $mainStack[-$isig];
948
                                $pubkey = $mainStack[-$ikey];
949
950 18
                                // Erase the signature and public key.
951 18
                                unset($mainStack[-$isig], $mainStack[-$ikey]);
952
953 18
                                // Decrement $i, since we are consuming stack values.
954 18
                                $i -= 2;
955
956 12
                                if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) {
957 12
                                    $isig++;
958 12
                                    $sigCount--;
959
                                }
960 12
961
                                $ikey++;
962
                                $keyCount--;
963
964
                                // If there are more signatures left than keys left,
965
                                // then too many signatures have failed. Exit early,
966
                                // without checking any further signatures.
967 12
                                if ($sigCount > $keyCount) {
968
                                    $fSuccess = false;
969 6
                                }
970 6
                            }
971
972
                            while ($i-- > 1) {
973
                                $mainStack->pop();
974
                            }
975
976
                            // A bug causes CHECKMULTISIG to consume one extra argument
977
                            // whose contents were not checked in any way.
978
                            //
979
                            // Unfortunately this is a potential source of mutability,
980
                            // so optionally verify it is exactly equal to zero prior
981
                            // to removing it from the stack.
982
                            if ($mainStack->isEmpty()) {
983
                                throw new \RuntimeException('Invalid stack operation');
984
                            }
985
986
                            if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) {
987
                                throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
988
                            }
989
990
                            $mainStack->pop();
991
                            $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
992
993
                            if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
994
                                if ($fSuccess) {
995
                                    $mainStack->pop();
996
                                } else {
997
                                    throw new \RuntimeException('OP_CHECKMULTISIG verify');
998
                                }
999
                            }
1000
                            break;
1001
1002
                        default:
1003
                            throw new \RuntimeException('Opcode not found');
1004
                    }
1005
1006
                    if (count($mainStack) + count($altStack) > 1000) {
1007
                        throw new \RuntimeException('Invalid stack size, exceeds 1000');
1008
                    }
1009
                }
1010
            }
1011
1012
            if (!$vfStack->end() === 0) {
1013
                throw new \RuntimeException('Unbalanced conditional at script end');
1014
            }
1015
1016
            return true;
1017
        } catch (ScriptRuntimeException $e) {
1018
            // echo "\n Runtime: " . $e->getMessage() . "\n";
1019
            // Failure due to script tags, can access flag: $e->getFailureFlag()
1020
            return false;
1021
        } catch (\Exception $e) {
1022
            // echo "\n General: " . $e->getMessage()  . PHP_EOL . $e->getTraceAsString();
1023
            return false;
1024
        }
1025
    }
1026
}
1027