Conditions | 204 |
Paths | 5300 |
Total Lines | 653 |
Code Lines | 445 |
Lines | 0 |
Ratio | 0 % |
Tests | 424 |
CRAP Score | 204.6739 |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | <?php |
||
364 | 2498 | public function evaluate(ScriptInterface $script, Stack $mainStack, $sigVersion, $flags, Checker $checker) |
|
365 | { |
||
366 | 2498 | $hashStartPos = 0; |
|
367 | 2498 | $opCount = 0; |
|
368 | 2498 | $zero = gmp_init(0, 10); |
|
369 | 2498 | $altStack = new Stack(); |
|
370 | 2498 | $vfStack = new Stack(); |
|
371 | 2498 | $minimal = ($flags & self::VERIFY_MINIMALDATA) !== 0; |
|
372 | 2498 | $parser = $script->getScriptParser(); |
|
373 | |||
374 | 2498 | if ($script->getBuffer()->getSize() > 10000) { |
|
375 | 2 | return false; |
|
376 | } |
||
377 | |||
378 | try { |
||
379 | 2498 | foreach ($parser as $operation) { |
|
380 | 2448 | $opCode = $operation->getOp(); |
|
381 | 2448 | $pushData = $operation->getData(); |
|
382 | 2448 | $fExec = !$this->checkExec($vfStack, false); |
|
383 | |||
384 | // If pushdata was written to |
||
385 | 2448 | if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) { |
|
386 | 6 | throw new \RuntimeException('Error - push size'); |
|
387 | } |
||
388 | |||
389 | // OP_RESERVED should not count towards opCount |
||
390 | 2446 | if ($opCode > Opcodes::OP_16 && ++$opCount) { |
|
391 | 2320 | $this->checkOpcodeCount($opCount); |
|
392 | } |
||
393 | |||
394 | 2446 | if (in_array($opCode, $this->disabledOps, true)) { |
|
395 | 48 | throw new \RuntimeException('Disabled Opcode'); |
|
396 | } |
||
397 | |||
398 | 2446 | if ($fExec && $operation->isPush()) { |
|
399 | // In range of a pushdata opcode |
||
400 | 1846 | if ($minimal && !$this->checkMinimalPush($opCode, $pushData)) { |
|
401 | 42 | throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required'); |
|
402 | } |
||
403 | |||
404 | 1804 | $mainStack->push($pushData); |
|
405 | // echo " - [pushed '" . $pushData->getHex() . "']\n"; |
||
406 | 2366 | } elseif ($fExec || (Opcodes::OP_IF <= $opCode && $opCode <= Opcodes::OP_ENDIF)) { |
|
407 | // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n"; |
||
408 | switch ($opCode) { |
||
409 | 2366 | case Opcodes::OP_1NEGATE: |
|
410 | 2366 | case Opcodes::OP_1: |
|
411 | 2322 | case Opcodes::OP_2: |
|
412 | 2314 | case Opcodes::OP_3: |
|
413 | 2314 | case Opcodes::OP_4: |
|
414 | 2314 | case Opcodes::OP_5: |
|
415 | 2314 | case Opcodes::OP_6: |
|
416 | 2314 | case Opcodes::OP_7: |
|
417 | 2312 | case Opcodes::OP_8: |
|
418 | 2312 | case Opcodes::OP_9: |
|
419 | 2312 | case Opcodes::OP_10: |
|
420 | 2312 | case Opcodes::OP_11: |
|
421 | 2310 | case Opcodes::OP_12: |
|
422 | 2310 | case Opcodes::OP_13: |
|
423 | 2310 | case Opcodes::OP_14: |
|
424 | 2310 | case Opcodes::OP_15: |
|
425 | 2310 | case Opcodes::OP_16: |
|
426 | 1416 | $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode); |
|
427 | 1416 | $mainStack->push(Number::int($num)->getBuffer()); |
|
428 | 1416 | break; |
|
429 | |||
430 | 2310 | case Opcodes::OP_CHECKLOCKTIMEVERIFY: |
|
431 | 12 | if (!($flags & self::VERIFY_CHECKLOCKTIMEVERIFY)) { |
|
432 | 12 | if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { |
|
433 | 2 | throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged'); |
|
434 | } |
||
435 | 10 | break; |
|
436 | } |
||
437 | |||
438 | if ($mainStack->isEmpty()) { |
||
439 | throw new \RuntimeException('Invalid stack operation - CLTV'); |
||
440 | } |
||
441 | |||
442 | $lockTime = Number::buffer($mainStack[-1], $minimal, 5, $this->math); |
||
443 | if (!$checker->checkLockTime($lockTime)) { |
||
444 | throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime'); |
||
445 | } |
||
446 | |||
447 | break; |
||
448 | |||
449 | 2308 | case Opcodes::OP_CHECKSEQUENCEVERIFY: |
|
450 | 24 | if (!($flags & self::VERIFY_CHECKSEQUENCEVERIFY)) { |
|
451 | 12 | if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { |
|
452 | 2 | throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged'); |
|
453 | } |
||
454 | 10 | break; |
|
455 | } |
||
456 | |||
457 | 12 | if ($mainStack->isEmpty()) { |
|
458 | 2 | throw new \RuntimeException('Invalid stack operation - CSV'); |
|
459 | } |
||
460 | |||
461 | 10 | $sequence = Number::buffer($mainStack[-1], $minimal, 5, $this->math); |
|
462 | 8 | $nSequence = $sequence->getGmp(); |
|
463 | 8 | if ($this->math->cmp($nSequence, $zero) < 0) { |
|
464 | 2 | throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime'); |
|
465 | } |
||
466 | |||
467 | 6 | if ($this->math->cmp($this->math->bitwiseAnd($nSequence, gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) { |
|
468 | 2 | break; |
|
469 | } |
||
470 | |||
471 | 4 | if (!$checker->checkSequence($sequence)) { |
|
472 | 4 | throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied sequence'); |
|
473 | } |
||
474 | break; |
||
475 | |||
476 | 2294 | case Opcodes::OP_NOP1: |
|
477 | 2292 | case Opcodes::OP_NOP4: |
|
478 | 2290 | case Opcodes::OP_NOP5: |
|
479 | 2288 | case Opcodes::OP_NOP6: |
|
480 | 2286 | case Opcodes::OP_NOP7: |
|
481 | 2284 | case Opcodes::OP_NOP8: |
|
482 | 2282 | case Opcodes::OP_NOP9: |
|
483 | 2280 | case Opcodes::OP_NOP10: |
|
484 | 54 | if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { |
|
485 | 20 | throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged'); |
|
486 | } |
||
487 | 34 | break; |
|
488 | |||
489 | 2274 | case Opcodes::OP_NOP: |
|
490 | 168 | break; |
|
491 | |||
492 | 2228 | case Opcodes::OP_IF: |
|
493 | 2174 | case Opcodes::OP_NOTIF: |
|
494 | // <expression> if [statements] [else [statements]] endif |
||
495 | 656 | $value = false; |
|
496 | 656 | if ($fExec) { |
|
497 | 656 | if ($mainStack->isEmpty()) { |
|
498 | 24 | throw new \RuntimeException('Unbalanced conditional'); |
|
499 | } |
||
500 | 632 | $vch = $mainStack[-1]; |
|
501 | |||
502 | 632 | if ($sigVersion === SigHash::V1 && ($flags & self::VERIFY_MINIMALIF)) { |
|
503 | 40 | if ($vch->getSize() > 1) { |
|
504 | 8 | throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded'); |
|
505 | } |
||
506 | |||
507 | 32 | if ($vch->getSize() === 1 && $vch->getBinary() !== "\x01") { |
|
508 | 16 | throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded'); |
|
509 | } |
||
510 | } |
||
511 | |||
512 | 608 | $buffer = Number::buffer($mainStack->pop(), $minimal)->getBuffer(); |
|
|
|||
513 | 608 | $value = $this->castToBool($buffer); |
|
514 | 608 | if ($opCode === Opcodes::OP_NOTIF) { |
|
515 | 90 | $value = !$value; |
|
516 | } |
||
517 | } |
||
518 | 608 | $vfStack->push($value); |
|
519 | 608 | break; |
|
520 | |||
521 | 2160 | case Opcodes::OP_ELSE: |
|
522 | 248 | if ($vfStack->isEmpty()) { |
|
523 | 8 | throw new \RuntimeException('Unbalanced conditional'); |
|
524 | } |
||
525 | 244 | $vfStack->push(!$vfStack->pop()); |
|
526 | 244 | break; |
|
527 | |||
528 | 2154 | case Opcodes::OP_ENDIF: |
|
529 | 412 | if ($vfStack->isEmpty()) { |
|
530 | 14 | throw new \RuntimeException('Unbalanced conditional'); |
|
531 | } |
||
532 | 404 | $vfStack->pop(); |
|
533 | 404 | break; |
|
534 | |||
535 | 1834 | case Opcodes::OP_VERIFY: |
|
536 | 60 | if ($mainStack->isEmpty()) { |
|
537 | 2 | throw new \RuntimeException('Invalid stack operation'); |
|
538 | } |
||
539 | 58 | $value = $this->castToBool($mainStack[-1]); |
|
540 | 58 | if (!$value) { |
|
541 | 2 | throw new \RuntimeException('Error: verify'); |
|
542 | } |
||
543 | 56 | $mainStack->pop(); |
|
544 | 56 | break; |
|
545 | |||
546 | 1820 | case Opcodes::OP_TOALTSTACK: |
|
547 | 16 | if ($mainStack->isEmpty()) { |
|
548 | 2 | throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK'); |
|
549 | } |
||
550 | 14 | $altStack->push($mainStack->pop()); |
|
551 | 14 | break; |
|
552 | |||
553 | 1816 | case Opcodes::OP_FROMALTSTACK: |
|
554 | 10 | if ($altStack->isEmpty()) { |
|
555 | 4 | throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK'); |
|
556 | } |
||
557 | 6 | $mainStack->push($altStack->pop()); |
|
558 | 6 | break; |
|
559 | |||
560 | 1810 | case Opcodes::OP_IFDUP: |
|
561 | // If top value not zero, duplicate it. |
||
562 | 12 | if ($mainStack->isEmpty()) { |
|
563 | 4 | throw new \RuntimeException('Invalid stack operation OP_IFDUP'); |
|
564 | } |
||
565 | 8 | $vch = $mainStack[-1]; |
|
566 | 8 | if ($this->castToBool($vch)) { |
|
567 | 6 | $mainStack->push($vch); |
|
568 | } |
||
569 | 8 | break; |
|
570 | |||
571 | 1804 | case Opcodes::OP_DEPTH: |
|
572 | 152 | $num = count($mainStack); |
|
573 | 152 | $depth = Number::int($num)->getBuffer(); |
|
574 | 152 | $mainStack->push($depth); |
|
575 | 152 | break; |
|
576 | |||
577 | 1786 | case Opcodes::OP_DROP: |
|
578 | 96 | if ($mainStack->isEmpty()) { |
|
579 | 4 | throw new \RuntimeException('Invalid stack operation OP_DROP'); |
|
580 | } |
||
581 | 92 | $mainStack->pop(); |
|
582 | 92 | break; |
|
583 | |||
584 | 1780 | case Opcodes::OP_DUP: |
|
585 | 92 | if ($mainStack->isEmpty()) { |
|
586 | 28 | throw new \RuntimeException('Invalid stack operation OP_DUP'); |
|
587 | } |
||
588 | 88 | $vch = $mainStack[-1]; |
|
589 | 88 | $mainStack->push($vch); |
|
590 | 88 | break; |
|
591 | |||
592 | 1762 | case Opcodes::OP_NIP: |
|
593 | 12 | if (count($mainStack) < 2) { |
|
594 | 6 | throw new \RuntimeException('Invalid stack operation OP_NIP'); |
|
595 | } |
||
596 | 6 | unset($mainStack[-2]); |
|
597 | 6 | break; |
|
598 | |||
599 | 1750 | case Opcodes::OP_OVER: |
|
600 | 12 | if (count($mainStack) < 2) { |
|
601 | 6 | throw new \RuntimeException('Invalid stack operation OP_OVER'); |
|
602 | } |
||
603 | 6 | $vch = $mainStack[-2]; |
|
604 | 6 | $mainStack->push($vch); |
|
605 | 6 | break; |
|
606 | |||
607 | 1742 | case Opcodes::OP_ROT: |
|
608 | 24 | if (count($mainStack) < 3) { |
|
609 | 8 | throw new \RuntimeException('Invalid stack operation OP_ROT'); |
|
610 | } |
||
611 | 16 | $mainStack->swap(-3, -2); |
|
612 | 16 | $mainStack->swap(-2, -1); |
|
613 | 16 | break; |
|
614 | |||
615 | 1730 | case Opcodes::OP_SWAP: |
|
616 | 20 | if (count($mainStack) < 2) { |
|
617 | 6 | throw new \RuntimeException('Invalid stack operation OP_SWAP'); |
|
618 | } |
||
619 | 14 | $mainStack->swap(-2, -1); |
|
620 | 14 | break; |
|
621 | |||
622 | 1722 | case Opcodes::OP_TUCK: |
|
623 | 12 | if (count($mainStack) < 2) { |
|
624 | 6 | throw new \RuntimeException('Invalid stack operation OP_TUCK'); |
|
625 | } |
||
626 | 6 | $vch = $mainStack[-1]; |
|
627 | 6 | $mainStack->add(- 2, $vch); |
|
628 | 6 | break; |
|
629 | |||
630 | 1714 | case Opcodes::OP_PICK: |
|
631 | 1696 | case Opcodes::OP_ROLL: |
|
632 | 58 | if (count($mainStack) < 2) { |
|
633 | 8 | throw new \RuntimeException('Invalid stack operation OP_PICK'); |
|
634 | } |
||
635 | |||
636 | 50 | $n = Number::buffer($mainStack[-1], $minimal, 4)->getGmp(); |
|
637 | 46 | $mainStack->pop(); |
|
638 | 46 | if ($this->math->cmp($n, $zero) < 0 || $this->math->cmp($n, gmp_init(count($mainStack))) >= 0) { |
|
639 | 10 | throw new \RuntimeException('Invalid stack operation OP_PICK'); |
|
640 | } |
||
641 | |||
642 | 36 | $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), gmp_init(1)), 10); |
|
643 | 36 | $vch = $mainStack[$pos]; |
|
644 | 36 | if ($opCode === Opcodes::OP_ROLL) { |
|
645 | 18 | unset($mainStack[$pos]); |
|
646 | } |
||
647 | 36 | $mainStack->push($vch); |
|
648 | 36 | break; |
|
649 | |||
650 | 1680 | case Opcodes::OP_2DROP: |
|
651 | 18 | if (count($mainStack) < 2) { |
|
652 | 2 | throw new \RuntimeException('Invalid stack operation OP_2DROP'); |
|
653 | } |
||
654 | 16 | $mainStack->pop(); |
|
655 | 16 | $mainStack->pop(); |
|
656 | 16 | break; |
|
657 | |||
658 | 1676 | case Opcodes::OP_2DUP: |
|
659 | 12 | if (count($mainStack) < 2) { |
|
660 | 6 | throw new \RuntimeException('Invalid stack operation OP_2DUP'); |
|
661 | } |
||
662 | 6 | $string1 = $mainStack[-2]; |
|
663 | 6 | $string2 = $mainStack[-1]; |
|
664 | 6 | $mainStack->push($string1); |
|
665 | 6 | $mainStack->push($string2); |
|
666 | 6 | break; |
|
667 | |||
668 | 1668 | case Opcodes::OP_3DUP: |
|
669 | 22 | if (count($mainStack) < 3) { |
|
670 | 8 | throw new \RuntimeException('Invalid stack operation OP_3DUP'); |
|
671 | } |
||
672 | 14 | $string1 = $mainStack[-3]; |
|
673 | 14 | $string2 = $mainStack[-2]; |
|
674 | 14 | $string3 = $mainStack[-1]; |
|
675 | 14 | $mainStack->push($string1); |
|
676 | 14 | $mainStack->push($string2); |
|
677 | 14 | $mainStack->push($string3); |
|
678 | 14 | break; |
|
679 | |||
680 | 1648 | case Opcodes::OP_2OVER: |
|
681 | 10 | if (count($mainStack) < 4) { |
|
682 | 6 | throw new \RuntimeException('Invalid stack operation OP_2OVER'); |
|
683 | } |
||
684 | 4 | $string1 = $mainStack[-4]; |
|
685 | 4 | $string2 = $mainStack[-3]; |
|
686 | 4 | $mainStack->push($string1); |
|
687 | 4 | $mainStack->push($string2); |
|
688 | 4 | break; |
|
689 | |||
690 | 1640 | case Opcodes::OP_2ROT: |
|
691 | 20 | if (count($mainStack) < 6) { |
|
692 | 2 | throw new \RuntimeException('Invalid stack operation OP_2ROT'); |
|
693 | } |
||
694 | 18 | $string1 = $mainStack[-6]; |
|
695 | 18 | $string2 = $mainStack[-5]; |
|
696 | 18 | unset($mainStack[-6], $mainStack[-5]); |
|
697 | 18 | $mainStack->push($string1); |
|
698 | 18 | $mainStack->push($string2); |
|
699 | 18 | break; |
|
700 | |||
701 | 1636 | case Opcodes::OP_2SWAP: |
|
702 | 10 | if (count($mainStack) < 4) { |
|
703 | 6 | throw new \RuntimeException('Invalid stack operation OP_2SWAP'); |
|
704 | } |
||
705 | 4 | $mainStack->swap(-3, -1); |
|
706 | 4 | $mainStack->swap(-4, -2); |
|
707 | 4 | break; |
|
708 | |||
709 | 1628 | case Opcodes::OP_SIZE: |
|
710 | 62 | if ($mainStack->isEmpty()) { |
|
711 | 4 | throw new \RuntimeException('Invalid stack operation OP_SIZE'); |
|
712 | } |
||
713 | 58 | $size = Number::int($mainStack[-1]->getSize()); |
|
714 | 58 | $mainStack->push($size->getBuffer()); |
|
715 | 58 | break; |
|
716 | |||
717 | 1622 | case Opcodes::OP_EQUAL: |
|
718 | 1400 | case Opcodes::OP_EQUALVERIFY: |
|
719 | 700 | if (count($mainStack) < 2) { |
|
720 | 8 | throw new \RuntimeException('Invalid stack operation OP_EQUAL'); |
|
721 | } |
||
722 | |||
723 | 692 | $equal = $mainStack[-2]->equals($mainStack[-1]); |
|
724 | 692 | $mainStack->pop(); |
|
725 | 692 | $mainStack->pop(); |
|
726 | 692 | $mainStack->push($equal ? $this->vchTrue : $this->vchFalse); |
|
727 | 692 | if ($opCode === Opcodes::OP_EQUALVERIFY) { |
|
728 | 126 | if ($equal) { |
|
729 | 108 | $mainStack->pop(); |
|
730 | } else { |
||
731 | 18 | throw new \RuntimeException('Error EQUALVERIFY'); |
|
732 | } |
||
733 | } |
||
734 | |||
735 | 676 | break; |
|
736 | |||
737 | // Arithmetic operations |
||
738 | 1338 | case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL: |
|
739 | 222 | if ($mainStack->isEmpty()) { |
|
740 | 12 | throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL'); |
|
741 | } |
||
742 | |||
743 | 210 | $num = Number::buffer($mainStack[-1], $minimal)->getGmp(); |
|
744 | |||
745 | 164 | if ($opCode === Opcodes::OP_1ADD) { |
|
746 | 14 | $num = $this->math->add($num, gmp_init(1)); |
|
747 | 150 | } elseif ($opCode === Opcodes::OP_1SUB) { |
|
748 | 6 | $num = $this->math->sub($num, gmp_init(1)); |
|
749 | 144 | } elseif ($opCode === Opcodes::OP_2MUL) { |
|
750 | $num = $this->math->mul(gmp_init(2), $num); |
||
751 | 144 | } elseif ($opCode === Opcodes::OP_NEGATE) { |
|
752 | 8 | $num = $this->math->sub($zero, $num); |
|
753 | 138 | } elseif ($opCode === Opcodes::OP_ABS) { |
|
754 | 10 | if ($this->math->cmp($num, $zero) < 0) { |
|
755 | 10 | $num = $this->math->sub($zero, $num); |
|
756 | } |
||
757 | 128 | } elseif ($opCode === Opcodes::OP_NOT) { |
|
758 | 116 | $num = gmp_init($this->math->cmp($num, $zero) === 0 ? 1 : 0); |
|
759 | } else { |
||
760 | // is OP_0NOTEQUAL |
||
761 | 12 | $num = gmp_init($this->math->cmp($num, $zero) !== 0 ? 1 : 0); |
|
762 | } |
||
763 | |||
764 | 164 | $mainStack->pop(); |
|
765 | |||
766 | 164 | $buffer = Number::int(gmp_strval($num, 10))->getBuffer(); |
|
767 | |||
768 | 164 | $mainStack->push($buffer); |
|
769 | 164 | break; |
|
770 | |||
771 | 1220 | case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX: |
|
772 | 328 | if (count($mainStack) < 2) { |
|
773 | 26 | throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)'); |
|
774 | } |
||
775 | |||
776 | 302 | $num1 = Number::buffer($mainStack[-2], $minimal)->getGmp(); |
|
777 | 268 | $num2 = Number::buffer($mainStack[-1], $minimal)->getGmp(); |
|
778 | |||
779 | 242 | if ($opCode === Opcodes::OP_ADD) { |
|
780 | 60 | $num = $this->math->add($num1, $num2); |
|
781 | 194 | } else if ($opCode === Opcodes::OP_SUB) { |
|
782 | 12 | $num = $this->math->sub($num1, $num2); |
|
783 | 182 | } else if ($opCode === Opcodes::OP_BOOLAND) { |
|
784 | 16 | $num = $this->math->cmp($num1, $zero) !== 0 && $this->math->cmp($num2, $zero) !== 0; |
|
785 | 166 | } else if ($opCode === Opcodes::OP_BOOLOR) { |
|
786 | 16 | $num = $this->math->cmp($num1, $zero) !== 0 || $this->math->cmp($num2, $zero) !== 0; |
|
787 | 150 | } elseif ($opCode === Opcodes::OP_NUMEQUAL) { |
|
788 | 56 | $num = $this->math->cmp($num1, $num2) === 0; |
|
789 | 110 | } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) { |
|
790 | 8 | $num = $this->math->cmp($num1, $num2) === 0; |
|
791 | 102 | } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) { |
|
792 | 10 | $num = $this->math->cmp($num1, $num2) !== 0; |
|
793 | 92 | } elseif ($opCode === Opcodes::OP_LESSTHAN) { |
|
794 | 16 | $num = $this->math->cmp($num1, $num2) < 0; |
|
795 | 76 | } elseif ($opCode === Opcodes::OP_GREATERTHAN) { |
|
796 | 16 | $num = $this->math->cmp($num1, $num2) > 0; |
|
797 | 60 | } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) { |
|
798 | 16 | $num = $this->math->cmp($num1, $num2) <= 0; |
|
799 | 44 | } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) { |
|
800 | 16 | $num = $this->math->cmp($num1, $num2) >= 0; |
|
801 | 28 | } elseif ($opCode === Opcodes::OP_MIN) { |
|
802 | 14 | $num = ($this->math->cmp($num1, $num2) <= 0) ? $num1 : $num2; |
|
803 | } else { |
||
804 | 14 | $num = ($this->math->cmp($num1, $num2) >= 0) ? $num1 : $num2; |
|
805 | } |
||
806 | |||
807 | 242 | $mainStack->pop(); |
|
808 | 242 | $mainStack->pop(); |
|
809 | 242 | $buffer = Number::int(gmp_strval($num, 10))->getBuffer(); |
|
810 | 242 | $mainStack->push($buffer); |
|
811 | |||
812 | 242 | if ($opCode === Opcodes::OP_NUMEQUALVERIFY) { |
|
813 | 8 | if ($this->castToBool($mainStack[-1])) { |
|
814 | 8 | $mainStack->pop(); |
|
815 | } else { |
||
816 | throw new \RuntimeException('NUM EQUAL VERIFY error'); |
||
817 | } |
||
818 | } |
||
819 | 242 | break; |
|
820 | |||
821 | 892 | case Opcodes::OP_WITHIN: |
|
822 | 30 | if (count($mainStack) < 3) { |
|
823 | 2 | throw new \RuntimeException('Invalid stack operation'); |
|
824 | } |
||
825 | |||
826 | 28 | $num1 = Number::buffer($mainStack[-3], $minimal)->getGmp(); |
|
827 | 26 | $num2 = Number::buffer($mainStack[-2], $minimal)->getGmp(); |
|
828 | 24 | $num3 = Number::buffer($mainStack[-1], $minimal)->getGmp(); |
|
829 | |||
830 | 22 | $value = $this->math->cmp($num2, $num1) <= 0 && $this->math->cmp($num1, $num3) < 0; |
|
831 | 22 | $mainStack->pop(); |
|
832 | 22 | $mainStack->pop(); |
|
833 | 22 | $mainStack->pop(); |
|
834 | 22 | $mainStack->push($value ? $this->vchTrue : $this->vchFalse); |
|
835 | 22 | break; |
|
836 | |||
837 | // Hash operation |
||
838 | 862 | case Opcodes::OP_RIPEMD160: |
|
839 | 850 | case Opcodes::OP_SHA1: |
|
840 | 834 | case Opcodes::OP_SHA256: |
|
841 | 822 | case Opcodes::OP_HASH160: |
|
842 | 682 | case Opcodes::OP_HASH256: |
|
843 | 280 | if ($mainStack->isEmpty()) { |
|
844 | 22 | throw new \RuntimeException('Invalid stack operation'); |
|
845 | } |
||
846 | |||
847 | 258 | $buffer = $mainStack[-1]; |
|
848 | 258 | if ($opCode === Opcodes::OP_RIPEMD160) { |
|
849 | 10 | $hash = Hash::ripemd160($buffer); |
|
850 | 250 | } elseif ($opCode === Opcodes::OP_SHA1) { |
|
851 | 12 | $hash = Hash::sha1($buffer); |
|
852 | 238 | } elseif ($opCode === Opcodes::OP_SHA256) { |
|
853 | 12 | $hash = Hash::sha256($buffer); |
|
854 | 230 | } elseif ($opCode === Opcodes::OP_HASH160) { |
|
855 | 220 | $hash = Hash::sha256ripe160($buffer); |
|
856 | } else { |
||
857 | 10 | $hash = Hash::sha256d($buffer); |
|
858 | } |
||
859 | |||
860 | 258 | $mainStack->pop(); |
|
861 | 258 | $mainStack->push($hash); |
|
862 | 258 | break; |
|
863 | |||
864 | 668 | case Opcodes::OP_CODESEPARATOR: |
|
865 | 2 | $hashStartPos = $parser->getPosition(); |
|
866 | 2 | break; |
|
867 | |||
868 | 666 | case Opcodes::OP_CHECKSIG: |
|
869 | 442 | case Opcodes::OP_CHECKSIGVERIFY: |
|
870 | 242 | if (count($mainStack) < 2) { |
|
871 | 14 | throw new \RuntimeException('Invalid stack operation'); |
|
872 | } |
||
873 | |||
874 | 238 | $vchPubKey = $mainStack[-1]; |
|
875 | 238 | $vchSig = $mainStack[-2]; |
|
876 | |||
877 | 238 | $scriptCode = new Script($script->getBuffer()->slice($hashStartPos)); |
|
878 | |||
879 | 238 | $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags); |
|
880 | |||
881 | 190 | $mainStack->pop(); |
|
882 | 190 | $mainStack->pop(); |
|
883 | 190 | $mainStack->push($success ? $this->vchTrue : $this->vchFalse); |
|
884 | |||
885 | 190 | if (!$success && ($flags & self::VERIFY_NULLFAIL) && $vchSig->getSize() > 0) { |
|
886 | 2 | throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Signature must be zero for failed OP_CHECK(MULTIS)SIG operation'); |
|
887 | } |
||
888 | |||
889 | 188 | if ($opCode === Opcodes::OP_CHECKSIGVERIFY) { |
|
890 | 8 | if ($success) { |
|
891 | 8 | $mainStack->pop(); |
|
892 | } else { |
||
893 | throw new \RuntimeException('Checksig verify'); |
||
894 | } |
||
895 | } |
||
896 | 188 | break; |
|
897 | |||
898 | 438 | case Opcodes::OP_CHECKMULTISIG: |
|
899 | 262 | case Opcodes::OP_CHECKMULTISIGVERIFY: |
|
900 | 250 | $i = 1; |
|
901 | 250 | if (count($mainStack) < $i) { |
|
902 | 2 | throw new \RuntimeException('Invalid stack operation'); |
|
903 | } |
||
904 | |||
905 | 248 | $keyCount = Number::buffer($mainStack[-$i], $minimal)->getInt(); |
|
906 | 244 | if ($keyCount < 0 || $keyCount > 20) { |
|
907 | 4 | throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20'); |
|
908 | } |
||
909 | |||
910 | 240 | $opCount += $keyCount; |
|
911 | 240 | $this->checkOpcodeCount($opCount); |
|
912 | |||
913 | // Extract positions of the keys, and signatures, from the stack. |
||
914 | 240 | $ikey = ++$i; |
|
915 | 240 | $ikey2 = $keyCount + 2; |
|
916 | 240 | $i += $keyCount; |
|
917 | 240 | if (count($mainStack) < $i) { |
|
918 | 2 | throw new \RuntimeException('Invalid stack operation'); |
|
919 | } |
||
920 | |||
921 | 238 | $sigCount = Number::buffer($mainStack[-$i], $minimal)->getInt(); |
|
922 | 232 | if ($sigCount < 0 || $sigCount > $keyCount) { |
|
923 | 4 | throw new \RuntimeException('Invalid Signature count'); |
|
924 | } |
||
925 | |||
926 | 228 | $isig = ++$i; |
|
927 | 228 | $i += $sigCount; |
|
928 | |||
929 | // Extract the script since the last OP_CODESEPARATOR |
||
930 | 228 | $scriptCode = new Script($script->getBuffer()->slice($hashStartPos)); |
|
931 | |||
932 | 228 | $fSuccess = true; |
|
933 | 228 | while ($fSuccess && $sigCount > 0) { |
|
934 | // Fetch the signature and public key |
||
935 | 108 | $sig = $mainStack[-$isig]; |
|
936 | 106 | $pubkey = $mainStack[-$ikey]; |
|
937 | |||
938 | 106 | if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) { |
|
939 | 52 | $isig++; |
|
940 | 52 | $sigCount--; |
|
941 | } |
||
942 | |||
943 | 94 | $ikey++; |
|
944 | 94 | $keyCount--; |
|
945 | |||
946 | // If there are more signatures left than keys left, |
||
947 | // then too many signatures have failed. Exit early, |
||
948 | // without checking any further signatures. |
||
949 | 94 | if ($sigCount > $keyCount) { |
|
950 | 46 | $fSuccess = false; |
|
951 | } |
||
952 | } |
||
953 | |||
954 | 210 | while ($i-- > 1) { |
|
955 | // If the operation failed, we require that all signatures must be empty vector |
||
956 | 210 | if (!$fSuccess && ($flags & self::VERIFY_NULLFAIL) && !$ikey2 && $mainStack[-1]->getSize() > 0) { |
|
957 | 4 | throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Bad signature must be empty vector'); |
|
958 | } |
||
959 | |||
960 | 210 | if ($ikey2 > 0) { |
|
961 | 210 | $ikey2--; |
|
962 | } |
||
963 | |||
964 | 210 | $mainStack->pop(); |
|
965 | } |
||
966 | |||
967 | // A bug causes CHECKMULTISIG to consume one extra argument |
||
968 | // whose contents were not checked in any way. |
||
969 | // |
||
970 | // Unfortunately this is a potential source of mutability, |
||
971 | // so optionally verify it is exactly equal to zero prior |
||
972 | // to removing it from the stack. |
||
973 | 206 | if ($mainStack->isEmpty()) { |
|
974 | 2 | throw new \RuntimeException('Invalid stack operation'); |
|
975 | } |
||
976 | |||
977 | 206 | if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) { |
|
978 | 6 | throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0'); |
|
979 | } |
||
980 | |||
981 | 200 | $mainStack->pop(); |
|
982 | 200 | $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse); |
|
983 | |||
984 | 200 | if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) { |
|
985 | 70 | if ($fSuccess) { |
|
986 | 70 | $mainStack->pop(); |
|
987 | } else { |
||
988 | throw new \RuntimeException('OP_CHECKMULTISIG verify'); |
||
989 | } |
||
990 | } |
||
991 | 200 | break; |
|
992 | |||
993 | default: |
||
994 | 188 | throw new \RuntimeException('Opcode not found'); |
|
995 | } |
||
996 | |||
997 | 2138 | if (count($mainStack) + count($altStack) > 1000) { |
|
998 | 2382 | throw new \RuntimeException('Invalid stack size, exceeds 1000'); |
|
999 | } |
||
1000 | } |
||
1001 | } |
||
1002 | |||
1003 | 2396 | if (count($vfStack) !== 0) { |
|
1004 | 12 | throw new \RuntimeException('Unbalanced conditional at script end'); |
|
1005 | } |
||
1006 | |||
1007 | 2386 | return true; |
|
1008 | 884 | } catch (ScriptRuntimeException $e) { |
|
1009 | // echo "\n Runtime: " . $e->getMessage() . "\n" . $e->getTraceAsString() . PHP_EOL; |
||
1010 | // Failure due to script tags, can access flag: $e->getFailureFlag() |
||
1011 | 172 | return false; |
|
1012 | 712 | } catch (\Exception $e) { |
|
1013 | // echo "\n General: " . $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL; |
||
1014 | 712 | return false; |
|
1015 | } |
||
1016 | } |
||
1017 | } |
||
1018 |
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: