Exemplo n.º 1
0
 /**
  * @param $opCode
  * @param ScriptStack $mainStack
  * @throws \BitWasp\Bitcoin\Exceptions\ScriptStackException
  * @throws \Exception
  */
 public function op($opCode, ScriptStack $mainStack)
 {
     if ($mainStack->size() < 1) {
         throw new \Exception('Invalid stack operation');
     }
     $opCodes = $this->opCodes;
     $opName = $opCodes->getOp($opCode);
     $buffer = $mainStack->top(-1);
     if ($opCodes->cmp($opCode, 'OP_RIPEMD160') >= 0 && $opCodes->cmp($opCode, 'OP_HASH256') <= 0) {
         if ($opName == 'OP_RIPEMD160') {
             $hash = Hash::ripemd160($buffer);
         } elseif ($opName == 'OP_SHA1') {
             $hash = Hash::sha1($buffer);
         } elseif ($opName == 'OP_SHA256') {
             $hash = Hash::sha256($buffer);
         } elseif ($opName == 'OP_HASH160') {
             $hash = Hash::sha256ripe160($buffer);
         } else {
             // is hash256
             $hash = Hash::sha256d($buffer);
         }
         $mainStack->pop();
         $mainStack->push($hash);
     } else {
         throw new \Exception('Opcode not found');
     }
 }
Exemplo n.º 2
0
 public function getMockMessage($command, $invalidChecksum = false)
 {
     $payload = $this->getMockPayload($command);
     $net = Bitcoin::getDefaultNetwork();
     $msg = $this->getMock('BitWasp\\Bitcoin\\Networking\\NetworkMessage', ['getCommand', 'getPayload', 'getChecksum'], [$net, $payload]);
     $msg->expects($this->atLeastOnce())->method('getCommand')->willReturn($command);
     $msg->expects($this->atLeastOnce())->method('getPayload')->willReturn($payload);
     if ($invalidChecksum) {
         $random = new Random();
         $bytes = $random->bytes(4);
         $msg->expects($this->atLeastOnce())->method('getChecksum')->willReturn($bytes);
     } else {
         $msg->expects($this->atLeastOnce())->method('getChecksum')->willReturn(Hash::sha256d(new Buffer())->slice(0, 4));
     }
     return $msg;
 }
Exemplo n.º 3
0
 /**
  * @param BlockInterface $block
  * @param TransactionSerializerInterface $txSerializer
  * @return BlockData
  */
 public function parseUtxos(BlockInterface $block, TransactionSerializerInterface $txSerializer)
 {
     $blockData = new BlockData();
     $unknown = [];
     $hashStorage = new HashStorage();
     // Record every Outpoint required for the block.
     foreach ($block->getTransactions() as $t => $tx) {
         if ($tx->isCoinbase()) {
             continue;
         }
         foreach ($tx->getInputs() as $in) {
             $outpoint = $in->getOutPoint();
             $unknown[$outpoint->getTxId()->getBinary() . $outpoint->getVout()] = $outpoint;
         }
     }
     foreach ($block->getTransactions() as $tx) {
         /** @var BufferInterface $buffer */
         $buffer = $txSerializer->serialize($tx);
         $hash = Hash::sha256d($buffer)->flip();
         $hashStorage->attach($tx, $hash);
         $hashBin = $hash->getBinary();
         foreach ($tx->getOutputs() as $i => $out) {
             $lookup = $hashBin . $i;
             if (isset($unknown[$lookup])) {
                 // Remove unknown outpoints which consume this output
                 $outpoint = $unknown[$lookup];
                 $utxo = new Utxo($outpoint, $out);
                 unset($unknown[$lookup]);
             } else {
                 // Record new utxos which are not consumed in the same block
                 $utxo = new Utxo(new OutPoint($hash, $i), $out);
                 $blockData->remainingNew[] = $utxo;
             }
             // All utxos produced are stored
             $blockData->parsedUtxos[] = $utxo;
         }
     }
     $blockData->requiredOutpoints = array_values($unknown);
     $blockData->hashStorage = $hashStorage;
     return $blockData;
 }
