/**
  * @param EcAdapterInterface $ecAdapter
  * @param ScriptInterface $outputScript
  * @param RedeemScript $redeemScript
  */
 public function __construct(EcAdapterInterface $ecAdapter, ScriptInterface $outputScript, RedeemScript $redeemScript = null)
 {
     $classifier = new OutputClassifier($outputScript);
     $this->scriptType = $this->prevOutType = $classifier->classify();
     // Reclassify if the output is P2SH, so we know how to sign it.
     if ($this->scriptType == OutputClassifier::PAYTOSCRIPTHASH) {
         if (null === $redeemScript) {
             throw new \InvalidArgumentException('Redeem script is required when output is P2SH');
         }
         $rsClassifier = new OutputClassifier($redeemScript);
         $this->scriptType = $rsClassifier->classify();
     }
     // Gather public keys from redeemScript / outputScript
     $this->ecAdapter = $ecAdapter;
     $this->redeemScript = $redeemScript;
     $this->prevOutScript = $outputScript;
     // According to scriptType, extract public keys
     $this->execForInputTypes(function () {
         // For pay to pub key hash - nothing useful in output script
         $this->publicKeys = [];
     }, function () {
         // For pay to pub key - we can extract this from the output script
         $chunks = $this->prevOutScript->getScriptParser()->parse();
         $this->publicKeys = [PublicKeyFactory::fromHex($chunks[0]->getHex(), $this->ecAdapter)];
     }, function () {
         // Multisig - refer to the redeemScript
         $this->publicKeys = $this->redeemScript->getKeys();
     });
 }
示例#2
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $publicKeys = [];
     $parse = $script->getScriptParser()->decode();
     if (count($parse) < 4) {
         throw new \InvalidArgumentException('Malformed multisig script');
     }
     $mCode = $parse[0]->getOp();
     $nCode = $parse[count($parse) - 2]->getOp();
     $this->m = (int) \BitWasp\Bitcoin\Script\decodeOpN($mCode);
     foreach (array_slice($parse, 1, -2) as $key) {
         /** @var \BitWasp\Bitcoin\Script\Parser\Operation $key */
         if (!$key->isPush()) {
             throw new \RuntimeException('Malformed multisig script');
         }
         $publicKeys[] = PublicKeyFactory::fromHex($key->getData());
     }
     $n = \BitWasp\Bitcoin\Script\decodeOpN($nCode);
     $this->n = count($publicKeys);
     if ($this->n === 0 || $this->n !== $n) {
         throw new \LogicException('No public keys found in script');
     }
     $this->script = $script;
     $this->keys = $publicKeys;
 }
示例#3
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $this->script = $script;
     $chunks = $script->getScriptParser()->decode();
     if (count($chunks) < 1 || !$chunks[0]->isPush()) {
         throw new \InvalidArgumentException('Malformed pay-to-pubkey script');
     }
     $this->publicKey = PublicKeyFactory::fromHex($chunks[0]->getData());
 }
示例#4
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $this->script = $script;
     $chunks = $this->script->getScriptParser()->decode();
     if (count($chunks) < 5 || !$chunks[2]->isPush()) {
         throw new \RuntimeException('Malformed pay-to-pubkey-hash script');
     }
     /** @var Buffer $hash */
     $hash = $chunks[2]->getData();
     $this->hash = $hash;
 }
示例#5
0
 /**
  * @param ScriptInterface $script
  * @param NetworkInterface $network
  * @return String
  * @throws \RuntimeException
  */
 public static function getAssociatedAddress(ScriptInterface $script, NetworkInterface $network = null)
 {
     $classifier = new OutputClassifier($script);
     $network = $network ?: Bitcoin::getNetwork();
     try {
         if ($classifier->isPayToPublicKey()) {
             $address = PublicKeyFactory::fromHex($script->getScriptParser()->decode()[0]->getData())->getAddress();
         } else {
             $address = self::fromOutputScript($script);
         }
         return Base58::encodeCheck(Buffer::hex($network->getAddressByte() . $address->getHash(), 21));
     } catch (\Exception $e) {
         throw new \RuntimeException('No address associated with this script type');
     }
 }
