public function testMethods() { $loop = new \React\EventLoop\StreamSelectLoop(); $dns = (new \BitWasp\Bitcoin\Networking\Dns\Factory())->create('8.8.8.8', $loop); $network = Bitcoin::getDefaultNetwork(); $random = new Random(); $messages = new \BitWasp\Bitcoin\Networking\Messages\Factory($network, $random); $factory = new Factory($dns, $messages, $loop); $this->assertInstanceOf($this->peerType, $factory->getPeer()); $services = Buffer::hex('00', 8); $address = $factory->getAddress('127.0.0.1', 8332, $services); $this->assertInstanceOf($this->addrType, $address); $connector = $factory->getConnector(); $this->assertInstanceOf($this->connType, $connector); $server = $factory->getServer(); $this->assertInstanceOf($this->serverType, $server); $locator = $factory->getLocator(); $this->assertInstanceOf($this->locatorType, $locator); $listener = $factory->getListener($server); $this->assertInstanceOf($this->listenerType, $listener); $handler = $factory->getPacketHandler(); $this->assertInstanceOf($this->handlerType, $handler); $manager = $factory->getManager(); $this->assertInstanceOf($this->managerType, $manager); }
public function __construct() { parent::__construct(function () { $consensus = new ConsensusFactory(Bitcoin::getEcAdapter()); $loop = LoopFactory::create(); $context = new ZmqContext($loop); $control = $context->getSocket(\ZMQ::SOCKET_SUB); $control->connect('tcp://127.0.0.1:5594'); $control->subscribe('control'); $control->on('messages', function ($msg) use($loop) { if ($msg[1] == 'shutdown') { $loop->stop(); } }); $results = $context->getSocket(\ZMQ::SOCKET_PUSH); $results->connect("tcp://127.0.0.1:5593"); $workers = $context->getSocket(\ZMQ::SOCKET_PULL); $workers->connect('tcp://127.0.0.1:5592'); $workers->on('message', function ($message) use($consensus, $results) { $details = json_decode($message, true); $txid = $details['txid']; $flags = $details['flags']; $vin = $details['vin']; $scriptPubKey = new Script(Buffer::hex($details['scriptPubKey'])); $tx = TransactionFactory::fromHex($details['tx']); $results->send(json_encode(['txid' => $txid, 'vin' => $vin, 'result' => $consensus->getConsensus(new Flags($flags))->verify($tx, $scriptPubKey, $vin)])); }); $loop->run(); exit(0); }); }
/** * Connect to $numSeeds DNS seeds * * @param int $numSeeds * @return \React\Promise\Promise|\React\Promise\PromiseInterface */ public function queryDnsSeeds($numSeeds = 1) { $peerList = new Deferred(); // Take $numSeeds $seedHosts = self::dnsSeedHosts(); $seeds = array_slice($seedHosts, 0, min($numSeeds, count($seedHosts))); // Connect to $numSeeds peers /** @var Peer[] $vNetAddr */ $vNetAddr = []; foreach ($seeds as $seed) { echo " [ query DNS seed: " . $seed . " ] \n"; $this->dns->resolve($seed)->then(function ($ipList) use(&$vNetAddr, $peerList, &$numSeeds) { $vNetAddr[] = $ipList; if (count($vNetAddr) == $numSeeds) { $peerList->resolve($vNetAddr); } }); } // Compile the list of lists of peers into $this->knownAddresses return $peerList->promise()->then(function (array $vPeerVAddrs) { shuffle($vPeerVAddrs); /** @var NetworkAddressInterface[] $addresses */ $addresses = []; array_map(function (array $value) use(&$addresses) { foreach ($value as $ip) { $addresses[] = new NetworkAddress(Buffer::hex('01', 8), $ip, 8333); } }, $vPeerVAddrs); $this->knownAddresses = array_merge($this->knownAddresses, $addresses); return $this; }); }
public function testPeer() { $localhost = '127.0.0.1'; $localport = '8333'; $remotehost = '127.0.0.1'; $remoteport = '9999'; $loop = new StreamSelectLoop(); $dnsResolverFactory = new \React\Dns\Resolver\Factory(); $dns = $dnsResolverFactory->createCached('8.8.8.8', $loop); $reactServer = new Server($loop); $network = Bitcoin::getDefaultNetwork(); $client = new NetworkAddress(Buffer::hex('0000000000000001'), $localhost, $localport); $server = new NetworkAddress(Buffer::hex('0000000000000001'), $remotehost, $remoteport); $msgs = new Factory($network, new Random()); $serverReceivedConnection = false; $serverListener = new Listener($server, $msgs, $reactServer, $loop); $serverListener->on('connection', function (Peer $peer) use(&$serverReceivedConnection, &$serverListener) { $peer->close(); $serverReceivedConnection = true; }); $serverListener->listen($server->getPort()); $connector = new Connector($loop, $dns); $clientConnection = new Peer($client, $msgs, $loop); $clientConnection->connect($connector, $server)->then(function (Peer $peer) use($serverListener, &$loop) { $peer->close(); $serverListener->close(); }); $loop->run(); $this->assertTrue($serverReceivedConnection); }
/** * Composes a send transaction * @param string $asset A counterparty asset name or BTC * @param mixed $quantity Quantity of asset to send. Accepts a float or a Tokenly\CounterpartyTransactionComposer\Quantity or Tokenly\CryptoQuantity\CryptoQuantity object. Use a Quantity object for indivisible assets. * @param mixed $destination A single destination bitcoin address. For BTC sends an array of [[address, amount], [address, amount]] is also allowed. Amounts should be float values. * @param string $private_key_wif The private key in ASCII WIF format. This can be null to compose an unsigned transaction. * @param array $utxos An array of UTXOs. Each UTXO should be ['txid' => txid, 'n' => n, 'amount' => amount (in satoshis), 'script' => script hexadecimal string] * @param mixed $change_address_collection a single address string to receive all change. Or an array of [[address, amount], [address, amount], [address]]. Amounts should be float values. An address with no amount for the last entry will send the remaining change to that address. * @param float $fee A fee * @param float $btc_dust Amount of BTC dust to send with the Counterparty transaction. * @return Array returns a ComposedTransaction object */ public function composeSend($asset, $quantity, $destination, $private_key_wif, $utxos, $change_address_collection = null, $fee = null, $btc_dust = null) { if ($asset == 'BTC') { return $this->composeBTCSend($quantity, $destination, $private_key_wif, $utxos, $change_address_collection, $fee); } $fee_satoshis = $fee === null ? self::DEFAULT_FEE : intval(round($fee * self::SATOSHI)); $btc_dust_satoshis = $btc_dust === null ? self::DEFAULT_BTC_DUST : intval(round($btc_dust * self::SATOSHI)); // get total and change amount $change_amounts = $this->calculateAndValidateChange($utxos, $btc_dust_satoshis, $fee_satoshis, $change_address_collection); $tx_builder = TransactionFactory::build(); // add the UTXO inputs $transaction_outputs = $this->addInputsAndReturnPreviousOutputs($utxos, $tx_builder); // pay the btc_dust to the destination if (is_array($destination)) { throw new Exception("Multiple destinations are not supported for counterparty sends", 1); } $tx_builder->payToAddress($btc_dust_satoshis, AddressFactory::fromString($destination)); // build the OP_RETURN script $op_return_builder = new OpReturnBuilder(); $op_return = $op_return_builder->buildOpReturn($quantity, $asset, $utxos[0]['txid']); $script = ScriptFactory::create()->op('OP_RETURN')->push(Buffer::hex($op_return, 28))->getScript(); $tx_builder->output(0, $script); // pay the change to self $this->payChange($change_amounts, $tx_builder); // sign if ($private_key_wif !== null) { $signed_transaction = $this->signTx($private_key_wif, $tx_builder, $transaction_outputs); return $this->buildReturnValuesFromTransactionAndInputs($signed_transaction, $utxos, true); } return $this->buildReturnValuesFromTransactionAndInputs($tx_builder->get(), $utxos, false); }
/** * @param string $hex * @param bool $compressed * @param EcAdapterInterface|null $ecAdapter * @return PrivateKey */ public static function fromHex($hex, $compressed = false, EcAdapterInterface $ecAdapter = null) { $hex = Buffer::hex($hex); $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter(); $hexSerializer = new HexPrivateKeySerializer($ecAdapter); return $hexSerializer->parse($hex)->setCompressed($compressed); }
/** * @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()])); }
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()); }
/** * @param BlockInterface $block * @return \BitWasp\Buffertools\BufferInterface */ public function serialize(BlockInterface $block) { $buffer = $block->getBuffer(); $size = $buffer->getSize(); $data = new Parser($this->getHeaderTemplate()->write([Buffer::hex($this->network->getNetMagicBytes()), $size])); $data->writeBytes($size, $buffer); return $data->getBuffer(); }
/** * @param NodeInterface $node * @param array $params * @return array */ public function execute(NodeInterface $node, array $params) { if (strlen($params[self::PARAM_HASH]) !== 64) { throw new \RuntimeException('Invalid hash'); } $index = $node->chain()->fetchIndex(Buffer::hex($params[self::PARAM_HASH])); return ['header' => $this->convertIndexToArray($index)]; }
/** * @param string $ip * @return Buffer */ private function getIpBuffer($ip) { $hex = (string) dechex(ip2long($ip)); $hex = strlen($hex) % 2 == 1 ? '0' . $hex : $hex; $hex = '00000000000000000000' . 'ffff' . $hex; $buffer = Buffer::hex($hex); return $buffer; }
/** * @param $hex * @return PublicKey * @throws \Exception */ public function parse($hex) { $hex = Buffer::hex($hex); if (!in_array($hex->getSize(), [PublicKey::LENGTH_COMPRESSED, PublicKey::LENGTH_UNCOMPRESSED])) { throw new \Exception('Invalid hex string, must match size of compressed or uncompressed public key'); } return $this->ecAdapter->publicKeyFromBuffer($hex); }
/** * @param Parser $parser * @return Buffer * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange */ public function read(Parser &$parser) { $bits = $this->readBits($parser->readBytes($this->length)); if (!$this->isBigEndian()) { $bits = $this->flipBits($bits); } return Buffer::hex(str_pad($this->getMath()->baseConvert($bits, 2, 16), $this->length * 2, '0', STR_PAD_LEFT), $this->length, $this->getMath()); }
/** * @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); }
/** * @param NetworkInterface|null $network * @return string */ public function getAddress(NetworkInterface $network = null) { $network = $network ?: Bitcoin::getNetwork(); $witnessByte = dechex($this->witnessVersion); $witnessByte = strlen($witnessByte) % 2 == 0 ? $witnessByte : '0' . $witnessByte; $payload = Buffer::hex($this->getPrefixByte($network) . $witnessByte . "00" . $this->getHash()); return Base58::encodeCheck($payload); }
/** * @param NodeInterface $node * @param array $params * @return array */ public function execute(NodeInterface $node, array $params) { if (strlen($params[self::PARAM_TXID]) !== 64) { throw new \RuntimeException('Invalid txid field'); } $txid = Buffer::hex($params[self::PARAM_TXID], 32); $tx = $node->chain()->fetchTransaction($node->transactions(), $txid); return ['tx' => $this->convertTransactionToArray($tx)]; }
public function testNetworkSerializer() { $net = Bitcoin::getDefaultNetwork(); $factory = new Factory($net, new Random()); $inv = $factory->inv([new Inventory(Inventory::MSG_BLOCK, Buffer::hex('4141414141414141414141414141414141414141414141414141414141414141'))]); $serialized = $inv->getNetworkMessage()->getBuffer(); $parsed = $factory->parse(new Parser($serialized))->getPayload(); $this->assertEquals($inv, $parsed); }
public function testNetworkSerializer() { $array = [new Inventory(Inventory::MSG_TX, Buffer::hex('4141414141414141414141414141414141414141414141414141414141414141')), new Inventory(Inventory::MSG_TX, Buffer::hex('4141414141414141414141414141414141414141414141414141414141414142')), new Inventory(Inventory::MSG_TX, Buffer::hex('4141414141414141414141414141414141414141414141414141414141414143'))]; $not = new NotFound($array); $serializer = new NetworkMessageSerializer(Bitcoin::getDefaultNetwork()); $serialized = $not->getNetworkMessage()->getBuffer(); $parsed = $serializer->parse($serialized)->getPayload(); $this->assertEquals($not, $parsed); }
/** * 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()); }
public function testNetworkSerializer() { $net = Bitcoin::getDefaultNetwork(); $factory = new Factory($net, new Random()); $locator = new BlockLocator([Buffer::hex('4141414141414141414141414141414141414141414141414141414141414141')], Buffer::hex('0000000000000000000000000000000000000000000000000000000000000000')); $getblocks = $factory->getblocks('1', $locator); $serialized = $getblocks->getNetworkMessage()->getBuffer(); $parsed = $factory->parse(new Parser($serialized))->getPayload(); $this->assertEquals($getblocks, $parsed); }
/** * @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 EcAdapterInterface $ecAdapter * @param $int * @param bool $compressed * @throws InvalidPrivateKey */ public function __construct(EcAdapterInterface $ecAdapter, $int, $compressed = false) { $buffer = Buffer::hex($ecAdapter->getMath()->decHex($int), 32); if (false === $ecAdapter->validatePrivateKey($buffer)) { throw new InvalidPrivateKey('Invalid private key - must be less than curve order.'); } $this->ecAdapter = $ecAdapter; $this->secretMultiplier = $int; $this->setCompressed($compressed); }
public function testIsNext() { $first = new BlockIndex(new Buffer('aa', 32), 0, 0, new BlockHeader(0, new Buffer('', 32), new Buffer('', 32), 0, new Buffer(), 0)); $nextGood = new BlockIndex(new Buffer('bb'), 1, 0, new BlockHeader(0, new Buffer('aa', 32), new Buffer('', 32), 0, new Buffer(), 0)); $nextBadHeight = new BlockIndex(Buffer::hex('bc', 32), 222, 0, new BlockHeader(0, new Buffer('aa', 32), new Buffer('', 32), 0, new Buffer(), 0)); $nextBadHash = new BlockIndex(Buffer::hex('bd', 32), 1, 0, new BlockHeader(0, new Buffer('22', 32), new Buffer('', 32), 0, new Buffer(), 0)); $this->assertFalse($first->isNext($nextBadHash)); $this->assertFalse($first->isNext($nextBadHeight)); $this->assertTrue($first->isNext($nextGood)); }
public function testNetworkSerialize() { $math = new Math(); $factory = new Factory(Bitcoin::getDefaultNetwork(), new Random()); $filter = BloomFilter::create($math, 10, 1.0E-6, 0, new Flags(BloomFilter::UPDATE_ALL)); $filter->insertData(Buffer::hex('04943fdd508053c75000106d3bc6e2754dbcff19')); $filterload = $factory->filterload($filter); $serialized = $filterload->getNetworkMessage()->getBuffer(); $parsed = $factory->parse(new Parser($serialized))->getPayload(); $this->assertEquals($parsed, $filterload); }
public function testGetVarString() { $strings = array('', '00', '00010203040506070809', '00010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102'); $math = EccFactory::getAdapter(); $varstring = new VarString(new VarInt($math)); foreach ($strings as $string) { $binary = $varstring->write(Buffer::hex($string)); $parser = new Parser(new Buffer($binary)); $original = $varstring->read($parser); $this->assertSame($string, $original->getHex()); } }
public function testNetworkAddressTimestamp() { $ip = '127.0.0.1'; $port = 8333; $time = time(); $services = Buffer::hex('0000000000000001'); $from = new NetworkAddressTimestamp($time, $services, $ip, $port); $this->assertEquals($time, $from->getTimestamp()); $this->assertEquals($services, $from->getServices()); $this->assertEquals($ip, $from->getIp()); $this->assertEquals($port, $from->getPort()); }
/** * @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)]; }
public function testNetworkSerializer() { $network = Bitcoin::getDefaultNetwork(); $time = '9999999'; $ip = '192.168.0.1'; $port = '8333'; $services = Buffer::hex('0000000000000000', 8); $add = new NetworkAddressTimestamp($time, $services, $ip, $port); $parser = new NetworkMessageSerializer(Bitcoin::getDefaultNetwork()); $factory = new Factory($network, new Random()); $addr = $factory->addr([$add]); $serialized = $addr->getNetworkMessage()->getBuffer(); $parsed = $parser->parse($serialized)->getPayload(); $this->assertEquals($addr, $parsed); }
/** * @throws \BitWasp\Bitcoin\Exceptions\RandomBytesFailure * @expectedException \RuntimeException * @expectedExceptionMessage */ public function testInvalidBytes() { $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); $bitcoin = new NetworkMessageSerializer(NetworkFactory::bitcoin()); $serialized = $version->getNetworkMessage(NetworkFactory::viacoinTestnet())->getBuffer(); $bitcoin->parse($serialized); }
public function testReadVector() { $math = EccFactory::getAdapter(); $varint = new VarInt($math); $vector = new Vector($varint, function (Parser &$parser) { return $parser->readBytes(16); }); $eBuffer = Buffer::hex('010203040506070809000a0b0c0d0e0f'); $hex = '03010203040506070809000a0b0c0d0e0f010203040506070809000a0b0c0d0e0f010203040506070809000a0b0c0d0e0f'; $buffer = Buffer::hex($hex); $parser = new Parser($buffer); $array = $vector->read($parser); foreach ($array as $item) { $this->assertEquals($eBuffer->getBinary(), $item->getBinary()); } }