Exemplo n.º 4
0
 /**
  * @param BlockHeaderInterface[] $headers
  * @return HeadersBatch
  */
 public function prepareBatch(array $headers)
 {
     $countHeaders = count($headers);
     if (0 === $countHeaders) {
         return new HeadersBatch($this->chains->best($this->math), []);
     }
     $bestPrev = null;
     $firstUnknown = null;
     $hashStorage = new HashStorage();
     foreach ($headers as $i => &$head) {
         if ($this->chains->isKnownHeader($head->getPrevBlock())) {
             $bestPrev = $head->getPrevBlock();
         }
         $hash = Hash::sha256d($head->getBuffer())->flip();
         $hashStorage->attach($head, $hash);
         if ($firstUnknown === null && !$this->chains->isKnownHeader($hash)) {
             $firstUnknown = $i;
         }
     }
     if (!$bestPrev instanceof BufferInterface) {
         throw new \RuntimeException('Headers::accept(): Unknown start header');
     }
     $view = $this->chains->isTip($bestPrev);
     if ($view === false) {
         throw new \RuntimeException('Headers::accept(): Unhandled fork');
     }
     $prevIndex = $view->getIndex();
     $access = $this->chains->access($view);
     $batch = [];
     if ($firstUnknown !== null) {
         $versionInfo = $this->db->findSuperMajorityInfoByView($view);
         $forks = new Forks($this->consensus->getParams(), $prevIndex, $versionInfo);
         for ($i = $firstUnknown; $i < $countHeaders; $i++) {
             /**
              * @var BufferInterface $hash
              * @var BlockHeaderInterface $header
              */
             $header = $headers[$i];
             $hash = $hashStorage[$header];
             $this->headerCheck->check($hash, $header);
             $index = $this->getNextIndex($hash, $prevIndex, $header);
             $forks->next($index);
             $this->headerCheck->checkContextual($access, $index, $prevIndex, $forks);
             $batch[] = $index;
             $prevIndex = $index;
         }
     }
     return new HeadersBatch($view, $batch);
 }
Exemplo n.º 5
0
 /**
  * @return Buffer
  */
 public function getHash()
 {
     return Hash::sha256d($this->getBuffer())->flip();
 }
Exemplo n.º 6
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)));
 }