示例#6
0
 /**
  * @param ScriptInterface $script
  * @return RedeemScript
  */
 public static function fromScript(ScriptInterface $script)
 {
     $publicKeys = [];
     $parse = $script->getScriptParser()->parse();
     $opCodes = $script->getOpcodes();
     $m = $opCodes->getOpByName($parse[0]) - $opCodes->getOpByName('OP_1') + 1;
     foreach (array_slice($parse, 1, -2) as $item) {
         if (!$item instanceof Buffer) {
             throw new \RuntimeException('Unable to load public key');
         }
         $publicKeys[] = PublicKeyFactory::fromHex($item->getHex());
     }
     if (count($publicKeys) == 0) {
         throw new \LogicException('No public keys found in script');
     }
     return new self($m, $publicKeys);
 }
 /**
  * @return bool
  */
 public function isMultisig()
 {
     $opCodes = $this->script->getOpcodes();
     $count = count($this->evalScript);
     if ($count <= 3) {
         return false;
     }
     $mOp = $this->evalScript[0];
     $nOp = $this->evalScript[$count - 2];
     $lastOp = $this->evalScript[$count - 1];
     $keys = array_slice($this->evalScript, 1, -2);
     $keysValid = function () use($keys) {
         $valid = true;
         foreach ($keys as $key) {
             $valid &= $key instanceof Buffer && PublicKey::isCompressedOrUncompressed($key);
         }
         return $valid;
     };
     return $count >= 2 && is_string($mOp) && is_string($nOp) && is_string($lastOp) && $opCodes->cmp($opCodes->getOpByName($mOp), 'OP_0') >= 0 && $opCodes->cmp($opCodes->getOpByName($nOp), 'OP_16') <= 0 && $this->evalScript[$count - 1] == 'OP_CHECKMULTISIG' && $keysValid();
 }
示例#8
0
 /**
  * returns a mix of Buffer objects and strings
  *
  * @return Buffer[]|string[]
  */
 public function parse()
 {
     $data = array();
     while ($this->next($opCode, $pushData)) {
         if ($opCode < 1) {
             $push = Buffer::hex('00');
         } elseif ($opCode <= 78) {
             $push = $pushData;
         } else {
             // None of these are pushdatas, so just an opcode
             $push = $this->script->getOpCodes()->getOp($opCode);
         }
         $data[] = $push;
     }
     $this->resetPosition();
     return $data;
 }
示例#9
0
 /**
  * @return SigValues
  */
 public function serializeSignatures()
 {
     static $emptyScript = null;
     static $emptyWitness = null;
     if (is_null($emptyScript) || is_null($emptyWitness)) {
         $emptyScript = new Script();
         $emptyWitness = new ScriptWitness([]);
     }
     /** @var BufferInterface[] $return */
     $outputType = (new OutputClassifier($this->txOut->getScript()))->classify();
     /** @var SigValues $answer */
     $answer = new SigValues($emptyScript, $emptyWitness);
     $serialized = $this->serializeSimpleSig($outputType, $answer);
     $p2sh = false;
     if (!$serialized && $outputType === OutputClassifier::PAYTOSCRIPTHASH) {
         $p2sh = true;
         $outputType = (new OutputClassifier($this->redeemScript))->classify();
         $serialized = $this->serializeSimpleSig($outputType, $answer);
     }
     if (!$serialized && $outputType === OutputClassifier::WITNESS_V0_KEYHASH) {
         $answer = new SigValues($emptyScript, new ScriptWitness([$this->signatures[0]->getBuffer(), $this->publicKeys[0]->getBuffer()]));
     } else {
         if (!$serialized && $outputType === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
             $outputType = (new OutputClassifier($this->witnessScript))->classify();
             $serialized = $this->serializeSimpleSig($outputType, $answer);
             if ($serialized) {
                 $data = [];
                 foreach ($answer->getScriptSig()->getScriptParser()->decode() as $o) {
                     $data[] = $o->getData();
                 }
                 $data[] = $this->witnessScript->getBuffer();
                 $answer = new SigValues($emptyScript, new ScriptWitness($data));
             }
         }
     }
     if ($p2sh) {
         $answer = new SigValues(ScriptFactory::create($answer->getScriptSig()->getBuffer())->push($this->redeemScript->getBuffer())->getScript(), $answer->getScriptWitness());
     }
     return $answer;
 }
示例#10
0
 /**
  * @param BufferInterface $programHash
  * @return bool
  */
 public function isWitness(&$programHash = null)
 {
     $buffer = $this->script->getBuffer();
     $size = $buffer->getSize();
     if ($size < 4 || $size > 34) {
         return false;
     }
     $parser = $this->script->getScriptParser();
     $script = $parser->decode();
     if (count($script) !== 2 || !$script[1]->isPush()) {
         return false;
     }
     $version = $script[0]->getOp();
     if ($version != Opcodes::OP_0 && ($version < Opcodes::OP_1 || $version > Opcodes::OP_16)) {
         return false;
     }
     $witness = $script[1];
     if ($size === $witness->getDataSize() + 2) {
         $programHash = $witness->getData();
         return true;
     }
     return false;
 }
