/** * @param TransactionSignature $txSig * @return \BitWasp\Buffertools\Buffer */ public function serialize(TransactionSignature $txSig) { $sig = $this->sigSerializer->serialize($txSig->getSignature()); $parser = new Parser($sig->getHex()); $parser->writeBytes(1, Buffer::int($txSig->getHashType(), 1)); $buffer = $parser->getBuffer(); return $buffer; }
/** * @return \BitWasp\Bitcoin\Block\BlockInterface */ public function getGenesisBlock() { $timestamp = new Buffer('The Times 03/Jan/2009 Chancellor on brink of second bailout for banks', null, $this->math); $publicKey = Buffer::hex('04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f', null, $this->math); $inputScript = ScriptFactory::sequence([Buffer::int('486604799', 4, $this->math)->flip(), Buffer::int('4', null, $this->math), $timestamp]); $outputScript = ScriptFactory::sequence([$publicKey, Opcodes::OP_CHECKSIG]); return new Block($this->math, $this->getGenesisBlockHeader(), new TransactionCollection([(new TxBuilder())->version('1')->input(new Buffer('', 32), 4294967295.0, $inputScript)->output(5000000000.0, $outputScript)->locktime(0)->get()])); }
/** * @param BloomFilter $filter * @return BufferInterface */ public function serialize(BloomFilter $filter) { $math = new Math(); $vBuf = []; foreach ($filter->getData() as $i) { $vBuf[] = Buffer::int($i, 1, $math); } return $this->getTemplate()->write([$vBuf, $filter->getNumHashFuncs(), $filter->getTweak(), (string) $filter->getFlags()]); }
/** * @param PublicKey $publicKey * @return Buffer */ private function doSerialize(PublicKey $publicKey) { $math = $this->ecAdapter->getMath(); $point = $publicKey->getPoint(); $compressed = $publicKey->isCompressed(); $parser = new Parser('', $math); $parser->writeBytes(1, $this->getPrefix($compressed, $point)); $compressed ? $parser->writeBytes(32, Buffer::int($point->getX(), null, $math)) : $parser->writeBytes(32, Buffer::int($point->getX(), null, $math))->writeBytes(32, Buffer::int($point->getY(), null, $math)); return $parser->getBuffer(); }
/** * @param EcAdapter $ecAdapter * @param $int * @param bool $compressed * @throws InvalidPrivateKey */ public function __construct(EcAdapter $ecAdapter, $int, $compressed = false) { if (false === $ecAdapter->validatePrivateKey(Buffer::int($int, 32, $ecAdapter->getMath()))) { throw new InvalidPrivateKey('Invalid private key - must be less than curve order.'); } if (false === is_bool($compressed)) { throw new \InvalidArgumentException('PrivateKey: Compressed argument must be a boolean'); } $this->ecAdapter = $ecAdapter; $this->secretMultiplier = $int; $this->compressed = $compressed; }
/** * @param AlertDetail $detail * @return \BitWasp\Buffertools\Buffer */ public function serialize(AlertDetail $detail) { $setCancels = []; foreach ($detail->getSetCancel() as $toCancel) { $t = new Parser(); $setCancels[] = $t->writeBytes(4, Buffer::int($toCancel), true)->getBuffer(); } $setSubVers = []; foreach ($detail->getSetSubVer() as $subVer) { $t = new Parser(); $setSubVers[] = $t->writeBytes(4, Buffer::int($subVer), true)->getBuffer(); } return $this->getTemplate()->write([$detail->getVersion(), $detail->getRelayUntil(), $detail->getExpiration(), $detail->getId(), $detail->getCancel(), $setCancels, $detail->getMinVer(), $detail->getMaxVer(), $setSubVers, $detail->getPriority(), $detail->getComment(), $detail->getStatusBar()]); }
/** * @param array $bits * @return array */ private function bitsToBuffers(array $bits) { $math = Bitcoin::getMath(); $vBuffers = str_split(str_pad('', (count($bits) + 7) / 8, '0', STR_PAD_LEFT)); $nBits = count($bits); for ($p = 0; $p < $nBits; $p++) { $index = (int) floor($p / 8); $vBuffers[$index] |= $bits[$p] << $p % 8; } foreach ($vBuffers as &$value) { $value = Buffer::int($value, null, $math); } unset($value); return $vBuffers; }
/** * Push data into the stack. * * @param $data * @return $this * @throws \Exception */ public function push(Buffer $data) { $length = $data->getSize(); $parsed = new Parser('', $this->math); /** Note that larger integers are serialized without flipping bits - Big endian */ if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) { $varInt = Buffertools::numToVarInt($length); $data = new Buffer($varInt->getBinary() . $data->getBinary(), null, $this->math); $parsed->writeBytes($data->getSize(), $data); } else { if ($length <= 0xff) { $lengthSize = 1; } elseif ($length <= 0xffff) { $lengthSize = 2; } else { $lengthSize = 4; } $op = $this->opcodes->getOpByName('OP_PUSHDATA' . $lengthSize); $parsed->writeBytes(1, Buffer::int($op))->writeBytes($lengthSize, Buffer::int($length), true)->writeBytes($length, $data); } $this->script .= $parsed->getBuffer()->getBinary(); return $this; }
/** * @dataProvider IntVectors */ public function testIntConstruct($int, $size, $expectedHex, $math) { $buffer = Buffer::int($int, $size, $math); $this->assertEquals($expectedHex, $buffer->getHex()); }
/** * 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)))); }
/** * @param int $tweak * @return PrivateKey */ public function tweakMul($tweak) { $adapter = $this->ecAdapter; $math = $adapter->getMath(); $context = $adapter->getContext(); $privateKey = $this->getBinary(); // mod by reference $tweak = Buffer::int($tweak, 32, $math)->getBinary(); $ret = \secp256k1_ec_privkey_tweak_mul($context, $privateKey, $tweak); if ($ret !== 1) { throw new \RuntimeException('Secp256k1 privkey tweak mul: failed'); } $secret = $math->hexDec(bin2hex($privateKey)); return $adapter->getPrivateKey($secret, $this->compressed); }
/** * @param int $bytes * @return Buffer */ public function bytes($bytes) { $integer = $this->hmac->generate($this->ecAdapter->getGenerator()->getOrder()); return Buffer::int($integer, $bytes, $this->ecAdapter->getMath()); }
/** * @param Buffer $bits * @return Buffer */ public function getTargetHash(Buffer $bits) { return Buffer::int($this->getTarget($bits), 32, $this->math); }
/** * Derive a child key * * @param $sequence * @return HierarchicalKey * @throws \Exception */ public function deriveChild($sequence) { $chain = Buffer::int($this->getChainCode(), 32, $this->ecAdapter->getMath()); $hash = Hash::hmac('sha512', $this->getHmacSeed($sequence), $chain); $offset = $hash->slice(0, 32); $chain = $hash->slice(32); $key = $this->isPrivate() ? $this->getPrivateKey() : $this->getPublicKey(); if (false === $this->ecAdapter->validatePrivateKey($offset)) { return $this->deriveChild($sequence + 1); } return new HierarchicalKey($this->ecAdapter, $this->getDepth() + 1, $this->getChildFingerprint(), $sequence, $chain->getInt(), $key->tweakAdd($offset->getInt())); }
/** * @param PrivateKeyInterface $privateKey * @return BufferInterface */ public function serialize(PrivateKeyInterface $privateKey) { return Buffer::int($privateKey->getSecretMultiplier(), 32, $this->ecAdapter->getMath()); }
/** * Here, we return max 2000 headers following $hash. * Useful for helping other nodes sync. * @param string $hash * @return array */ public function fetchNextHeaders($hash) { if ($this->debug) { echo "db: called fetchNextHeaders ({$hash})\n"; } $stmt = $this->dbh->prepare("\n SELECT child.version, child.prevBlock, child.merkleRoot,\n child.nTimestamp, child.nBits, child.nNonce, child.height\n FROM headerIndex AS child, headerIndex AS parent\n WHERE child.rgt < parent.rgt\n AND parent.hash = :hash\n LIMIT 2000\n "); $stmt->bindParam(':hash', $hash); if ($stmt->execute()) { $results = array(); foreach ($stmt->fetchAll() as $row) { $results[] = new BlockHeader($row['version'], $row['prevBlock'], $row['merkleRoot'], $row['nTimestamp'], Buffer::int($row['nBits'], 4), $row['nNonce']); } $stmt->closeCursor(); return $results; } throw new \RuntimeException('Failed to fetch next headers ' . $hash); }
/** * @param $height * @return \React\Promise\Promise */ public function blockGetHeader($height) { return $this->client->request('blockchain.block.get_header', [$height])->then(function (Response $response) { $content = $response->getResult(); return new BlockHeader($content['version'], isset($content['prev_block_hash']) ? Buffer::hex($content['prev_block_hash'], 32) : new Buffer('', 32), Buffer::hex($content['merkle_root'], 32), $content['timestamp'], Buffer::int($content['bits'], 4, $this->math), $content['nonce']); }); }
/** * 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))); }