Exemplo n.º 7
0
 /**
  * @param string $message
  * @return \BitWasp\Buffertools\Buffer
  */
 public function calculateMessageHash($message)
 {
     $content = new Buffer("Bitcoin Signed Message:\n" . Buffertools::numToVarInt(strlen($message))->getBinary() . $message);
     $hash = Hash::sha256d($content);
     return $hash;
 }
 /**
  * @param Parser $parser
  * @return NetworkMessage
  * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
  * @throws \Exception
  */
 public function fromParser(Parser $parser)
 {
     list($netBytes, $command, $payloadSize, $checksum) = $this->getHeaderTemplate()->parse($parser);
     /** @var Buffer $netBytes */
     /** @var Buffer $command */
     /** @var int|string $payloadSize */
     /** @var Buffer $checksum */
     if ($netBytes->getHex() !== $this->network->getNetMagicBytes()) {
         throw new \RuntimeException('Invalid magic bytes for network');
     }
     $buffer = $payloadSize > 0 ? $parser->readBytes($payloadSize) : new Buffer('', 0, $this->math);
     // Compare payload checksum against header value
     if (Hash::sha256d($buffer)->slice(0, 4)->getBinary() !== $checksum->getBinary()) {
         throw new \RuntimeException('Invalid packet checksum');
     }
     $cmd = trim($command->getBinary());
     switch ($cmd) {
         case Message::VERSION:
             $payload = $this->versionSerializer->parse($buffer);
             break;
         case Message::VERACK:
             $payload = new VerAck();
             break;
         case Message::SENDHEADERS:
             $payload = new SendHeaders();
             break;
         case Message::ADDR:
             $payload = $this->addrSerializer->parse($buffer);
             break;
         case Message::INV:
             $payload = $this->invSerializer->parse($buffer);
             break;
         case Message::GETDATA:
             $payload = $this->getDataSerializer->parse($buffer);
             break;
         case Message::NOTFOUND:
             $payload = $this->notFoundSerializer->parse($buffer);
             break;
         case Message::GETBLOCKS:
             $payload = $this->getBlocksSerializer->parse($buffer);
             break;
         case Message::GETHEADERS:
             $payload = $this->getHeadersSerializer->parse($buffer);
             break;
         case Message::TX:
             $payload = new Tx($this->txSerializer->parse($buffer));
             break;
         case Message::BLOCK:
             $payload = new Block($this->blockSerializer->parse($buffer));
             break;
         case Message::HEADERS:
             $payload = $this->headersSerializer->parse($buffer);
             break;
         case Message::GETADDR:
             $payload = new GetAddr();
             break;
         case Message::MEMPOOL:
             $payload = new MemPool();
             break;
         case Message::FEEFILTER:
             $payload = $this->feeFilterSerializer->parse($buffer);
             break;
         case Message::FILTERLOAD:
             $payload = $this->filterLoadSerializer->parse($buffer);
             break;
         case Message::FILTERADD:
             $payload = $this->filterAddSerializer->parse($buffer);
             break;
         case Message::FILTERCLEAR:
             $payload = new FilterClear();
             break;
         case Message::MERKLEBLOCK:
             $payload = $this->merkleBlockSerializer->parse($buffer);
             break;
         case Message::PING:
             $payload = $this->pingSerializer->parse($buffer);
             break;
         case Message::PONG:
             $payload = $this->pongSerializer->parse($buffer);
             break;
         case Message::REJECT:
             $payload = $this->rejectSerializer->parse($buffer);
             break;
         case Message::ALERT:
             $payload = $this->alertSerializer->parse($buffer);
             break;
         default:
             throw new \RuntimeException('Invalid command');
     }
     return new NetworkMessage($this->network, $payload);
 }
Exemplo n.º 9
0
 /**
  * @return Buffer
  */
 public function getTxHash()
 {
     return Hash::sha256d($this->getBuffer());
 }
Exemplo n.º 10
0
 /**
  * @param TransactionInterface|null $coinbaseTx
  * @return Block
  * @throws \BitWasp\Bitcoin\Exceptions\MerkleTreeEmpty
  */
 public function run(TransactionInterface $coinbaseTx = null)
 {
     $nonce = '0';
     $maxNonce = $this->math->pow(2, 32);
     // Allow user supplied transactions
     if ($coinbaseTx == null) {
         $coinbaseTx = new Transaction();
         $coinbaseTx->getInputs()->addInput(new TransactionInput('0000000000000000000000000000000000000000000000000000000000000000', 4294967295.0));
         $coinbaseTx->getOutputs()->addOutput(new TransactionOutput(5000000000.0, $this->script));
     }
     $inputs = $coinbaseTx->getInputs();
     $found = false;
     $usingDiff = $this->lastBlockHeader->getBits();
     $diff = new Difficulty($this->math);
     $target = $diff->getTarget($usingDiff);
     while (false === $found) {
         // Set coinbase script, and build Merkle tree & block header.
         $inputs->getInput(0)->setScript($this->getCoinbaseScriptBuf());
         $transactions = new TransactionCollection(array_merge(array($coinbaseTx), $this->transactions->getTransactions()));
         $merkleRoot = new MerkleRoot($this->math, $transactions);
         $merkleHash = $merkleRoot->calculateHash();
         $header = new BlockHeader($this->version, $this->lastBlockHeader->getBlockHash(), $merkleHash, $this->timestamp, $usingDiff, '0');
         $t = microtime(true);
         // Loop through all nonces (up to 2^32). Restart after modifying extranonce.
         while ($this->math->cmp($header->getNonce(), $maxNonce) <= 0) {
             $header->setNonce($this->math->add($header->getNonce(), '1'));
             $hash = (new Parser())->writeBytes(32, Hash::sha256d($header->getBuffer()), true)->getBuffer();
             if ($this->math->cmp($hash->getInt(), $target) <= 0) {
                 $block = new Block($this->math, $header, $transactions);
                 return $block;
             }
             if ($this->report && $this->math->cmp($this->math->mod($header->getNonce(), 100000), '0') == 0) {
                 $time = microtime(true) - $t;
                 $khash = $nonce / $time / 1000;
                 echo "extraNonce[{$this->extraNonce}] nonce[{$nonce}] time[{$time}] khash/s[{$khash}] \n";
             }
         }
         // Whenever we exceed 2^32, increment extraNonce and reset $nonce
         $this->extraNonce++;
         $nonce = '0';
     }
 }