示例#11
0
 /**
  * @return bool
  */
 public function run()
 {
     $math = $this->ecAdapter->getMath();
     $opcodes = $this->script->getOpCodes();
     $flags = $this->flags;
     $mainStack = $this->state->getMainStack();
     $altStack = $this->state->getAltStack();
     $vfStack = $this->state->getVfStack();
     $this->hashStartPos = 0;
     $this->opCount = 0;
     $parser = $this->script->getScriptParser();
     $_bn0 = Buffer::hex('00');
     $_bn1 = Buffer::hex('01');
     if ($this->script->getBuffer()->getSize() > 10000) {
         return false;
     }
     $checkFExec = function () use(&$vfStack) {
         $c = 0;
         $len = $vfStack->end();
         for ($i = 0; $i < $len; $i++) {
             if ($vfStack->top(0 - $len - $i) == true) {
                 $c++;
             }
         }
         return (bool) $c;
     };
     try {
         while ($parser->next($opCode, $pushData) === true) {
             $fExec = !$checkFExec();
             // If pushdata was written to,
             if ($pushData instanceof Buffer && $pushData->getSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
                 throw new \Exception('Error - push size');
             }
             // OP_RESERVED should not count towards opCount
             if ($this->script->getOpcodes()->cmp($opCode, 'OP_16') > 0 && ++$this->opCount) {
                 $this->checkOpcodeCount();
             }
             if ($this->checkDisabledOpcodes) {
                 if ($this->isDisabledOp($opCode)) {
                     throw new \Exception('Disabled Opcode');
                 }
             }
             if ($fExec && $opCode >= 0 && $opcodes->cmp($opCode, 'OP_PUSHDATA4') <= 0) {
                 // In range of a pushdata opcode
                 if ($flags->checkFlags(InterpreterInterface::VERIFY_MINIMALDATA) && !$this->checkMinimalPush($opCode, $pushData)) {
                     throw new ScriptRuntimeException(InterpreterInterface::VERIFY_MINIMALDATA, 'Minimal pushdata required');
                 }
                 $mainStack->push($pushData);
                 //echo " - [pushed '" . $pushData->getHex() . "']\n";
             } elseif ($fExec || $opcodes->isOp($opCode, 'OP_IF') <= 0 && $opcodes->isOp($opCode, 'OP_ENDIF')) {
                 //echo " - [". $opcodes->getOp($opCode) . "]\n";
                 switch ($opCode) {
                     case $opcodes->getOpByName('OP_1NEGATE'):
                     case $opcodes->cmp($opCode, 'OP_1') >= 0 && $opcodes->cmp($opCode, 'OP_16') <= 0:
                         $pushInt = new PushIntOperation($opcodes);
                         $pushInt->op($opCode, $mainStack);
                         break;
                     case $opcodes->cmp($opCode, 'OP_NOP1') >= 0 && $opcodes->cmp($opCode, 'OP_NOP10') <= 0:
                         if ($flags->checkFlags(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                             throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOPS found - this is discouraged');
                         }
                         break;
                     case $opcodes->getOpByName('OP_NOP'):
                     case $opcodes->isOp($opCode, 'OP_IF') || $opcodes->isOp($opCode, 'OP_NOTIF'):
                     case $opcodes->isOp($opCode, 'OP_ELSE') || $opcodes->isOp($opCode, 'OP_ENDIF'):
                     case $opcodes->getOpByName('OP_VERIFY'):
                     case $opcodes->getOpByName('OP_RETURN'):
                         $flowControl = new FlowControlOperation($opcodes, function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         });
                         $flowControl->op($opCode, $mainStack, $vfStack, $fExec);
                         break;
                     case $opcodes->getOpByName('OP_RESERVED'):
                         // todo
                         break;
                     case $opcodes->getOpByName('OP_TOALTSTACK'):
                     case $opcodes->getOpByName('OP_FROMALTSTACK'):
                     case $opcodes->cmp($opCode, 'OP_IFDUP') >= 0 && $opcodes->cmp($opCode, 'OP_TUCK') <= 0:
                     case $opcodes->cmp($opCode, 'OP_2DROP') >= 0 && $opcodes->cmp($opCode, 'OP_2SWAP') <= 0:
                         $stackOper = new StackOperation($opcodes, $this->ecAdapter->getMath(), function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         });
                         $stackOper->op($opCode, $mainStack, $altStack);
                         break;
                     case $opcodes->getOpByName('OP_SIZE'):
                         if ($mainStack->size() < 1) {
                             throw new \Exception('Invalid stack operation OP_SIZE');
                         }
                         // todo: Int sizes?
                         $vch = $mainStack->top(-1);
                         $size = Buffer::hex($math->decHex($vch->getSize()));
                         $mainStack->push($size);
                         break;
                     case $opcodes->getOpByName('OP_EQUAL'):
                         // cscriptnum
                     // cscriptnum
                     case $opcodes->getOpByName('OP_EQUALVERIFY'):
                         //case $this->isOp($opCode, 'OP_NOTEQUAL'): // use OP_NUMNOTEQUAL
                         if ($mainStack->size() < 2) {
                             throw new \Exception('Invalid stack operation OP_EQUAL');
                         }
                         $vch1 = $mainStack->top(-2);
                         $vch2 = $mainStack->top(-1);
                         $equal = $vch1->getBinary() === $vch2->getBinary();
                         // OP_NOTEQUAL is disabled
                         //if ($this->isOp($opCode, 'OP_NOTEQUAL')) {
                         //    $equal = !$equal;
                         //}
                         $mainStack->pop();
                         $mainStack->pop();
                         $mainStack->push($equal ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_EQUALVERIFY')) {
                             if ($equal) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('Error EQUALVERIFY');
                             }
                         }
                         break;
                         // Arithmetic operations
                     // Arithmetic operations
                     case $opcodes->cmp($opCode, 'OP_1ADD') >= 0 && $opcodes->cmp($opCode, 'OP_WITHIN') <= 0:
                         $arithmetic = new ArithmeticOperation($opcodes, $this->ecAdapter->getMath(), function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         }, $_bn0, $_bn1);
                         $arithmetic->op($opCode, $mainStack);
                         break;
                         // Hash operations
                     // Hash operations
                     case $opcodes->cmp($opCode, 'OP_RIPEMD160') >= 0 && $opcodes->cmp($opCode, 'OP_HASH256') <= 0:
                         $hash = new HashOperation($opcodes);
                         $hash->op($opCode, $mainStack);
                         break;
                     case $opcodes->getOpByName('OP_CODESEPARATOR'):
                         $this->hashStartPos = $parser->getPosition();
                         break;
                     case $opcodes->getOpByName('OP_CHECKSIG'):
                     case $opcodes->getOpByName('OP_CHECKSIGVERIFY'):
                         if ($mainStack->size() < 2) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $vchPubKey = $mainStack->top(-1);
                         $vchSig = $mainStack->top(-2);
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
                         $mainStack->pop();
                         $mainStack->pop();
                         $mainStack->push($success ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_CHECKSIGVERIFY')) {
                             if ($success) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('Checksig verify');
                             }
                         }
                         break;
                     case $opcodes->getOpByName('OP_CHECKMULTISIG'):
                     case $opcodes->getOpByName('OP_CHECKMULTISIGVERIFY'):
                         $i = 1;
                         if ($mainStack->size() < $i) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $math = $this->ecAdapter->getMath();
                         $keyCount = $mainStack->top(-$i)->getInt();
                         if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
                             throw new \Exception('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;
                         if ($mainStack->size() < $i) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $sigCount = $mainStack->top(-$i)->getInt();
                         // cscriptnum
                         if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
                             throw new \Exception('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 = $mainStack->top(-$isig);
                             $pubkey = $mainStack->top(-$ikey);
                             // Erase the signature and public key.
                             $mainStack->erase(-$isig);
                             $mainStack->erase(-$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) {
                             $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 ($mainStack->size() < 1) {
                             throw new \Exception('Invalid stack operation');
                         }
                         if ($flags->checkFlags(InterpreterInterface::VERIFY_NULL_DUMMY) && $mainStack->top(-1)->getSize()) {
                             throw new ScriptRuntimeException(InterpreterInterface::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
                         }
                         $mainStack->pop();
                         $mainStack->push($fSuccess ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_CHECKMULTISIGVERIFY')) {
                             if ($fSuccess) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('OP_CHECKMULTISIG verify');
                             }
                         }
                         break;
                     default:
                         throw new \Exception('Opcode not found');
                 }
                 if ($mainStack->size() + $altStack->size() > 1000) {
                     throw new \Exception('Invalid stack size, exceeds 1000');
                 }
             }
         }
         if (!$vfStack->end() == 0) {
             throw new \Exception('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;
     }
 }
示例#12
0
 /**
  * @return string
  */
 public function getHumanReadable()
 {
     return implode(' ', array_map(function (Operation $operation) {
         return $operation->isPush() ? $operation->getData()->getHex() : $this->script->getOpcodes()->getOp($operation->getOp());
     }, $this->decode()));
 }
示例#13
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;
     }
 }
