/** * @param $m * @param KeyInterface[] $keys * @param bool $sort * @return RedeemScript */ public static function multisig($m, array $keys = array(), $sort = true) { if ($sort) { $keys = \BitWasp\Buffertools\Buffertools::sort($keys); } return new RedeemScript($m, $keys); }
/** * @param callable|null $hashFunction * @return string * @throws MerkleTreeEmpty */ public function calculateHash(callable $hashFunction = null) { $hashFxn = $hashFunction ?: function ($value) { return hash('sha256', hash('sha256', $value, true), true); }; $txCount = count($this->transactions); if ($txCount == 0) { // TODO: Probably necessary. Should always have a coinbase at least. throw new MerkleTreeEmpty('Cannot compute Merkle root of an empty tree'); } if ($txCount == 1) { $buffer = $hashFxn($this->transactions->getTransaction(0)->getBinary()); } else { // Create a fixed size Merkle Tree $tree = new FixedSizeTree($txCount + $txCount % 2, $hashFxn); // Compute hash of each transaction $last = ''; foreach ($this->transactions->getTransactions() as $i => $transaction) { $last = $transaction->getBinary(); $tree->set($i, $last); } // Check if we need to repeat the last hash (odd number of transactions) if (!$this->math->isEven($txCount)) { $tree->set($txCount, $last); } $buffer = $tree->hash(); } $hash = new Buffer(Buffertools::flipBytes($buffer)); $hash = $hash->getHex(); $this->setLastHash($hash); return $this->getLastHash(); }
/** * @param int $m * @param PublicKeyInterface[] $keys * @param bool|true $sort * @return ScriptCreator|Script */ public function multisig($m, array $keys = [], $sort = true) { $n = count($keys); if ($m > $n) { throw new \LogicException('Required number of sigs exceeds number of public keys'); } if ($n > 16) { throw new \LogicException('Number of public keys is greater than 16'); } if ($sort) { $keys = Buffertools::sort($keys); } $opM = \BitWasp\Bitcoin\Script\encodeOpN($m); $opN = \BitWasp\Bitcoin\Script\encodeOpN($n); $script = ScriptFactory::create(); foreach ($keys as $key) { if (!$key instanceof PublicKeyInterface) { throw new \LogicException('Values in $keys[] must be a PublicKey'); } $script->push($key->getBuffer()); } $keyBuf = $script->getScript()->getBuffer(); $script = new Script(new Buffer(chr($opM) . $keyBuf->getBinary() . chr($opN) . chr(Opcodes::OP_CHECKMULTISIG))); return $script; }
/** * @param NetworkInterface $network * @param PrivateKeyInterface $privateKey * @return string */ public function serialize(NetworkInterface $network, PrivateKeyInterface $privateKey) { $serialized = Buffertools::concat(Buffer::hex($network->getPrivByte()), $this->hexSerializer->serialize($privateKey)); if ($privateKey->isCompressed()) { $serialized = Buffertools::concat($serialized, new Buffer("", 1)); } return Base58::encodeCheck($serialized); }
public function testHashIsSerializedInReverseOrder() { $buffer = Buffer::hex('0001020300010203000102030001020300010203000102030001020300010203'); $inv = Inventory::block($buffer); $results = unpack("Vtype/H64hash", $inv->getBinary()); $parsedBuffer = Buffer::hex($results['hash']); $this->assertEquals($buffer->getHex(), Buffertools::flipBytes($parsedBuffer)->getHex()); }
/** * @dataProvider getVectors */ public function testByteStringLe($math, $size, $string) { $buffer = Buffer::hex($string, $size); $t = new ByteString($math, $size, ByteOrder::LE); $out = $t->write($buffer); $eFlipped = Buffertools::flipBytes(pack("H*", $string)); $this->assertEquals($eFlipped, $out); $parser = new Parser(new Buffer($out)); $this->assertEquals($string, $t->read($parser)->getHex()); }
/** * @param $bitSize * @return array */ private function generateSizeBasedTests($bitSize, $byteOrder) { $math = EccFactory::getAdapter(); $halfPos = $math->baseConvert(str_pad('7', $bitSize / 4, 'f', STR_PAD_RIGHT), 16, 10); $maxPos = $math->baseConvert(str_pad('', $bitSize / 4, 'f', STR_PAD_RIGHT), 16, 10); $test = function ($integer) use($bitSize, $math, $byteOrder) { $hex = str_pad($math->baseConvert($integer, 10, 16), $bitSize / 4, '0', STR_PAD_LEFT); if ($byteOrder == ByteOrder::LE) { $hex = Buffertools::flipBytes(Buffer::hex($hex))->getHex(); } return [$integer, $hex, null]; }; return [$test(0), $test(1), $test($halfPos), $test($maxPos)]; }
/** * @expectedException \RuntimeException * @expectedExceptionMessage Invalid packet checksum */ public function testInvalidChecksum() { $v = '60002'; $services = Buffer::hex('0000000000000001'); $time = '123456789'; $recipient = new NetworkAddress(Buffer::hex('0000000000000001'), '10.0.0.1', '8332'); $sender = new NetworkAddress(Buffer::hex('0000000000000001'), '10.0.0.2', '8332'); $userAgent = new Buffer("/Satoshi:0.7.2/"); $lastBlock = '212672'; $random = new Random(); $nonce = $random->bytes(8)->getInt(); $version = new Version($v, $services, $time, $recipient, $sender, $nonce, $userAgent, $lastBlock, true); $msg = $version->getNetworkMessage(); $realBuffer = $msg->getBuffer(); $invalid = Buffertools::concat(Buffertools::concat($realBuffer->slice(0, 20), Buffer::hex('00000000')), $realBuffer->slice(24)); $serializer = new NetworkMessageSerializer(Bitcoin::getDefaultNetwork()); $serializer->parse($invalid); }
/** * @param int|string $m * @param string $path * @param array $keys * @param HierarchicalKeySequence $sequences * @param bool $sort */ public function __construct($m, $path, array $keys, HierarchicalKeySequence $sequences, $sort = false) { if (count($keys) < 1) { throw new \RuntimeException('Must have at least one HierarchicalKey for Multisig HD Script'); } // Sort here to guarantee calls to getKeys() returns keys in the same order as the redeemScript. if ($sort) { $keys = Buffertools::sort($keys, function (HierarchicalKey $key) { return $key->getPublicKey()->getBuffer(); }); } $this->m = $m; $this->path = $path; foreach ($keys as $key) { $this->keys[] = $key; } $this->sequences = $sequences; $this->sort = $sort; }
/** * 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; }
/** * @param HierarchicalKey[] $keys * @return HierarchicalKey[] */ private function sortHierarchicalKeys(array $keys) { return Buffertools::sort($keys, function (HierarchicalKey $key) { return $key->getPublicKey()->getBuffer(); }); }
/** * @return Buffer */ public function flip() { return Buffertools::flipBytes($this); }
/** * 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)))); }
/** * Take an array containing serializable objects. * @param SerializableInterface []|Buffer[] * @return $this */ public function writeArray($serializable) { $parser = new Parser(Buffertools::numToVarInt(count($serializable)), $this->math); foreach ($serializable as $object) { if ($object instanceof SerializableInterface) { $object = $object->getBuffer(); } if ($object instanceof Buffer) { $parser->writeBytes($object->getSize(), $object); } else { throw new \RuntimeException('Input to writeArray must be Buffer[], or SerializableInterface[]'); } } $this->string .= $parser->getBuffer()->getBinary(); return $this; }
/** * @return Buffer */ public function getMPK() { $math = $this->ecAdapter->getMath(); $point = $this->getMasterPublicKey()->getPoint(); return Buffertools::concat(Buffer::hex($math->decHex($point->getX()), 32), Buffer::hex($math->decHex($point->getY()), 32)); }
/** * @param TransactionInputInterface $input * @return Buffer */ public function serialize(TransactionInputInterface $input) { return Buffertools::concat($this->outpointSerializer->serialize($input->getOutPoint()), $this->getInputTemplate()->write([$input->getScript()->getBuffer(), $input->getSequence()])); }
/** * @return string */ public function getTransactionId() { $hash = bin2hex(Buffertools::flipBytes(Hash::sha256d($this->getBuffer()))); return $hash; }
public function testConcat() { $a = Buffer::hex("1100"); $b = Buffer::hex("0011"); $c = Buffer::hex("11", 2); $this->assertEquals("11000011", Buffertools::concat($a, $b)->getHex()); $this->assertEquals("00111100", Buffertools::concat($b, $a)->getHex()); $this->assertEquals("11000011", Buffertools::concat($a, $c)->getHex()); $this->assertEquals("00111100", Buffertools::concat($c, $a)->getHex()); }
/** * Encode the given data in base58, with a checksum to check integrity. * * @param BufferInterface $data * @return string * @throws \Exception */ public static function encodeCheck(BufferInterface $data) { return self::encode(Buffertools::concat($data, self::checksum($data))); }
/** * @param Alert $alert * @return \BitWasp\Buffertools\Buffer */ public function serialize(Alert $alert) { $sig = $alert->getSignature(); return Buffertools::concat($alert->getDetail()->getBuffer(), $this->getSigTemplate()->write([$sig->getR(), $sig->getS()])); }
/** * @param string $message * @return BufferInterface * @throws \Exception */ private function calculateBody($message) { return new Buffer("Bitcoin Signed Message:\n" . Buffertools::numToVarInt(strlen($message))->getBinary() . $message, null, $this->ecAdapter->getMath()); }
/** * @param GetBlocks $msg * @return \BitWasp\Buffertools\Buffer */ public function serialize(GetBlocks $msg) { return Buffertools::concat($this->getVersionTemplate()->write([$msg->getVersion()]), $this->locator->serialize($msg->getLocator())); }
/** * @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 BlockInterface $block * @return \BitWasp\Buffertools\Buffer */ public function serialize(BlockInterface $block) { return Buffertools::concat($this->headerSerializer->serialize($block->getHeader()), $this->getTxsTemplate()->write([$block->getTransactions()->all()])); }
/** * @param FilteredBlock $merkleBlock * @return \BitWasp\Buffertools\BufferInterface */ public function serialize(FilteredBlock $merkleBlock) { return Buffertools::concat($this->headerSerializer->serialize($merkleBlock->getHeader()), $this->treeSerializer->serialize($merkleBlock->getPartialTree())); }
/** * @param NetworkMessage $object * @return Buffer */ public function serialize(NetworkMessage $object) { $payload = $object->getPayload()->getBuffer(); $command = str_pad(unpack("H*", $object->getCommand())[1], 24, '0', STR_PAD_RIGHT); $header = $this->getHeaderTemplate()->write([Buffer::hex($this->network->getNetMagicBytes()), Buffer::hex($command), $payload->getSize(), $object->getChecksum()]); return Buffertools::concat($header, $payload); }
/** * Create a buffer containing data to be hashed hashed to yield the child offset * * @param integer|string $sequence * @return Buffer * @throws \Exception */ public function getHmacSeed($sequence) { $hardened = $this->ecAdapter->getMath()->getBinaryMath()->isNegative($sequence, 32); if ($hardened) { if ($this->isPrivate() === false) { throw new \Exception("Can't derive a hardened key without the private key"); } $buffer = Buffertools::concat(new Buffer(""), $this->getPrivateKey()->getBuffer()); } else { $buffer = $this->getPublicKey()->getBuffer(); } return (new Parser($buffer))->writeBytes(4, Buffer::int($sequence, 4))->getBuffer(); }
/** * 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)); } }
/** * @param Alert $alert * @return \BitWasp\Buffertools\Buffer */ public function serialize(Alert $alert) { $detail = $alert->getDetail()->getBuffer(); $sig = $this->getSigBuf()->write([$alert->getSignature()->getBuffer()]); return Buffertools::concat($detail, $sig); }