Exemplo n.º 11
0
 /**
  * @param string $message
  * @return \BitWasp\Buffertools\BufferInterface
  */
 public function calculateMessageHash($message)
 {
     return Hash::sha256d($this->calculateBody($message));
 }
Exemplo n.º 12
0
 /**
  * @return string
  */
 public function getTransactionId()
 {
     $hash = bin2hex(Buffertools::flipBytes(Hash::sha256d($this->getBuffer())));
     return $hash;
 }
Exemplo n.º 13
0
 /**
  * @return \BitWasp\Buffertools\BufferInterface
  */
 public function getWitnessTxId()
 {
     return Hash::sha256d($this->getWitnessBuffer())->flip();
 }
Exemplo n.º 14
0
 /**
  * @return BufferInterface
  */
 public function getChecksum()
 {
     $data = $this->getPayload()->getBuffer();
     return Hash::sha256d($data)->slice(0, 4);
 }
Exemplo n.º 15
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 $inputToSign
  * @param int $sighashType
  * @return Buffer
  * @throws \Exception
  */
 public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SignatureHashInterface::SIGHASH_ALL)
 {
     $copy = $this->transaction->makeCopy();
     $inputs = $copy->getInputs();
     $outputs = $copy->getOutputs();
     if ($inputToSign > count($inputs)) {
         throw new \Exception('Input does not exist');
     }
     // Default SIGHASH_ALL procedure: null all input scripts
     $inputCount = count($inputs);
     for ($i = 0; $i < $inputCount; $i++) {
         $inputs->getInput($i)->setScript(new Script());
     }
     $inputs->getInput($inputToSign)->setScript($txOutScript);
     $math = Bitcoin::getMath();
     if ($math->bitwiseAnd($sighashType, 31) == SignatureHashInterface::SIGHASH_NONE) {
         // Set outputs to empty vector, and set sequence number of inputs to 0.
         $copy->setOutputs(new TransactionOutputCollection());
         // Let the others update at will. Set sequence of inputs we're not signing to 0.
         $inputCount = count($inputs);
         for ($i = 0; $i < $inputCount; $i++) {
             if ($math->cmp($i, $inputToSign) !== 0) {
                 $inputs->getInput($i)->setSequence(0);
             }
         }
     } elseif ($math->bitwiseAnd($sighashType, 31) == SignatureHashInterface::SIGHASH_SINGLE) {
         // Resize output array to $inputToSign + 1, set remaining scripts to null,
         // and set sequence's to zero.
         $nOutput = $inputToSign;
         if ($math->cmp($nOutput, count($outputs)) >= 0) {
             return Buffer::hex('0100000000000000000000000000000000000000000000000000000000000000');
         }
         // Resize..
         $outputs = $outputs->slice(0, $nOutput + 1)->getOutputs();
         // Set to null
         for ($i = 0; $i < $nOutput; $i++) {
             $outputs[$i] = new TransactionOutput($math->getBinaryMath()->getTwosComplement(-1, 64), new Script());
         }
         $copy->setOutputs(new TransactionOutputCollection($outputs));
         // Let the others update at will. Set sequence of inputs we're not signing to 0.
         $inputCount = count($inputs);
         for ($i = 0; $i < $inputCount; $i++) {
             if ($math->cmp($i, $inputToSign) != 0) {
                 $inputs->getInput($i)->setSequence(0);
             }
         }
     }
     // This can happen regardless of whether it's ALL, NONE, or SINGLE
     if ($math->bitwiseAnd($sighashType, SignatureHashInterface::SIGHASH_ANYONECANPAY)) {
         $input = $inputs->getInput($inputToSign);
         $copy->setInputs(new TransactionInputCollection([$input]));
     }
     // Serialize the TxCopy and append the 4 byte hashtype (little endian);
     $txParser = new Parser($copy->getBuffer());
     $txParser->writeInt(4, $sighashType, true);
     return Hash::sha256d($txParser->getBuffer());
 }