示例#14
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $this->script = $script;
     $this->evalScript = $script->getScriptParser()->parse();
 }
示例#15
0
 /**
  * Create a P2SH output script
  *
  * @param ScriptInterface $p2shScript
  * @return ScriptInterface
  */
 public function payToScriptHash(ScriptInterface $p2shScript)
 {
     return ScriptFactory::create()->op('OP_HASH160')->push($p2shScript->getScriptHash())->op('OP_EQUAL')->getScript();
 }
示例#16
0
 /**
  * @param ScriptInterface $script
  * @return $this
  */
 public function concat(ScriptInterface $script)
 {
     $this->script .= $script->getBinary();
     return $this;
 }
 /**
  * @param ScriptInterface $inputScript
  * @param ScriptInterface $redeemScript
  * @return ScriptInterface
  */
 public function payToScriptHash(ScriptInterface $inputScript, ScriptInterface $redeemScript)
 {
     return ScriptFactory::create($inputScript->getBuffer())->push($redeemScript->getBuffer())->getScript();
 }
示例#18
0
 /**
  * @param array $signatures
  * @param array $publicKeys
  * @return ScriptInterface
  */
 public function makeScriptSig(array $signatures = [], array $publicKeys = [])
 {
     $inputScript = $this->handler->makeScriptSig($signatures, $publicKeys);
     return ScriptFactory::create($inputScript->getBuffer())->push($this->redeemScript->getBuffer())->getScript();
 }
