Example #1
0
 /**
  * @param ScriptInterface $scriptSig
  * @param ScriptInterface $scriptPubKey
  * @param $nInputToSign
  * @return bool
  * @throws \Exception
  */
 public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $nInputToSign)
 {
     $this->inputToSign = $nInputToSign;
     if (!$this->setScript($scriptSig)->run()) {
         return false;
     }
     $mainStack = $this->state->getMainStack();
     $stackCopy = new ScriptStack();
     if ($this->flags->checkFlags(InterpreterInterface::VERIFY_P2SH)) {
         $stackCopy = $this->state->cloneMainStack();
     }
     if (!$this->setScript($scriptPubKey)->run()) {
         return false;
     }
     if ($mainStack->size() == 0) {
         return false;
     }
     if (false === $this->castToBool($mainStack->top(-1))) {
         return false;
     }
     $verifier = new OutputClassifier($scriptPubKey);
     if ($this->flags->checkFlags(InterpreterInterface::VERIFY_P2SH) && $verifier->isPayToScriptHash()) {
         if (!$scriptSig->isPushOnly()) {
             return false;
         }
         // Restore mainStack to how it was after evaluating scriptSig
         $mainStack = $this->state->restoreMainStack($stackCopy)->getMainStack();
         if ($mainStack->size() == 0) {
             return false;
         }
         // Load redeemscript as the scriptPubKey
         $scriptPubKey = new Script($mainStack->top(-1));
         $mainStack->pop();
         if (!$this->setScript($scriptPubKey)->run()) {
             return false;
         }
     }
     return true;
 }