Exemplo n.º 16
0
 /**
  * Calculate a checksum for the given data
  *
  * @param BufferInterface $data
  * @return BufferInterface
  */
 public static function checksum(BufferInterface $data)
 {
     return Hash::sha256d($data)->slice(0, 4);
 }
Exemplo n.º 17
0
 /**
  * {@inheritdoc}
  * @see \BitWasp\Bitcoin\Block\BlockHeaderInterface::getBlockHash()
  */
 public function getBlockHash()
 {
     $parser = new Parser();
     return $parser->writeBytes(32, Hash::sha256d($this->getBuffer()), true)->getBuffer()->getHex();
 }
Exemplo n.º 18
0
 /**
  * @param int|string $sequence
  * @param bool $change
  * @return int|string
  */
 public function getSequenceOffset($sequence, $change = false)
 {
     return Hash::sha256d(new Buffer(sprintf("%s:%s:%s", $sequence, $change ? '1' : '0', $this->getMPK()->getBinary())))->getInt();
 }
 /**
  * @param Parser $parser
  * @return NetworkMessage
  * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
  * @throws \Exception
  */
 public function fromParser(Parser &$parser)
 {
     list($netBytes, $command, $payloadSize, $checksum) = $this->getHeaderTemplate()->parse($parser);
     /** @var Buffer $netBytes */
     /** @var Buffer $command */
     /** @var int|string $payloadSize */
     /** @var Buffer $checksum */
     if ($netBytes->getHex() !== $this->network->getNetMagicBytes()) {
         throw new \RuntimeException('Invalid magic bytes for network');
     }
     $buffer = $payloadSize > 0 ? $parser->readBytes($payloadSize) : new Buffer();
     // Compare payload checksum against header value
     if (Hash::sha256d($buffer)->slice(0, 4)->getBinary() !== $checksum->getBinary()) {
         throw new \RuntimeException('Invalid packet checksum');
     }
     $cmd = trim($command->getBinary());
     switch ($cmd) {
         case 'version':
             $payload = $this->versionSerializer->parse($buffer);
             break;
         case 'verack':
             $payload = new VerAck();
             break;
         case 'addr':
             $payload = $this->addrSerializer->parse($buffer);
             break;
         case 'inv':
             $payload = $this->invSerializer->parse($buffer);
             break;
         case 'getdata':
             $payload = $this->getDataSerializer->parse($buffer);
             break;
         case 'notfound':
             $payload = $this->notFoundSerializer->parse($buffer);
             break;
         case 'getblocks':
             $payload = $this->getBlocksSerializer->parse($buffer);
             break;
         case 'getheaders':
             $payload = $this->getHeadersSerializer->parse($buffer);
             break;
         case 'tx':
             $payload = new Tx($this->txSerializer->parse($buffer));
             break;
         case 'block':
             $payload = new Block($this->blockSerializer->parse($buffer));
             break;
         case 'headers':
             $payload = $this->headersSerializer->parse($buffer);
             break;
         case 'getaddr':
             $payload = new GetAddr();
             break;
         case 'mempool':
             $payload = new MemPool();
             break;
         case 'filterload':
             $payload = $this->filterLoadSerializer->parse($buffer);
             break;
         case 'filteradd':
             $payload = $this->filterAddSerializer->parse($buffer);
             break;
         case 'filterclear':
             $payload = new FilterClear();
             break;
         case 'merkleblock':
             $payload = $this->merkleBlockSerializer->parse($buffer);
             break;
         case 'ping':
             $payload = $this->pingSerializer->parse($buffer);
             break;
         case 'pong':
             $payload = $this->pongSerializer->parse($buffer);
             break;
         case 'reject':
             $payload = $this->rejectSerializer->parse($buffer);
             break;
         case 'alert':
             $payload = $this->alertSerializer->parse($buffer);
             break;
         default:
             throw new \RuntimeException('Invalid command');
     }
     return new NetworkMessage($this->network, $payload);
 }