示例#19
0
 /**
  * @param ScriptInterface $scriptSig
  * @return int
  */
 public function countP2shSigOps(ScriptInterface $scriptSig)
 {
     if (!ScriptFactory::scriptPubKey()->classify($this)->isPayToScriptHash()) {
         return $this->countSigOps(true);
     }
     $parser = $scriptSig->getScriptParser();
     $data = null;
     foreach ($parser as $exec) {
         if ($exec->getOp() > Opcodes::OP_16) {
             return 0;
         }
         if ($exec->isPush()) {
             $data = $exec->getData();
         }
     }
     if (!$data instanceof BufferInterface) {
         return 0;
     }
     return (new Script($data))->countSigOps(true);
 }
示例#20
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $this->decoded = $script->getScriptParser()->decode();
 }
示例#21
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);
 }
示例#22
0
 /**
  * P2shScript constructor.
  * @param ScriptInterface $script
  * @param Opcodes|null $opcodes
  */
 public function __construct(ScriptInterface $script, Opcodes $opcodes = null)
 {
     parent::__construct($script->getBuffer(), $opcodes);
     $this->outputScript = ScriptFactory::scriptPubKey()->payToScriptHash($script);
 }
示例#23
0
 /**
  * Calculate the hash of the current transaction, when you are looking to
  * spend $txOut, and are signing $inputToSign. The SigHashType defaults to
  * SIGHASH_ALL, though SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ANYONECANPAY
  * can be used.
  *
  * @param ScriptInterface $txOutScript
  * @param int $inputToSign
  * @param int $sighashType
  * @return BufferInterface
  * @throws \Exception
  */
 public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SigHash::ALL)
 {
     $sighashType = (int) $sighashType;
     $hashPrevOuts = $this->hashPrevOuts($sighashType);
     $hashSequence = $this->hashSequences($sighashType);
     $hashOutputs = $this->hashOutputs($sighashType, $inputToSign);
     $input = $this->transaction->getInput($inputToSign);
     return Hash::sha256d(new Buffer(pack("V", $this->transaction->getVersion()) . $hashPrevOuts->getBinary() . $hashSequence->getBinary() . $input->getOutPoint()->getBinary() . ScriptFactory::create()->push($txOutScript->getBuffer())->getScript()->getBinary() . Buffer::int($this->amount, 8)->flip()->getBinary() . pack("V", $input->getSequence()) . $hashOutputs->getBinary() . pack("V", $this->transaction->getLockTime()) . pack("V", $sighashType)));
 }