Example #2
0
 /**
  * @return bool
  */
 private function run()
 {
     $math = $this->math;
     $this->hashStartPos = 0;
     $this->opCount = 0;
     $parser = $this->script->getScriptParser();
     if ($this->script->getBuffer()->getSize() > 10000) {
         return false;
     }
     try {
         foreach ($parser as $operation) {
             $opCode = $operation->getOp();
             $pushData = $operation->getData();
             $fExec = $this->checkExec();
             // If pushdata was written to,
             if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
                 throw new \RuntimeException('Error - push size');
             }
             // OP_RESERVED should not count towards opCount
             if ($opCode > Opcodes::OP_16 && ++$this->opCount) {
                 $this->checkOpcodeCount();
             }
             if (in_array($opCode, $this->disabledOps, true)) {
                 throw new \RuntimeException('Disabled Opcode');
             }
             if ($fExec && $operation->isPush()) {
                 // In range of a pushdata opcode
                 if ($this->minimalPush && !$this->checkMinimalPush($opCode, $pushData)) {
                     throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
                 }
                 $this->mainStack->push($pushData);
                 // echo " - [pushed '" . $pushData->getHex() . "']\n";
             } elseif ($fExec || $opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF) {
                 // echo "OPCODE - " . $this->script->getOpCodes()->getOp($opCode) . "\n";
                 switch ($opCode) {
                     case Opcodes::OP_1NEGATE:
                     case Opcodes::OP_1:
                     case Opcodes::OP_2:
                     case Opcodes::OP_3:
                     case Opcodes::OP_4:
                     case Opcodes::OP_5:
                     case Opcodes::OP_6:
                     case Opcodes::OP_7:
                     case Opcodes::OP_8:
                     case Opcodes::OP_9:
                     case Opcodes::OP_10:
                     case Opcodes::OP_11:
                     case Opcodes::OP_12:
                     case Opcodes::OP_13:
                     case Opcodes::OP_14:
                     case Opcodes::OP_15:
                     case Opcodes::OP_16:
                         $num = $opCode - (Opcodes::OP_1 - 1);
                         $this->mainStack->push(Number::int($num)->getBuffer());
                         break;
                     case Opcodes::OP_CHECKLOCKTIMEVERIFY:
                         if (!$this->flags->checkFlags(self::VERIFY_CHECKLOCKTIMEVERIFY)) {
                             if ($this->flags->checkFlags(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                                 throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
                             }
                             break;
                         }
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation - CLTV');
                         }
                         $lockTime = Number::buffer($this->mainStack[-1], $this->minimalPush, 5, $math);
                         if (!$this->checkLockTime($lockTime)) {
                             throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
                         }
                         break;
                     case Opcodes::OP_CHECKSEQUENCEVERIFY:
                         if (!$this->flags->checkFlags(self::VERIFY_CHECKSEQUENCEVERIFY)) {
                             if ($this->flags->checkFlags(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                                 throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
                             }
                             break;
                         }
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation - CSV');
                         }
                         $sequence = Number::buffer($this->mainStack[-1], $this->minimalPush, 5, $math);
                         $nSequence = $sequence->getInt();
                         if ($math->cmp($nSequence, 0) < 0) {
                             throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
                         }
                         if ($math->cmp($math->bitwiseAnd($nSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), '0') !== 0) {
                             break;
                         }
                         if (!$this->checkSequence($sequence)) {
                             throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied locktime');
                         }
                         break;
                     case Opcodes::OP_NOP1:
                     case Opcodes::OP_NOP4:
                     case Opcodes::OP_NOP5:
                     case Opcodes::OP_NOP6:
                     case Opcodes::OP_NOP7:
                     case Opcodes::OP_NOP8:
                     case Opcodes::OP_NOP9:
                     case Opcodes::OP_NOP10:
                         if ($this->flags->checkFlags(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                             throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
                         }
                         break;
                     case Opcodes::OP_NOP:
                         break;
                     case Opcodes::OP_IF:
                     case Opcodes::OP_NOTIF:
                         // <expression> if [statements] [else [statements]] endif
                         $value = false;
                         if ($fExec) {
                             if ($this->mainStack->isEmpty()) {
                                 throw new \RuntimeException('Unbalanced conditional');
                             }
                             // todo
                             $buffer = Number::buffer($this->mainStack->pop(), $this->minimalPush)->getBuffer();
                             $value = $this->castToBool($buffer);
                             if ($opCode === Opcodes::OP_NOTIF) {
                                 $value = !$value;
                             }
                         }
                         $this->vfStack->push($value ? $this->vchTrue : $this->vchFalse);
                         break;
                     case Opcodes::OP_ELSE:
                         if ($this->vfStack->isEmpty()) {
                             throw new \RuntimeException('Unbalanced conditional');
                         }
                         $this->vfStack[-1] = !$this->vfStack->end() ? $this->vchTrue : $this->vchFalse;
                         break;
                     case Opcodes::OP_ENDIF:
                         if ($this->vfStack->isEmpty()) {
                             throw new \RuntimeException('Unbalanced conditional');
                         }
                         break;
                     case Opcodes::OP_VERIFY:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $value = $this->castToBool($this->mainStack[-1]);
                         if (!$value) {
                             throw new \RuntimeException('Error: verify');
                         }
                         $this->mainStack->pop();
                         break;
                     case Opcodes::OP_RESERVED:
                         // todo
                         break;
                     case Opcodes::OP_TOALTSTACK:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
                         }
                         $this->altStack->push($this->mainStack->pop());
                         break;
                     case Opcodes::OP_FROMALTSTACK:
                         if ($this->altStack->isEmpty()) {
                             throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
                         }
                         $this->mainStack->push($this->altStack->pop());
                         break;
                     case Opcodes::OP_IFDUP:
                         // If top value not zero, duplicate it.
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_IFDUP');
                         }
                         $vch = $this->mainStack[-1];
                         if ($this->castToBool($vch)) {
                             $this->mainStack->push($vch);
                         }
                         break;
                     case Opcodes::OP_DEPTH:
                         $num = count($this->mainStack);
                         if ($num === 0) {
                             $depth = $this->vchFalse;
                         } else {
                             $depth = Number::int($num)->getBuffer();
                         }
                         $this->mainStack->push($depth);
                         break;
                     case Opcodes::OP_DROP:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_DROP');
                         }
                         $this->mainStack->pop();
                         break;
                     case Opcodes::OP_DUP:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_DUP');
                         }
                         $vch = $this->mainStack[-1];
                         $this->mainStack->push($vch);
                         break;
                     case Opcodes::OP_NIP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_NIP');
                         }
                         unset($this->mainStack[-2]);
                         break;
                     case Opcodes::OP_OVER:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_OVER');
                         }
                         $vch = $this->mainStack[-2];
                         $this->mainStack->push($vch);
                         break;
                     case Opcodes::OP_ROT:
                         if (count($this->mainStack) < 3) {
                             throw new \RuntimeException('Invalid stack operation OP_ROT');
                         }
                         $this->mainStack->swap(-3, -2);
                         $this->mainStack->swap(-2, -1);
                         break;
                     case Opcodes::OP_SWAP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_SWAP');
                         }
                         $this->mainStack->swap(-2, -1);
                         break;
                     case Opcodes::OP_TUCK:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_TUCK');
                         }
                         $vch = $this->mainStack[-1];
                         $this->mainStack->add(count($this->mainStack) - 1 - 2, $vch);
                         break;
                     case Opcodes::OP_PICK:
                     case Opcodes::OP_ROLL:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_PICK');
                         }
                         $n = Number::buffer($this->mainStack[-1], $this->minimalPush, 4)->getInt();
                         $this->mainStack->pop();
                         if ($math->cmp($n, 0) < 0 || $math->cmp($n, count($this->mainStack)) >= 0) {
                             throw new \RuntimeException('Invalid stack operation OP_PICK');
                         }
                         $pos = (int) $math->sub($math->sub(0, $n), 1);
                         $vch = $this->mainStack[$pos];
                         if ($opCode === Opcodes::OP_ROLL) {
                             unset($this->mainStack[$pos]);
                         }
                         $this->mainStack->push($vch);
                         break;
                     case Opcodes::OP_2DROP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_2DROP');
                         }
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         break;
                     case Opcodes::OP_2DUP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_2DUP');
                         }
                         $string1 = $this->mainStack[-2];
                         $string2 = $this->mainStack[-1];
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         break;
                     case Opcodes::OP_3DUP:
                         if (count($this->mainStack) < 3) {
                             throw new \RuntimeException('Invalid stack operation OP_3DUP');
                         }
                         $string1 = $this->mainStack[-3];
                         $string2 = $this->mainStack[-2];
                         $string3 = $this->mainStack[-1];
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         $this->mainStack->push($string3);
                         break;
                     case Opcodes::OP_2OVER:
                         if (count($this->mainStack) < 4) {
                             throw new \RuntimeException('Invalid stack operation OP_2OVER');
                         }
                         $string1 = $this->mainStack[-4];
                         $string2 = $this->mainStack[-3];
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         break;
                     case Opcodes::OP_2ROT:
                         if (count($this->mainStack) < 6) {
                             throw new \RuntimeException('Invalid stack operation OP_2ROT');
                         }
                         $string1 = $this->mainStack[-6];
                         $string2 = $this->mainStack[-5];
                         unset($this->mainStack[-6], $this->mainStack[-5]);
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         break;
                     case Opcodes::OP_2SWAP:
                         if (count($this->mainStack) < 4) {
                             throw new \RuntimeException('Invalid stack operation OP_2SWAP');
                         }
                         $this->mainStack->swap(-3, -1);
                         $this->mainStack->swap(-4, -2);
                         break;
                     case Opcodes::OP_SIZE:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_SIZE');
                         }
                         // todo: Int sizes?
                         $vch = $this->mainStack[-1];
                         $this->mainStack->push(Number::int($vch->getSize())->getBuffer());
                         break;
                     case Opcodes::OP_EQUAL:
                     case Opcodes::OP_EQUALVERIFY:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_EQUAL');
                         }
                         $vch1 = $this->mainStack[-2];
                         $vch2 = $this->mainStack[-1];
                         $equal = $vch1->getBinary() === $vch2->getBinary();
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->push($equal ? $this->vchTrue : $this->vchFalse);
                         if ($opCode === Opcodes::OP_EQUALVERIFY) {
                             if ($equal) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('Error EQUALVERIFY');
                             }
                         }
                         break;
                         // Arithmetic operations
                     // Arithmetic operations
                     case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
                         if ($this->mainStack->isEmpty()) {
                             throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
                         }
                         $num = Number::buffer($this->mainStack[-1], $this->minimalPush)->getInt();
                         if ($opCode === Opcodes::OP_1ADD) {
                             $num = $math->add($num, '1');
                         } elseif ($opCode === Opcodes::OP_1SUB) {
                             $num = $math->sub($num, '1');
                         } elseif ($opCode === Opcodes::OP_2MUL) {
                             $num = $math->mul(2, $num);
                         } elseif ($opCode === Opcodes::OP_NEGATE) {
                             $num = $math->sub(0, $num);
                         } elseif ($opCode === Opcodes::OP_ABS) {
                             if ($math->cmp($num, '0') < 0) {
                                 $num = $math->sub(0, $num);
                             }
                         } elseif ($opCode === Opcodes::OP_NOT) {
                             $num = (int) $math->cmp($num, '0') === 0;
                         } else {
                             // is OP_0NOTEQUAL
                             $num = (int) ($math->cmp($num, '0') !== 0);
                         }
                         $this->mainStack->pop();
                         $buffer = Number::int($num)->getBuffer();
                         $this->mainStack->push($buffer);
                         break;
                     case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
                         if (count($this->mainStack) < 2) {
                             throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
                         }
                         $num1 = Number::buffer($this->mainStack[-2], $this->minimalPush)->getInt();
                         $num2 = Number::buffer($this->mainStack[-1], $this->minimalPush)->getInt();
                         if ($opCode === Opcodes::OP_ADD) {
                             $num = $math->add($num1, $num2);
                         } else {
                             if ($opCode === Opcodes::OP_SUB) {
                                 $num = $math->sub($num1, $num2);
                             } else {
                                 if ($opCode === Opcodes::OP_BOOLAND) {
                                     $num = $math->cmp($num1, $this->int0->getInt()) !== 0 && $math->cmp($num2, $this->int0->getInt()) !== 0;
                                 } else {
                                     if ($opCode === Opcodes::OP_BOOLOR) {
                                         $num = $math->cmp($num1, $this->int0->getInt()) !== 0 || $math->cmp($num2, $this->int0->getInt()) !== 0;
                                     } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
                                         $num = $math->cmp($num1, $num2) === 0;
                                     } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
                                         $num = $math->cmp($num1, $num2) === 0;
                                     } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
                                         $num = $math->cmp($num1, $num2) !== 0;
                                     } elseif ($opCode === Opcodes::OP_LESSTHAN) {
                                         $num = $math->cmp($num1, $num2) < 0;
                                     } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
                                         $num = $math->cmp($num1, $num2) > 0;
                                     } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
                                         $num = $math->cmp($num1, $num2) <= 0;
                                     } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
                                         $num = $math->cmp($num1, $num2) >= 0;
                                     } elseif ($opCode === Opcodes::OP_MIN) {
                                         $num = $math->cmp($num1, $num2) <= 0 ? $num1 : $num2;
                                     } else {
                                         $num = $math->cmp($num1, $num2) >= 0 ? $num1 : $num2;
                                     }
                                 }
                             }
                         }
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $buffer = Number::int($num)->getBuffer();
                         $this->mainStack->push($buffer);
                         if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
                             if ($this->castToBool($this->mainStack[-1])) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('NUM EQUAL VERIFY error');
                             }
                         }
                         break;
                     case Opcodes::OP_WITHIN:
                         if (count($this->mainStack) < 3) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $num1 = Number::buffer($this->mainStack[-3], $this->minimalPush)->getInt();
                         $num2 = Number::buffer($this->mainStack[-2], $this->minimalPush)->getInt();
                         $num3 = Number::buffer($this->mainStack[-1], $this->minimalPush)->getInt();
                         $value = $math->cmp($num2, $num1) <= 0 && $math->cmp($num1, $num3) < 0;
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->push($value ? $this->vchFalse : $this->vchTrue);
                         break;
                         // Hash operation
                     // Hash operation
                     case Opcodes::OP_RIPEMD160:
                     case Opcodes::OP_SHA1:
                     case Opcodes::OP_SHA256:
                     case Opcodes::OP_HASH160:
                     case Opcodes::OP_HASH256:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $buffer = $this->mainStack[-1];
                         if ($opCode === Opcodes::OP_RIPEMD160) {
                             $hash = Hash::ripemd160($buffer);
                         } elseif ($opCode === Opcodes::OP_SHA1) {
                             $hash = Hash::sha1($buffer);
                         } elseif ($opCode === Opcodes::OP_SHA256) {
                             $hash = Hash::sha256($buffer);
                         } elseif ($opCode === Opcodes::OP_HASH160) {
                             $hash = Hash::sha256ripe160($buffer);
                         } else {
                             $hash = Hash::sha256d($buffer);
                         }
                         $this->mainStack->pop();
                         $this->mainStack->push($hash);
                         break;
                     case Opcodes::OP_CODESEPARATOR:
                         $this->hashStartPos = $parser->getPosition();
                         break;
                     case Opcodes::OP_CHECKSIG:
                     case Opcodes::OP_CHECKSIGVERIFY:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $vchPubKey = $this->mainStack[-1];
                         $vchSig = $this->mainStack[-2];
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->push($success ? $this->vchTrue : $this->vchFalse);
                         if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
                             if ($success) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('Checksig verify');
                             }
                         }
                         break;
                     case Opcodes::OP_CHECKMULTISIG:
                     case Opcodes::OP_CHECKMULTISIGVERIFY:
                         $i = 1;
                         if (count($this->mainStack) < $i) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $keyCount = Number::buffer($this->mainStack[-$i], $this->minimalPush)->getInt();
                         if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
                             throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20');
                         }
                         $this->opCount += $keyCount;
                         $this->checkOpcodeCount();
                         // Extract positions of the keys, and signatures, from the stack.
                         $ikey = ++$i;
                         $i += $keyCount;
                         /** @var int $i */
                         if (count($this->mainStack) < $i) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $sigCount = Number::buffer($this->mainStack[-$i], $this->minimalPush)->getInt();
                         if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
                             throw new \RuntimeException('Invalid Signature count');
                         }
                         $isig = ++$i;
                         $i += $sigCount;
                         // Extract the script since the last OP_CODESEPARATOR
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $fSuccess = true;
                         while ($fSuccess && $sigCount > 0) {
                             // Fetch the signature and public key
                             $sig = $this->mainStack[-$isig];
                             $pubkey = $this->mainStack[-$ikey];
                             // Erase the signature and public key.
                             unset($this->mainStack[-$isig], $this->mainStack[-$ikey]);
                             // Decrement $i, since we are consuming stack values.
                             $i -= 2;
                             if ($this->checkSig($scriptCode, $sig, $pubkey)) {
                                 $isig++;
                                 $sigCount--;
                             }
                             $ikey++;
                             $keyCount--;
                             // If there are more signatures left than keys left,
                             // then too many signatures have failed. Exit early,
                             // without checking any further signatures.
                             if ($sigCount > $keyCount) {
                                 $fSuccess = false;
                             }
                         }
                         while ($i-- > 1) {
                             $this->mainStack->pop();
                         }
                         // A bug causes CHECKMULTISIG to consume one extra argument
                         // whose contents were not checked in any way.
                         //
                         // Unfortunately this is a potential source of mutability,
                         // so optionally verify it is exactly equal to zero prior
                         // to removing it from the stack.
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         if ($this->flags->checkFlags(self::VERIFY_NULL_DUMMY) && $this->mainStack[-1]->getSize()) {
                             throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
                         }
                         $this->mainStack->pop();
                         $this->mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
                         if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
                             if ($fSuccess) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('OP_CHECKMULTISIG verify');
                             }
                         }
                         break;
                     default:
                         throw new \RuntimeException('Opcode not found');
                 }
                 if (count($this->mainStack) + count($this->altStack) > 1000) {
                     throw new \RuntimeException('Invalid stack size, exceeds 1000');
                 }
             }
         }
         if (!$this->vfStack->end() === 0) {
             throw new \RuntimeException('Unbalanced conditional at script end');
         }
         return true;
     } catch (ScriptRuntimeException $e) {
         // echo "\n Runtime: " . $e->getMessage() . "\n";
         // Failure due to script tags, can access flag: $e->getFailureFlag()
         return false;
     } catch (\Exception $e) {
         // echo "\n General: " . $e->getMessage() ;
         return false;
     }
 }
Example #3
0
 /**
  * @param TransactionInterface $tx
  * @param Flags $flags
  * @param array $scripts
  * @return string
  */
 private function dispatch(TransactionInterface $tx, Flags $flags, array $scripts)
 {
     $this->blockingZmq->send(json_encode(['txid' => $tx->getTxId()->getHex(), 'tx' => $tx->getHex(), 'flags' => $flags->getFlags(), 'scripts' => $scripts]));
     $result = $this->blockingZmq->recv();
     return (bool) $result;
 }
Example #4
0
 /**
  * @param int $flag
  * @return bool
  */
 public function checkFlag($flag)
 {
     return $this->math->cmp($this->math->bitwiseAnd($this->flags->getFlags(), self::UPDATE_MASK), $flag) === 0;
 }
 /**
  * @param TransactionInterface $tx
  * @param ScriptInterface $scriptPubKey
  * @param int $nInputToSign
  * @return bool
  */
 public function verify(TransactionInterface $tx, ScriptInterface $scriptPubKey, $nInputToSign)
 {
     $error = 0;
     return (bool) bitcoinconsensus_verify_script($scriptPubKey->getBinary(), $tx->getBinary(), $nInputToSign, $this->flags->getFlags(), $error);
 }