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); }); }
public function __construct(LoopInterface $loop) { $context = new \React\ZMQ\Context($loop); $pusher = new Pusher(); $workers = $context->getSocket(\ZMQ::SOCKET_PUSH); $workers->bind("tcp://127.0.0.1:5557"); // Listen for work from the web $receiver = $context->getSocket(\ZMQ::SOCKET_PULL); $receiver->bind("tcp://127.0.0.1:5555"); $receiver->on('message', function ($msg) use($workers) { // Create echo "Received: " . $msg . "\n"; $workers->send($msg); }); $control = $context->getSocket(\ZMQ::SOCKET_PULL); $control->bind('tcp://127.0.0.1:5510'); $control->on('message', function ($msg) use($pusher) { echo "CONTROL MESSAGE\n"; $arr = json_decode($msg, true); $slug = $arr['slug']; $req = $arr['req']; $pusher->send($slug, $arr['req']); }); $listener = $context->getSocket(\ZMQ::SOCKET_PULL); $listener->bind('tcp://127.0.0.1:5559'); $listener->on('message', function ($msg) use($pusher) { echo " + - - - - - - - - - + \n"; echo " RESULTS \n"; print_r($msg); $message = json_decode($msg, true); if ($message['command'] == 'client.gotRequest') { $pusher->onClientGotRequest($message['slug']); } else { if ($message['command'] == 'client.gotPayment') { $pusher->onClientGotPayment($message['slug']); } else { if ($message['command'] == 'tx.complete') { try { $request = Request::find(['slug' => $message['slug']]); $transaction = TransactionFactory::fromHex($message['tx']); Transaction::create(['transaction' => $message['tx'], 'request_id' => $request->id, 'txid' => $transaction->getTransactionId()]); $pusher->onCompleteTx($message['slug'], $message['tx']); } catch (\Exception $e) { return; } } else { if ($message['command'] == 'tx.partial') { $pusher->onCompleteTx($message['slug'], $message['tx']); } } } } echo "\n + - - - - - - - - - + \n\n"; }); // Set up our WebSocket server for clients wanting real-time updates $webSock = new \React\Socket\Server($loop); $webSock->listen(8080, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect $webServer = new \Ratchet\Server\IoServer(new \Ratchet\Http\HttpServer(new \Ratchet\WebSocket\WsServer(new \Ratchet\Wamp\WampServer($pusher))), $webSock); }
/** * @param $txid * @return \React\Promise\Promise */ public function transactionGet($txid) { return $this->client->request('blockchain.transaction.get', [$txid])->then(function (Response $response) { return TransactionFactory::fromHex($response->getResult()); }); }
/** * @param TransactionInterface $tx * @param array $inputs * @param array $privateKeys * @param int $sighash * @param NetworkInterface $network * @return \BitWasp\Bitcoin\Transaction\Transaction * @throws \Exception */ public function signrawtransaction(TransactionInterface $tx, array $inputs = [], array $privateKeys = [], $sighash = SigHash::ALL, NetworkInterface $network = null) { $tx = $this->client->execute('signrawtransaction', [$tx->getHex(), $inputs, array_map(function (PrivateKeyInterface $privateKey) use($network) { return $privateKey->toWif($network); }, $privateKeys), $sighash]); $this->checkNotNull($tx); return TransactionFactory::fromHex($tx['hex']); }
<?php require_once __DIR__ . "/../vendor/autoload.php"; use BitWasp\Bitcoin\Bitcoin; use BitWasp\Bitcoin\Transaction\TransactionFactory; use BitWasp\Bitcoin\Address\AddressFactory; use BitWasp\Bitcoin\Key\PrivateKeyFactory; Bitcoin::setNetwork(\BitWasp\Bitcoin\Network\NetworkFactory::bitcoinTestnet()); $network = Bitcoin::getNetwork(); $ecAdapter = Bitcoin::getEcAdapter(); $privateKey = PrivateKeyFactory::fromHex('17a2209250b59f07a25b560aa09cb395a183eb260797c0396b82904f918518d5'); echo "[Key: " . $privateKey->toWif($network) . " Address " . $privateKey->getAddress()->getAddress($network) . "]\n"; $txHex = '010000000114a2856f5a2992a4ca0814be16a0ae79e2f88a6f53a20fcbcad5249165f56ee7010000006a47304402201e733603ac36239010e05ad229b4a18411d5507950f696db0771a5b7fe8e051202203c46da7e970e89cbbdfb4ee62fa775597a32e5029ab1d2a94f786999df2c2fd201210271127f11b833239aefd400b11d576e7cc48c6969c8e5f8e30b0f5ec0a514edf7feffffff02801a0600000000001976a914c4126d1b70f5667e492e3301c3aa8bf1031e21a888ac75a29d1d000000001976a9141ef8d6913c289890a5e9ec249fedde4440877d0288ac88540500'; $myTx = TransactionFactory::fromHex($txHex); $spendOutput = 0; $recipient = AddressFactory::fromString('n1b2a9rFvuU9wBgBaoWngNvvMxRV94ke3x'); echo "[Send to: " . $recipient->getAddress($network) . " \n"; echo "Generate a transaction spending the one above \n"; $spendTx = TransactionFactory::build()->spendOutputFrom($myTx, $spendOutput)->payToAddress(40000, $recipient)->get(); echo "Sign transaction\n"; $signer = TransactionFactory::sign($spendTx); $signer->sign(0, $privateKey, $myTx->getOutput($spendOutput)->getScript()); echo "Generate transaction: \n"; $new = $signer->get(); echo $new->getHex() . "\n";
/** * @return TransactionCollection */ public function getTransactions() { return new TransactionCollection(array_map(function ($binTx) { return TransactionFactory::fromHex(bin2hex($binTx)); }, $this->payment->getTransactionsList())); }
public function parseBTCTransaction($raw_transaction_hex, $is_counterparty = false) { if (!$raw_transaction_hex) { throw new Exception("Transaction hex was empty", 1); } $transaction = TransactionFactory::fromHex($raw_transaction_hex); $out = []; // inputs $inputs = $transaction->getInputs(); $out['inputs'] = []; foreach ($inputs as $input) { // build the ASM $asm = ""; $opcodes = $input->getScript()->getOpCodes(); foreach ($input->getScript()->getScriptParser()->decode() as $op) { // $asm .= $opcodes->getOp($op->getOp()); if ($op->isPush()) { $item = $op->getData()->getHex(); } else { $item = $opcodes->getOp($op->getOp()); } $asm = ltrim($asm . " " . $item); } // extract the address $address = null; // address decoding not implemented yet // $classifier = new InputClassifier($script); // if ($classifier->isPayToPublicKeyHash()) { // $decoded = $script->getScriptParser()->decode(); // $hex_buffer = $decoded[1]->getData(); // $public_key = PublicKeyFactory::fromHex($hex_buffer); // $address = $public_key->getAddress()->getAddress(); // } else if ($classifier->isPayToScriptHash()) { // $decoded = $script->getScriptParser()->decode(); // $hex_buffer = $decoded[count($decoded)-1]->getData(); // $script = ScriptFactory::fromHex($hex_buffer); // $sh_address = new ScriptHashAddress($script->getScriptHash()); // $address = $sh_address->getAddress(); // } $out['inputs'][] = ['txid' => $input->getTransactionId(), 'n' => $input->getVout(), 'asm' => $asm]; } // txid $out['txid'] = $transaction->getTxId()->getHex(); // destination $output_offset = 0; $outputs = $transaction->getOutputs(); $destination = AddressFactory::getAssociatedAddress($outputs[$output_offset]->getScript()); $out['destination'] = $destination; $out['btc_amount'] = $outputs[0]->getValue(); if ($is_counterparty) { // OP_RETURN ++$output_offset; $obfuscated_op_return_hex = $outputs[$output_offset]->getScript()->getScriptParser()->decode()[1]->getData()->getHex(); $hex = $this->arc4decrypt($transaction->getInput(0)->getTransactionId(), $obfuscated_op_return_hex); $counterparty_data = $this->parseTransactionData(hex2bin(substr($hex, 16))); $out = $counterparty_data + $out; $out['btc_dust_size'] = $outputs[0]->getValue(); } // change ++$output_offset; $change = []; for ($i = $output_offset; $i < count($outputs); $i++) { $output = $outputs[$i]; $change[] = [AddressFactory::getAssociatedAddress($outputs[$i]->getScript()), $output->getValue()]; } $out['change'] = $change; // sum all outputs $sum_out = 0; foreach ($outputs as $output) { $sum_out += $output->getValue(); } $out['sum_out'] = $sum_out; return $out; }
private function getTestTx() { $hex = '0100000014e3b8f4a75dd3a033744d8245148d5a8b734e6ebb157ac12d49e65d4f01f6c86c000000006c493046022100e8a2df24fd890121d8dd85c249b742d0585ec17d18b1bf97050e72eaaceb1580022100d7c37967048a617d7551c8249ea5e58cbf71a0508a6c459dd3a9dfefba3c592f0121030c51892ad8c9df7590c84bc2475576d6dc0815a5bf3ca37f1c58fe82e45d9ef5ffffffff0f1408fa2773334487d1d37e45cb399d049c7e46db3faadfcc204656bce57f5e000000006b483045022100cc339da0e9330b2375124a5fae678b130a4e5215310d85a1db2c7da32dd9633a02205fb02c932eab91733920bab341ad61097f2ff5dc73e46577ce3b70fd0e2ecc4b0121033fd9e31bd2bdc7029d6f1cca55655c4b484aca7fdea11547b37a4aeaf347e132ffffffff72a329f6d5cb92a3cd9e8aa252f0c683f28cb57e8884cfff42ffdddaca86f2e4000000006a473044022013f6b4e159ba9f88825746f9c7d1131cb14667a83d6b3871b5bab2a8f9f75759022024e29aa4bb7c7b994468c0a7a7add2bb180fc9a48b0187d2e06239b358cce8eb0121037cf462b312b1696f2654a21100a6a726238b91455d0f50f69526335d9022fdc5ffffffff49d1bb27b1027249326f3ad35a06b3c7fc9af2c0318caa02c1a90ae2e2a46cf8000000006c493046022100b6381612d4a5b1c75d57fdf93ce8fe39d4541a97af5cd312dbd91925cbd2037e022100a32954780c7d711059524f03ed78cf2e234554977a7e2139e7e7c6949835da660121025a4098ac03d3f706bfdaa795f80350aad15b5a6a578cee2991c16edae0255e76ffffffffc6b749366d13fca59f264b2714bc090660eb291361f23d79b6515f48f35dfd2c010000006b48304502205421f94ee54d829f921785860d4603b82caf8f3722f768e26970f4baa625c0dc022100f3c0cee26b1a98558386fbfcb568100582acc7bc8423e3402313298e0a3291520121026bff9f45e1645a6a67f70e80439d982d3d6dd0fe31258f93ae65161a14b54648ffffffff1f48a79c65634eb19c4a7eaca76a3f8f7c47b649cf54c8fbb5be6f87f6a42fa7010000006a47304402207907ec39e5ff6a85c5c0e5b8d7a81750e1e450e85839d87cdde4cfba405f6ee602205439aa642218bb676d2d2b9c0c63dd44395ad85fad2954ac1794bb4b35e134fc0121033fd9e31bd2bdc7029d6f1cca55655c4b484aca7fdea11547b37a4aeaf347e132ffffffff3a339577e45201a85797937aa44fe7a31f9240ba8a9169e46c32bbd82fbc2ae3000000006a473044022018e0dbfb5ee617fb7d1c28c672fb5428cbc1edb6ed8cc76bc68682432c4bfe450220187a7d4e7b2af69a8e7c7ebfd1b6f02143cf7a32c1041c01bc5324bcd97101880121033fd9e31bd2bdc7029d6f1cca55655c4b484aca7fdea11547b37a4aeaf347e132ffffffff7d0e4f252c644d051e35ee839abde736f26dd2046c7ca18711c759e3c86fb15a220000006a473044022004dfb719c5afca95db100f4e55fb4dd27f4f9faf1a6eff390d9bbdbf9b28fa4b02206dfd0d5f74b4c6d8424d44461c6a230fc174902d2be8d1792e029c3d67a1be9601210388da4b2db35387cf3bfc786453e8ca952b5386c95ce1b39e7ff5cf62cb7033fbfffffffff009c6f8a9b86d97989eaaacf3ada9f9428bae3a1a01339b85fa541680271c3a000000006b48304502203c17278401d3f7e6ab56597b6b88c783db5aa534897954e92a95732b5d714a860221008be28d72988a8ba69ff3889337468df0e83612e9cfbead9118fb8f4decfb3e93012102cda10f1505ff6a3613f6aa31becba07e44eddc25110ae083e360577556f0178dffffffff75068f094fd516d1658dac8c88ae027852a84423831b780925fb3d591a308227010000006b48304502203ce1a111748534bc601cc4a879f4097b098f7622d28a3da89ac7b90f3b29ee52022100b7de6a03241f6fea37179c3b2f68f45b69c963edc19dc0b3b0350bb8ffbd9063012102c64a6d411eef9f79890d317ad72329608be5f58f6757e1c5c6d4f21076345ddcfffffffffc844c99e1adab4677dda6b5def33ce549293a40d08da6a8c36fb9274c76ba43000000006c493046022100ee2b946560aced0633a5151f1fa5dd0249d68bef420e43e1f7edfd0e83619cfd022100a433121fb022efb69043310bd982a842e7f5be737069d3535962ecd78c4954070121033fd9e31bd2bdc7029d6f1cca55655c4b484aca7fdea11547b37a4aeaf347e132ffffffff32f65f550d4ef09e451303af1d2f9964b4f62e7b6562d5dbe23d4be89ae32e36010000006b483045022100bb4b1af8c22e51e9a3f71fb2cd883746e3812cbfd6a0695bbd22e53d573cb6f20220539e478519411e1d277a844810ab756004cc7755ff585cda7aa2c9d93ab4570b01210290a17b828f33417e7e3cb179bd50a621c8381964e6e239e91eacf3f414018770fffffffff6a6dafe0ae32f6c891de86c4aa12b318eeb89ca4d8792906ae03fd0db04c76f010000006b48304502203dfe2f1d58fdcab2f67f079c14a26c69e88718d38615d5de8230ee74c1e55d9e022100bc7651cf77f142366b502c65c59790a3d2cfa52852feab04212d3ea68e040be0012103214083806ce8aebf35544845c73f1e9d4dcad2fc6057e6cda963498f5420fef9ffffffff11c3e0e2a520bd829fab9b4311e603de79ce1304e1f28204334f79d1fd2c9138010000006b48304502206196ca600a02bf7fd210489e520b915b521dfa84a37bf56f2022aa8e89d62e6f022100949aabaafe92aaa8e207f0699a4745179fdc34a475d5c783785a58744ed5dd410121033fd9e31bd2bdc7029d6f1cca55655c4b484aca7fdea11547b37a4aeaf347e132ffffffff54dffe807c0b935528100313322949738717dc2af2f374f2a72af24b82291f70000000006b483045022100b84abff6f636082d2b85da4de25de13388a7901f0df5b87d871305e694667b4f0220477619c2140bc2d7dfab0d2b7d49f0729afb951831c7a64ee4bede7d8a46960b012102cda10f1505ff6a3613f6aa31becba07e44eddc25110ae083e360577556f0178dffffffff9923148b409a0888e42612e964216eb575b8fe4d0cfe5aad84323962c219373b000000006b483045022100cc88b67cce1655c38c60ada140b9c34343411cc309c45410edf2b4f133520c03022044c3554a294f6a412219b710689d67c5d8519fc6a139ee311985a14b8b55fd6f012102cda10f1505ff6a3613f6aa31becba07e44eddc25110ae083e360577556f0178dffffffff5bb09bbcbdd756af1f08457f7c1319aa67d5488f359b58e6f36920b808c789bb010000006a473044022051b255884c8f118780e394c054c5df12d813e3b9983a9854c89e7ffd015cbf5702200ed58fac38a91e19d755d30d910a04aa29848608b72f99aef68e0ecedcbdca53012103f57996ef25762717a75b6d75f0166a33f775783766513118b5476933d7af8078ffffffffc3c978e2e413506b75b7882cc38cf6f40d199c7226f73b5e4ab8295703fd4b03d50000006b48304502201a07c4ce0f76c4f5d96c45811bfa08c00483c987e1297324f40136a4c452c306022100d553873b6b3c20ae2b6a588704bae9daa5093b17283e0b196a27d6e4022ff8a50121032beebbe7e386f1fd27a9a3e59640deaa5f60835ab789012175c0f517149c77e3ffffffff3944343f32f93d43752bea7d916571e236295fbff53c7a9133d09f741116fac5000000006b483045022074aac638f77fed744feb1e99f4a71f20278686fc2f91264058facd9983cde9fb0221008250b8d62dbb8d3954517f9f80e236d1c74720723459b5d69656dadf781ee2e9012102cda10f1505ff6a3613f6aa31becba07e44eddc25110ae083e360577556f0178dffffffff781119976951dfc6f8a617077068fcb623475acef7eff09808a5ef3281a0ef70010000006b48304502200236e5c6b0787756449043fd413307c176be2b39fb7da11a18e10708a72877fe022100836b99f32532039e6326cbf282cbf78140c53883c474443bd6e6145e52dfb08d012103dd033367f07aee4f365a47cdf3c45147887492c6d20788970296d3b94eb1cfd8ffffffff1365487900000000001976a9143f6cd41f7caeda87b86bc9e009295f179612f45488acc7440f00000000001976a914df90390bee06889ed003b3c4d024a9fd211cf96788ace0ee4504000000001976a9141d8aa01e6628f333fd6fd9d3b88c3f761869caa488acd5080200000000001976a91412d24a8a61e1cd37825fd31bd61eebc2c32ad77688ac0842b582000000001976a9147fb7cccd54bf322ddae63b6b2e20a3624622091888acd5113916000000001976a914e75fa783b5b319578a53f2f18c37d88513d060b088ac80969800000000001976a9140ce8c479dad1b78baeea2217af17655b908b59b888ace8fb1204000000001976a9141a80a0ee9b4f1d03c3fb2220e4124d441431d41888ac00e1f505000000001976a914b1fdf35dd37a1a5359ad829f5f43760cd1ea61d588ac80626a94000000001976a9142a929cae46f0b4e5742ae38d6040f11a2b70e7d188ac109d0a02000000001976a914a4b5bfbe26ef9ac8d6e06738b50065b25f3dcce288acab110400000000001976a9146fbc4dd98c194853dc9a3e6f195bddad8eef609788ac00e1f505000000001976a914e1733c6b9e4c98f4ddc19370dd5cae0727af04e788ac5d44d502000000001976a9147514615f455bdfb8b9da74c7707ef0426606c33288ac404b4c00000000001976a914166e78015832ba760593bb292993391d4554a39a88acf0190b01000000001976a91409859ea62e00e0531873c135e37efbeca610d36788ace2fcae8f000000001976a914b40d92da29c478049a8d15bfd85b6ce230863df288acc07a3e22000000001976a9141e12d7845e76857bfba94079c1faca68b3ae24c088ac9e130e01000000001976a9144bdb0b7712726c2684461cd4e5b08934def0187c88ac00000000'; $tx = TransactionFactory::fromHex($hex); return [$hex, $tx]; }
require "vendor/autoload.php"; use React\EventLoop\Factory as LoopFactory; use React\ZMQ\Context as ZmqContext; use BitWasp\Bitcoin\Script\ScriptFactory; use BitWasp\Bitcoin\Transaction\TransactionFactory; use BitWasp\Bitcoin\Flags; use BitWasp\Buffertools\Buffer; use BitWasp\Bitcoin\Script\Script; $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(); } }); $workers = $context->getSocket(\ZMQ::SOCKET_REP); $workers->connect('tcp://127.0.0.1:5592'); $workers->on('message', function ($message) use($workers) { $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']); $workers->send(json_encode(['txid' => $txid, 'vin' => $vin, 'result' => ScriptFactory::consensus($flags)->verify($tx, $scriptPubKey, $vin, null)])); }); $loop->run();
public function testComposeP2SHSourceCounterpartyTransaction() { list($sender_address_1, $wif_key_1, $private_key_1) = $this->newAddressAndKey(); list($sender_address_2, $wif_key_2, $private_key_2) = $this->newAddressAndKey(); list($sender_address_3, $wif_key_3, $private_key_3) = $this->newAddressAndKey(); // build a multisig address (2 of 3) $public_keys = [$private_key_1->getPublicKey(), $private_key_2->getPublicKey(), $private_key_3->getPublicKey()]; $p2sh_script = ScriptFactory::p2sh()->multisig(2, $public_keys); $sender_address = $p2sh_script->getAddress()->getAddress(); // variables $utxos = $this->fakeUTXOs($sender_address); $asset = 'SOUP'; $quantity = 45; $destination = '1AAAA1111xxxxxxxxxxxxxxxxxxy43CZ9j'; $fee = 0.0001; $btc_dust = 5.432E-5; // compose the send $composer = new Composer(); $composed_unsigned_send = $composer->composeSend($asset, CryptoQuantity::fromFloat($quantity), $destination, null, $utxos, $sender_address, $fee, $btc_dust); list($txid, $unsigned_hex, $new_utxos) = $this->decomposeComposedTransaction($composed_unsigned_send); // parse the signed hex $transaction = TransactionFactory::fromHex($unsigned_hex); // check output 1 $tx_output_0 = $transaction->getOutput(0); PHPUnit::assertEquals(intval(round($btc_dust * self::SATOSHI)), $tx_output_0->getValue()); PHPUnit::assertEquals($destination, AddressFactory::fromOutputScript($tx_output_0->getScript())->getAddress()); // check output 2 $tx_output_1 = $transaction->getOutput(1); $op_return = $tx_output_1->getScript()->getScriptParser()->decode()[1]->getData()->getHex(); $txid = $transaction->getInput(0)->getOutPoint()->getTxId()->getHex(); $hex = $this->arc4decrypt($txid, $op_return); $expected_hex = '434e54525052545900000000000000000004fadf000000010c388d00'; PHPUnit::assertEquals($expected_hex, $hex); // check output 3 $tx_output_2 = $transaction->getOutput(2); PHPUnit::assertEquals(intval(round((0.123 + 0.0005 - $fee - $btc_dust) * self::SATOSHI)), $tx_output_2->getValue()); PHPUnit::assertEquals($sender_address, AddressFactory::fromOutputScript($tx_output_2->getScript())->getAddress()); // check $new_utxos PHPUnit::assertNotEmpty($new_utxos); PHPUnit::assertEquals(5432, $new_utxos[0]['amount']); }