/**
  * @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');
     }
 }
Example #2
0
 /**
  * @return \BitWasp\Buffertools\BufferInterface
  */
 public function getPubKeyHash()
 {
     if ($this instanceof PrivateKeyInterface) {
         $publicKey = $this->getPublicKey();
     } else {
         $publicKey = $this;
     }
     return Hash::sha256ripe160($publicKey->getBuffer());
 }
 /**
  * @param Buffer $entropy
  * @param integer $CSlen
  * @return string
  */
 private function calculateChecksum(Buffer $entropy, $CSlen)
 {
     $entHash = Hash::sha256($entropy);
     $math = $this->ecAdapter->getMath();
     // Convert byte string to padded binary string of 0/1's.
     $hashBits = str_pad($math->baseConvert($entHash->getHex(), 16, 2), 256, '0', STR_PAD_LEFT);
     // Take $CSlen bits for the checksum
     $checksumBits = substr($hashBits, 0, $CSlen);
     return $checksumBits;
 }
Example #4
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;
 }
Example #5
0
 /**
  * @param int $nHashNum
  * @param BufferInterface $data
  * @return string
  */
 public function hash($nHashNum, BufferInterface $data)
 {
     return $this->math->mod(Hash::murmur3($data, $nHashNum * self::TWEAK_START + $this->nTweak & 0xffffffff)->getInt(), count($this->vFilter) * 8);
 }
 /**
  * @return BufferInterface
  */
 public function getChecksum()
 {
     $data = $this->getPayload()->getBuffer();
     return Hash::sha256d($data)->slice(0, 4);
 }
Example #7
0
<?php

require "../vendor/autoload.php";
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\Script;
use BitWasp\Bitcoin\Transaction\OutPoint;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Script\WitnessProgram;
use BitWasp\Bitcoin\Script\P2shScript;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Script\Opcodes;
use BitWasp\Bitcoin\Bitcoin;
$wif = 'QP3p9tRpTGTefG4a8jKoktSWC7Um8qzvt8wGKMxwWyW3KTNxMxN7';
$s = \BitWasp\Bitcoin\Network\NetworkFactory::bitcoinSegnet();
Bitcoin::setNetwork($s);
$ec = \BitWasp\Bitcoin\Bitcoin::getEcAdapter();
$key = PrivateKeyFactory::fromWif($wif);
echo $key->getPublicKey()->getAddress()->getAddress() . PHP_EOL;
$outpoint = new OutPoint(Buffer::hex('499c2bff1499bf84bc63058fda3ed112c2c663389f108353798a1bd6a6651afe', 32), 0);
$scriptPubKey = ScriptFactory::scriptPubKey()->payToPubKeyHash($key->getPublicKey());
$value = 100000000;
$txOut = new \BitWasp\Bitcoin\Transaction\TransactionOutput($value, $scriptPubKey);
$destination = new WitnessProgram(0, Hash::sha256($scriptPubKey->getBuffer()));
$tx = TransactionFactory::build()->spendOutPoint($outpoint)->output(99990000, $destination->getScript())->get();
$signed = new \BitWasp\Bitcoin\Transaction\Factory\Signer($tx, $ec);
$signed->sign(0, $key, $txOut);
$ss = $signed->get();
echo $ss->getBuffer()->getHex() . PHP_EOL;
 /**
  * @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);
 }
 /**
  * @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;
 }
 /**
  * Derive a child key
  *
  * @param $sequence
  * @return HierarchicalKey
  * @throws \Exception
  */
 public function deriveChild($sequence)
 {
     $chain = Buffer::hex($this->ecAdapter->getMath()->decHex($this->getChainCode()), 32);
     $hash = Hash::hmac('sha512', $this->getHmacSeed($sequence), $chain);
     $offset = $hash->slice(0, 32);
     $chain = $hash->slice(32);
     if (false === $this->ecAdapter->validatePrivateKey($offset)) {
         return $this->deriveChild($sequence + 1);
     }
     return new HierarchicalKey($this->ecAdapter, $this->getDepth() + 1, $this->getChildFingerprint(), $sequence, $chain->getInt(), $this->isPrivate() ? $this->ecAdapter->privateKeyAdd($this->getPrivateKey(), $offset->getInt()) : $this->ecAdapter->publicKeyAdd($this->getPublicKey(), $offset->getInt()));
 }
 /**
  * @param Buffer $entropy
  * @param EcAdapterInterface $ecAdapter
  * @return HierarchicalKey
  */
 public static function fromEntropy(Buffer $entropy, EcAdapterInterface $ecAdapter = null)
 {
     $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter();
     $hash = Hash::hmac('sha512', $entropy, new Buffer('Bitcoin seed', null, $ecAdapter->getMath()));
     return new HierarchicalKey($ecAdapter, 0, 0, 0, $hash->slice(32, 32)->getInt(), PrivateKeyFactory::fromHex($hash->slice(0, 32)->getHex(), true, $ecAdapter));
 }
Example #12
0
 /**
  * @return \BitWasp\Buffertools\BufferInterface
  */
 public function getWitnessTxId()
 {
     return Hash::sha256d($this->getWitnessBuffer())->flip();
 }