Exemplo n.º 20
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)
 {
     $math = Bitcoin::getMath();
     $tx = new TxMutator($this->transaction);
     $inputs = $tx->inputsMutator();
     $outputs = $tx->outputsMutator();
     // Default SIGHASH_ALL procedure: null all input scripts
     foreach ($inputs as $input) {
         $input->script(new Script());
     }
     $inputs[$inputToSign]->script($txOutScript);
     if ($math->cmp($math->bitwiseAnd($sighashType, 31), SigHash::NONE) === 0) {
         // Set outputs to empty vector, and set sequence number of inputs to 0.
         $outputs->null();
         // Let the others update at will. Set sequence of inputs we're not signing to 0.
         foreach ($inputs as $i => $input) {
             if ($i !== $inputToSign) {
                 $input->sequence(0);
             }
         }
     } elseif ($math->cmp($math->bitwiseAnd($sighashType, 31), SigHash::SINGLE) === 0) {
         // Resize output array to $inputToSign + 1, set remaining scripts to null,
         // and set sequence's to zero.
         $nOutput = $inputToSign;
         if ($nOutput >= $this->nOutputs) {
             return Buffer::hex('0100000000000000000000000000000000000000000000000000000000000000', 32, $math);
         }
         // Resize, set to null
         $outputs->slice(0, $nOutput + 1);
         for ($i = 0; $i < $nOutput; $i++) {
             $outputs[$i]->null();
         }
         // Let the others update at will. Set sequence of inputs we're not signing to 0
         foreach ($inputs as $i => $input) {
             if ($i !== $inputToSign) {
                 $input->sequence(0);
             }
         }
     }
     // This can happen regardless of whether it's ALL, NONE, or SINGLE
     if ($math->cmp($math->bitwiseAnd($sighashType, SigHash::ANYONECANPAY), 0) > 0) {
         $input = $inputs[$inputToSign]->done();
         $inputs->null()->add($input);
     }
     return Hash::sha256d(Buffertools::concat($tx->done()->getBuffer(), Buffertools::flipBytes(Buffer::int($sighashType, 4, $math))));
 }
Exemplo n.º 21
0
 /**
  * Traverse the Merkle Tree hashes and extract those which have a matching bit.
  *
  * @param int $height
  * @param int $position
  * @param int $nBitsUsed
  * @param int $nHashUsed
  * @param BufferInterface[] $vMatch
  * @return BufferInterface
  */
 public function traverseAndExtract($height, $position, &$nBitsUsed, &$nHashUsed, &$vMatch)
 {
     if ($nBitsUsed >= count($this->vFlagBits)) {
         $this->fBad = true;
         return new Buffer();
     }
     $parent = $this->vFlagBits[$nBitsUsed++];
     if (0 === $height || !$parent) {
         if ($nHashUsed >= count($this->vHashes)) {
             $this->fBad = true;
             return new Buffer();
         }
         $hash = $this->vHashes[$nHashUsed++];
         if ($height === 0 && $parent) {
             $vMatch[] = $hash->flip();
         }
         return $hash;
     } else {
         $left = $this->traverseAndExtract($height - 1, $position * 2, $nBitsUsed, $nHashUsed, $vMatch);
         if ($position * 2 + 1 < $this->calcTreeWidth($height - 1)) {
             $right = $this->traverseAndExtract($height - 1, $position * 2 + 1, $nBitsUsed, $nHashUsed, $vMatch);
             if ($right === $left) {
                 $this->fBad = true;
             }
         } else {
             $right = $left;
         }
         return Hash::sha256d(Buffertools::concat($left, $right));
     }
 }
Exemplo n.º 22
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;
     }
 }