Example #13
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);
 }
Example #14
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';
     }
 }
Example #15
0
 /**
  * @param string $message
  * @return \BitWasp\Buffertools\BufferInterface
  */
 public function calculateMessageHash($message)
 {
     return Hash::sha256d($this->calculateBody($message));
 }
Example #16
0
 /**
  * @return string
  */
 public function getTransactionId()
 {
     $hash = bin2hex(Buffertools::flipBytes(Hash::sha256d($this->getBuffer())));
     return $hash;
 }
Example #17
0
 /**
  * Return a buffer containing the hash of this script.
  *
  * @return BufferInterface
  */
 public function getScriptHash()
 {
     return Hash::sha256ripe160($this->getBuffer());
 }
Example #18
0
 /**
  * @param PrivateKeyInterface $key
  * @param ScriptInterface|null $redeemScript
  * @param ScriptInterface|null $witnessScript
  * @return bool
  */
 public function sign(PrivateKeyInterface $key, ScriptInterface $redeemScript = null, ScriptInterface $witnessScript = null)
 {
     /** @var BufferInterface[] $return */
     $type = null;
     $return = [];
     $solved = $this->doSignature($key, $this->txOut->getScript(), $type, $return, 0);
     if ($solved && $type === OutputClassifier::PAYTOSCRIPTHASH) {
         $redeemScriptBuffer = $return[0];
         if (!$redeemScript instanceof ScriptInterface) {
             throw new \InvalidArgumentException('Must provide redeem script for P2SH');
         }
         if (!$redeemScript->getScriptHash()->getBinary() === $redeemScriptBuffer->getBinary()) {
             throw new \InvalidArgumentException("Incorrect redeem script - hash doesn't match");
         }
         $results = [];
         // ???
         $solved = $solved && $this->doSignature($key, $redeemScript, $type, $results, 0) && $type !== OutputClassifier::PAYTOSCRIPTHASH;
         if ($solved) {
             $this->redeemScript = $redeemScript;
         }
     }
     if ($solved && $type === OutputClassifier::WITNESS_V0_KEYHASH) {
         $pubKeyHash = $return[0];
         $witnessScript = ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $pubKeyHash, Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]);
         $subType = null;
         $subResults = [];
         $solved = $solved && $this->doSignature($key, $witnessScript, $subType, $subResults, 1);
     } else {
         if ($solved && $type === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
             $scriptHash = $return[0];
             if (!$witnessScript instanceof ScriptInterface) {
                 throw new \InvalidArgumentException('Must provide witness script for witness v0 scripthash');
             }
             if (!Hash::sha256($witnessScript->getBuffer())->getBinary() === $scriptHash->getBinary()) {
                 throw new \InvalidArgumentException("Incorrect witness script - hash doesn't match");
             }
             $subType = null;
             $subResults = [];
             $solved = $solved && $this->doSignature($key, $witnessScript, $subType, $subResults, 1) && $subType !== OutputClassifier::PAYTOSCRIPTHASH && $subType !== OutputClassifier::WITNESS_V0_SCRIPTHASH && $subType !== OutputClassifier::WITNESS_V0_KEYHASH;
             if ($solved) {
                 $this->witnessScript = $witnessScript;
             }
         }
     }
     return $solved;
 }
Example #19
0
 /**
  * @return Buffer
  */
 public function getHash()
 {
     return Hash::sha256d($this->getBuffer())->flip();
 }
Example #20
0
 /**
  * @return Buffer
  */
 public function getTxHash()
 {
     return Hash::sha256d($this->getBuffer());
 }
Example #21
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;
 }
 /**
  * @param Buffer $secret
  * @param PublicKeyInterface $a1
  * @param PublicKeyInterface $a2
  * @param PublicKeyInterface $b1
  * @param PublicKeyInterface $b2
  * @return ScriptInterface
  */
 public function payToLightningChannel(Buffer $secret, PublicKeyInterface $a1, PublicKeyInterface $a2, PublicKeyInterface $b1, PublicKeyInterface $b2)
 {
     return ScriptFactory::create()->op('OP_DEPTH')->op('OP_3')->op('OP_EQUAL')->op('OP_IF')->op('OP_HASH160')->push(Hash::sha256ripe160($secret))->op('OP_EQUALVERIFY')->concat(ScriptFactory::scriptPubKey()->multisig(2, [$a1, $b1]))->op('OP_ELSE')->concat(ScriptFactory::scriptPubKey()->multisig(2, [$a2, $b2]))->op('OP_ENDIF')->getScript();
 }
Example #23
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);
 }
 /**
  * @param $mnemonic
  * @param $passphrase
  * @return \BitWasp\Buffertools\BufferInterface
  * @throws \Exception
  */
 public function getSeed($mnemonic, $passphrase = '')
 {
     return Hash::pbkdf2('sha512', $this->normalize($mnemonic), $this->normalize("mnemonic" . $passphrase), 2048, 64);
 }
Example #25
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();
 }
Example #26
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);
 }
Example #28
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))));
 }
Example #29
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));
     }
 }
Example #30
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;
     }
 }