bitcoin-ruby 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.rubocop.yml +46 -0
- data/.travis.yml +5 -1
- data/Gemfile +11 -9
- data/Gemfile.lock +64 -12
- data/README.rdoc +17 -4
- data/Rakefile +58 -83
- data/bitcoin-ruby.gemspec +5 -2
- data/lib/bitcoin.rb +31 -14
- data/lib/bitcoin/bech32.rb +126 -132
- data/lib/bitcoin/bloom_filter.rb +24 -21
- data/lib/bitcoin/builder.rb +168 -126
- data/lib/bitcoin/connection.rb +21 -24
- data/lib/bitcoin/contracthash.rb +20 -24
- data/lib/bitcoin/dogecoin.rb +79 -77
- data/lib/bitcoin/electrum/mnemonic.rb +28 -25
- data/lib/bitcoin/ext_key.rb +3 -3
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +17 -13
- data/lib/bitcoin/ffi/openssl.rb +355 -338
- data/lib/bitcoin/ffi/secp256k1.rb +97 -64
- data/lib/bitcoin/protocol.rb +6 -3
- data/lib/bitcoin/protocol/address.rb +15 -13
- data/lib/bitcoin/protocol/aux_pow.rb +12 -15
- data/lib/bitcoin/protocol/block.rb +102 -76
- data/lib/bitcoin/protocol/handler.rb +2 -4
- data/lib/bitcoin/protocol/parser.rb +108 -92
- data/lib/bitcoin/protocol/partial_merkle_tree.rb +59 -47
- data/lib/bitcoin/protocol/reject.rb +26 -28
- data/lib/bitcoin/protocol/script_witness.rb +3 -8
- data/lib/bitcoin/protocol/tx.rb +250 -137
- data/lib/bitcoin/protocol/txin.rb +44 -38
- data/lib/bitcoin/protocol/txout.rb +27 -20
- data/lib/bitcoin/protocol/version.rb +47 -34
- data/lib/bitcoin/script.rb +18 -17
- data/lib/bitcoin/trezor/mnemonic.rb +113 -98
- data/lib/bitcoin/version.rb +1 -1
- data/spec/examples.txt +399 -0
- data/spec/{bitcoin/fixtures → fixtures}/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/base58_keys_invalid.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/base58_keys_valid.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/coinbase-toshi.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/coinbase.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/filteredblock-0.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-0.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-0.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-1.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-1.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-131025.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-131025.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-170.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-9.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-auxpow.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-testnet-1151351.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-testnet-26478.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-testnet-26478.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawblock-testnet-265322.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-01-toshi.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-01.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-01.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-02-toshi.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-02.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-02.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-03-toshi.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-03.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-03.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-04.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-05.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-p2wpkh.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-p2wpkh.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/script_tests.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/sighash.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-9fb65b7304aaa77ac9580823c2c06b259cc42591e5cce66d76a81b6f51cc5c28.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-a6ce7081addade7676cd2af75c4129eba6bf5e179a19c40c7d4cf6a5fe595954.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +0 -0
- data/spec/{bitcoin/fixtures → fixtures}/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +0 -0
- data/spec/helpers/block_helpers.rb +58 -0
- data/spec/helpers/fixture_helpers.rb +20 -0
- data/spec/helpers/library_helpers.rb +15 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/unit/bitcoin/bech32_spec.rb +187 -0
- data/spec/unit/bitcoin/bitcoin_spec.rb +1079 -0
- data/spec/unit/bitcoin/bloom_filter_spec.rb +33 -0
- data/spec/unit/bitcoin/builder_spec.rb +559 -0
- data/spec/unit/bitcoin/contracthash_spec.rb +52 -0
- data/spec/unit/bitcoin/ext_key_spec.rb +281 -0
- data/spec/unit/bitcoin/key_spec.rb +457 -0
- data/spec/unit/bitcoin/network_spec.rb +71 -0
- data/spec/unit/bitcoin/protocol/addr_spec.rb +90 -0
- data/spec/unit/bitcoin/protocol/aux_pow_spec.rb +45 -0
- data/spec/unit/bitcoin/protocol/bip143_spec.rb +334 -0
- data/spec/unit/bitcoin/protocol/block_spec.rb +280 -0
- data/spec/unit/bitcoin/protocol/getblocks_spec.rb +44 -0
- data/spec/unit/bitcoin/protocol/inv_spec.rb +166 -0
- data/spec/unit/bitcoin/protocol/notfound_spec.rb +44 -0
- data/spec/unit/bitcoin/protocol/parser_spec.rb +69 -0
- data/spec/unit/bitcoin/protocol/partial_merkle_tree_spec.rb +47 -0
- data/spec/unit/bitcoin/protocol/ping_spec.rb +62 -0
- data/spec/unit/bitcoin/protocol/tx_spec.rb +1515 -0
- data/spec/unit/bitcoin/protocol/txin_spec.rb +47 -0
- data/spec/unit/bitcoin/protocol/txout_spec.rb +36 -0
- data/spec/unit/bitcoin/protocol/version_spec.rb +121 -0
- data/spec/unit/bitcoin/script/opcodes_spec.rb +864 -0
- data/spec/unit/bitcoin/script/script_spec.rb +1610 -0
- data/spec/unit/bitcoin/secp256k1_spec.rb +138 -0
- data/spec/unit/bitcoin/trezor/mnemonic_spec.rb +193 -0
- data/spec/unit/integrations/dogecoin_spec.rb +215 -0
- metadata +381 -372
- data/lib/bitcoin/logger.rb +0 -86
- data/lib/bitcoin/protocol/alert.rb +0 -46
- data/spec/bitcoin/bech32_spec.rb +0 -160
- data/spec/bitcoin/bitcoin_spec.rb +0 -666
- data/spec/bitcoin/bloom_filter_spec.rb +0 -23
- data/spec/bitcoin/builder_spec.rb +0 -375
- data/spec/bitcoin/contracthash_spec.rb +0 -45
- data/spec/bitcoin/dogecoin_spec.rb +0 -176
- data/spec/bitcoin/ext_key_spec.rb +0 -180
- data/spec/bitcoin/ffi_openssl.rb +0 -45
- data/spec/bitcoin/fixtures/rawblock-170.json +0 -68
- data/spec/bitcoin/fixtures/rawblock-9.json +0 -39
- data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
- data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
- data/spec/bitcoin/fixtures/txdp-1.txt +0 -32
- data/spec/bitcoin/fixtures/txdp-2-signed.txt +0 -19
- data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +0 -14
- data/spec/bitcoin/helpers/fake_blockchain.rb +0 -183
- data/spec/bitcoin/key_spec.rb +0 -326
- data/spec/bitcoin/network_spec.rb +0 -50
- data/spec/bitcoin/performance/storage_spec.rb +0 -41
- data/spec/bitcoin/protocol/addr_spec.rb +0 -82
- data/spec/bitcoin/protocol/alert_spec.rb +0 -22
- data/spec/bitcoin/protocol/aux_pow_spec.rb +0 -45
- data/spec/bitcoin/protocol/bip143_spec.rb +0 -116
- data/spec/bitcoin/protocol/block_spec.rb +0 -208
- data/spec/bitcoin/protocol/getblocks_spec.rb +0 -32
- data/spec/bitcoin/protocol/inv_spec.rb +0 -134
- data/spec/bitcoin/protocol/notfound_spec.rb +0 -31
- data/spec/bitcoin/protocol/parser_spec.rb +0 -50
- data/spec/bitcoin/protocol/partial_merkle_tree_spec.rb +0 -38
- data/spec/bitcoin/protocol/ping_spec.rb +0 -51
- data/spec/bitcoin/protocol/reject.rb +0 -17
- data/spec/bitcoin/protocol/tx_spec.rb +0 -894
- data/spec/bitcoin/protocol/txin_spec.rb +0 -45
- data/spec/bitcoin/protocol/txout_spec.rb +0 -33
- data/spec/bitcoin/protocol/version_spec.rb +0 -110
- data/spec/bitcoin/script/opcodes_spec.rb +0 -773
- data/spec/bitcoin/script/script_spec.rb +0 -977
- data/spec/bitcoin/secp256k1_spec.rb +0 -78
- data/spec/bitcoin/spec_helper.rb +0 -108
- data/spec/bitcoin/trezor/mnemonic_spec.rb +0 -161
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Bitcoin
|
|
4
4
|
module Protocol
|
|
5
|
-
|
|
5
|
+
# https://en.bitcoin.it/wiki/Protocol_documentation#block
|
|
6
6
|
class Block
|
|
7
|
-
|
|
8
7
|
BLOCK_VERSION_DEFAULT = (1 << 0)
|
|
9
8
|
BLOCK_VERSION_AUXPOW = (1 << 8)
|
|
10
9
|
BLOCK_VERSION_CHAIN_START = (1 << 16)
|
|
@@ -15,8 +14,10 @@ module Bitcoin
|
|
|
15
14
|
|
|
16
15
|
# previous block hash
|
|
17
16
|
attr_accessor :prev_block_hash
|
|
18
|
-
alias
|
|
19
|
-
def prev_block=(hash)
|
|
17
|
+
alias prev_block prev_block_hash
|
|
18
|
+
def prev_block=(hash)
|
|
19
|
+
@prev_block_hash = hash
|
|
20
|
+
end
|
|
20
21
|
|
|
21
22
|
# transactions (Array of Tx)
|
|
22
23
|
attr_accessor :tx
|
|
@@ -44,7 +45,7 @@ module Bitcoin
|
|
|
44
45
|
|
|
45
46
|
attr_reader :partial_merkle_tree
|
|
46
47
|
|
|
47
|
-
alias
|
|
48
|
+
alias transactions tx
|
|
48
49
|
|
|
49
50
|
# compare to another block
|
|
50
51
|
def ==(other)
|
|
@@ -52,16 +53,17 @@ module Bitcoin
|
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
def binary_hash
|
|
55
|
-
[@hash].pack(
|
|
56
|
+
[@hash].pack('H*')
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def prev_block_hex
|
|
59
|
-
@prev_block_hex ||= @prev_block_hash.reverse.unpack(
|
|
60
|
+
@prev_block_hex ||= @prev_block_hash.reverse.unpack('H*')[0]
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# create block from raw binary +data+
|
|
63
|
-
def initialize(data=nil)
|
|
64
|
+
def initialize(data = nil)
|
|
64
65
|
@tx = []
|
|
66
|
+
@payload = nil
|
|
65
67
|
parse_data_from_io(data) if data
|
|
66
68
|
end
|
|
67
69
|
|
|
@@ -72,68 +74,74 @@ module Bitcoin
|
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
# parse raw binary data
|
|
75
|
-
def parse_data_from_io(buf, header_only=false)
|
|
77
|
+
def parse_data_from_io(buf, header_only = false)
|
|
76
78
|
buf = buf.is_a?(String) ? StringIO.new(buf) : buf
|
|
77
|
-
@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack(
|
|
79
|
+
@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack('Va32a32VVV')
|
|
78
80
|
recalc_block_hash
|
|
79
81
|
|
|
80
|
-
if Bitcoin.network[:auxpow_chain_id]
|
|
82
|
+
if !Bitcoin.network[:auxpow_chain_id].nil? && (@ver & BLOCK_VERSION_AUXPOW) > 0
|
|
81
83
|
@aux_pow = AuxPow.new(nil)
|
|
82
84
|
@aux_pow.parse_data_from_io(buf)
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
return buf if buf.eof?
|
|
86
88
|
|
|
87
|
-
if header_only == :filtered
|
|
88
|
-
@tx_count = buf.read(4).unpack("V")[0]
|
|
89
|
-
|
|
90
|
-
nhashes = Protocol.unpack_var_int_from_io(buf)
|
|
91
|
-
hashes = []
|
|
92
|
-
nhashes.times do
|
|
93
|
-
hashes << buf.read(256 / 8)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
nflags = Protocol.unpack_var_int_from_io(buf)
|
|
97
|
-
flags = buf.read(nflags)
|
|
98
|
-
|
|
99
|
-
@partial_merkle_tree = PartialMerkleTree.new(@tx_count, hashes, flags)
|
|
100
|
-
@partial_merkle_tree.set_value
|
|
101
|
-
|
|
102
|
-
return buf
|
|
103
|
-
end
|
|
89
|
+
return parse_filtered_header(buf) if header_only == :filtered
|
|
104
90
|
|
|
105
91
|
tx_size = Protocol.unpack_var_int_from_io(buf)
|
|
106
92
|
@tx_count = tx_size
|
|
107
93
|
return buf if header_only
|
|
108
94
|
|
|
109
|
-
tx_size.times
|
|
95
|
+
tx_size.times do
|
|
110
96
|
break if payload == true
|
|
111
97
|
return buf if buf.eof?
|
|
112
98
|
|
|
113
99
|
t = Tx.new(nil)
|
|
114
|
-
|
|
100
|
+
t.parse_data_from_io(buf)
|
|
115
101
|
@tx << t
|
|
116
|
-
|
|
102
|
+
end
|
|
117
103
|
|
|
118
104
|
@payload = to_payload
|
|
119
105
|
buf
|
|
120
106
|
end
|
|
121
107
|
|
|
108
|
+
def parse_filtered_header(buf)
|
|
109
|
+
@tx_count = buf.read(4).unpack('V')[0]
|
|
110
|
+
|
|
111
|
+
nhashes = Protocol.unpack_var_int_from_io(buf)
|
|
112
|
+
hashes = []
|
|
113
|
+
nhashes.times do
|
|
114
|
+
hashes << buf.read(256 / 8)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
nflags = Protocol.unpack_var_int_from_io(buf)
|
|
118
|
+
flags = buf.read(nflags)
|
|
119
|
+
|
|
120
|
+
@partial_merkle_tree = PartialMerkleTree.new(@tx_count, hashes, flags)
|
|
121
|
+
@partial_merkle_tree.assign_value
|
|
122
|
+
|
|
123
|
+
buf
|
|
124
|
+
end
|
|
125
|
+
|
|
122
126
|
# recalculate the block hash
|
|
123
127
|
def recalc_block_hash
|
|
124
|
-
@hash = Bitcoin.block_hash(
|
|
128
|
+
@hash = Bitcoin.block_hash(
|
|
129
|
+
@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver
|
|
130
|
+
)
|
|
125
131
|
end
|
|
126
132
|
|
|
127
133
|
def recalc_block_scrypt_hash
|
|
128
|
-
@scrypt_hash = Bitcoin.block_scrypt_hash(
|
|
134
|
+
@scrypt_hash = Bitcoin.block_scrypt_hash(
|
|
135
|
+
@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver
|
|
136
|
+
)
|
|
129
137
|
end
|
|
130
138
|
|
|
131
139
|
def recalc_mrkl_root
|
|
132
140
|
@mrkl_root = if partial_merkle_tree
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
141
|
+
partial_merkle_tree.root.value.htb_reverse
|
|
142
|
+
else
|
|
143
|
+
Bitcoin.hash_mrkl_tree(@tx.map(&:hash)).last.htb_reverse
|
|
144
|
+
end
|
|
137
145
|
end
|
|
138
146
|
|
|
139
147
|
# verify mrkl tree
|
|
@@ -141,7 +149,7 @@ module Bitcoin
|
|
|
141
149
|
if partial_merkle_tree
|
|
142
150
|
partial_merkle_tree.valid_tree?(@mrkl_root.reverse_hth)
|
|
143
151
|
else
|
|
144
|
-
@mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree(
|
|
152
|
+
@mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree(@tx.map(&:hash)).last
|
|
145
153
|
end
|
|
146
154
|
end
|
|
147
155
|
|
|
@@ -156,16 +164,21 @@ module Bitcoin
|
|
|
156
164
|
# get the block header info
|
|
157
165
|
# [<version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>]
|
|
158
166
|
def header_info
|
|
159
|
-
[
|
|
167
|
+
[
|
|
168
|
+
@ver, @prev_block_hash.reverse_hth, @mrkl_root.reverse_hth,
|
|
169
|
+
Time.at(@time), @bits, @nonce, @tx.size, @payload.size
|
|
170
|
+
]
|
|
160
171
|
end
|
|
161
172
|
|
|
162
173
|
# convert to raw binary format
|
|
163
174
|
def to_payload
|
|
164
|
-
|
|
165
|
-
head
|
|
166
|
-
|
|
175
|
+
@aux_pow ||= nil
|
|
176
|
+
head = [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce].pack('Va32a32VVV')
|
|
177
|
+
head << @aux_pow.to_payload if @aux_pow
|
|
178
|
+
return head if @tx.empty?
|
|
179
|
+
|
|
167
180
|
head << Protocol.pack_var_int(@tx.size)
|
|
168
|
-
@tx.each{|tx| head << tx.to_payload }
|
|
181
|
+
@tx.each { |tx| head << tx.to_payload }
|
|
169
182
|
head
|
|
170
183
|
end
|
|
171
184
|
|
|
@@ -175,11 +188,11 @@ module Bitcoin
|
|
|
175
188
|
'hash' => @hash, 'ver' => @ver,
|
|
176
189
|
'prev_block' => @prev_block_hash.reverse_hth, 'mrkl_root' => @mrkl_root.reverse_hth,
|
|
177
190
|
'time' => @time, 'bits' => @bits, 'nonce' => @nonce,
|
|
178
|
-
'n_tx' => @tx.size, 'size' => (@payload||to_payload).bytesize,
|
|
179
|
-
'tx' => @tx.map{|i| i.to_hash(options) },
|
|
180
|
-
'mrkl_tree' => Bitcoin.hash_mrkl_tree(
|
|
191
|
+
'n_tx' => @tx.size, 'size' => (@payload || to_payload).bytesize,
|
|
192
|
+
'tx' => @tx.map { |i| i.to_hash(options) },
|
|
193
|
+
'mrkl_tree' => Bitcoin.hash_mrkl_tree(@tx.map(&:hash))
|
|
181
194
|
}
|
|
182
|
-
h['aux_pow'] = @aux_pow.to_hash
|
|
195
|
+
h['aux_pow'] = @aux_pow.to_hash if @aux_pow
|
|
183
196
|
h
|
|
184
197
|
end
|
|
185
198
|
|
|
@@ -202,75 +215,90 @@ module Bitcoin
|
|
|
202
215
|
# introduced in block version 2 by BIP_0034
|
|
203
216
|
# blockchain height as seen by the block itself.
|
|
204
217
|
# do not trust this value, instead verify with chain storage.
|
|
205
|
-
def bip34_block_height(height=nil)
|
|
218
|
+
def bip34_block_height(height = nil)
|
|
206
219
|
return nil unless @ver >= 2
|
|
207
220
|
if height # generate height binary
|
|
208
|
-
buf = [height].pack(
|
|
209
|
-
[buf.bytesize, buf].pack(
|
|
221
|
+
buf = [height].pack('V').gsub(/\x00+$/, '')
|
|
222
|
+
[buf.bytesize, buf].pack('Ca*')
|
|
210
223
|
else
|
|
211
224
|
coinbase = @tx.first.inputs.first.script_sig
|
|
212
|
-
coinbase[1..coinbase[0].ord].ljust(4, "\x00").unpack(
|
|
225
|
+
coinbase[1..coinbase[0].ord].ljust(4, "\x00").unpack('V').first
|
|
213
226
|
end
|
|
214
|
-
rescue
|
|
227
|
+
rescue StandardError
|
|
215
228
|
nil
|
|
216
229
|
end
|
|
217
230
|
|
|
218
231
|
# convert to json representation as seen in the block explorer.
|
|
219
232
|
# (see also #from_json)
|
|
220
|
-
def to_json(options = {:
|
|
221
|
-
JSON.pretty_generate(
|
|
233
|
+
def to_json(options = { space: '' }, *_)
|
|
234
|
+
JSON.pretty_generate(to_hash(options), options)
|
|
222
235
|
end
|
|
223
236
|
|
|
224
237
|
# write json representation to a file
|
|
225
238
|
# (see also #to_json)
|
|
226
239
|
def to_json_file(path)
|
|
227
|
-
File.open(path, 'wb'){|f| f.print to_json; }
|
|
240
|
+
File.open(path, 'wb') { |f| f.print to_json; }
|
|
228
241
|
end
|
|
229
242
|
|
|
230
243
|
# parse ruby hash (see also #to_hash)
|
|
231
|
-
def self.from_hash(h, do_raise=true)
|
|
244
|
+
def self.from_hash(h, do_raise = true)
|
|
232
245
|
blk = new(nil)
|
|
233
|
-
blk.instance_eval
|
|
246
|
+
blk.instance_eval do
|
|
234
247
|
@ver, @time, @bits, @nonce = h.values_at('ver', 'time', 'bits', 'nonce')
|
|
235
|
-
@prev_block_hash, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
@prev_block_hash, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map(&:htb_reverse)
|
|
249
|
+
|
|
250
|
+
if do_raise && h['hash'] != recalc_block_hash
|
|
251
|
+
raise "Block hash mismatch! Claimed: #{h['hash']}, Actual: #{@hash}"
|
|
238
252
|
end
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
253
|
+
|
|
254
|
+
@aux_pow = AuxPow.from_hash(h['aux_pow']) if h['aux_pow']
|
|
255
|
+
|
|
256
|
+
h['tx'].each { |tx| @tx << Tx.from_hash(tx, do_raise) }
|
|
257
|
+
if h['tx'].any? && do_raise && !verify_mrkl_root
|
|
258
|
+
raise "Block merkle root mismatch! Block: #{h['hash']}"
|
|
243
259
|
end
|
|
244
|
-
|
|
260
|
+
end
|
|
245
261
|
blk
|
|
246
262
|
end
|
|
247
263
|
|
|
248
264
|
# convert ruby hash to raw binary
|
|
249
|
-
def self.binary_from_hash(h)
|
|
265
|
+
def self.binary_from_hash(h)
|
|
266
|
+
from_hash(h).to_payload
|
|
267
|
+
end
|
|
250
268
|
|
|
251
269
|
# parse json representation (see also #to_json)
|
|
252
|
-
def self.from_json(json_string)
|
|
270
|
+
def self.from_json(json_string)
|
|
271
|
+
from_hash(JSON.parse(json_string))
|
|
272
|
+
end
|
|
253
273
|
|
|
254
274
|
# convert json representation to raw binary
|
|
255
|
-
def self.binary_from_json(json_string)
|
|
275
|
+
def self.binary_from_json(json_string)
|
|
276
|
+
from_json(json_string).to_payload
|
|
277
|
+
end
|
|
256
278
|
|
|
257
279
|
# convert header to json representation.
|
|
258
|
-
def header_to_json(options = {:
|
|
280
|
+
def header_to_json(options = { space: '' })
|
|
259
281
|
h = to_hash
|
|
260
|
-
%w[tx mrkl_tree].each{|k| h.delete(k) }
|
|
261
|
-
JSON.pretty_generate(
|
|
282
|
+
%w[tx mrkl_tree].each { |k| h.delete(k) }
|
|
283
|
+
JSON.pretty_generate(h, options)
|
|
262
284
|
end
|
|
263
285
|
|
|
264
286
|
# block header binary output
|
|
265
287
|
def block_header
|
|
266
|
-
[
|
|
288
|
+
[
|
|
289
|
+
@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce, Protocol.pack_var_int(0)
|
|
290
|
+
].pack('Va32a32VVVa*')
|
|
267
291
|
end
|
|
268
292
|
|
|
269
293
|
# read binary block from a file
|
|
270
|
-
def self.from_file(path)
|
|
294
|
+
def self.from_file(path)
|
|
295
|
+
new(Bitcoin::Protocol.read_binary_file(path))
|
|
296
|
+
end
|
|
271
297
|
|
|
272
298
|
# read json block from a file
|
|
273
|
-
def self.from_json_file(path)
|
|
299
|
+
def self.from_json_file(path)
|
|
300
|
+
from_json(Bitcoin::Protocol.read_binary_file(path))
|
|
301
|
+
end
|
|
274
302
|
|
|
275
303
|
# get the (statistical) amount of work that was needed to generate this block.
|
|
276
304
|
def block_work
|
|
@@ -278,8 +306,6 @@ module Bitcoin
|
|
|
278
306
|
return 0 if target <= 0
|
|
279
307
|
(2**256) / (target + 1)
|
|
280
308
|
end
|
|
281
|
-
|
|
282
309
|
end
|
|
283
|
-
|
|
284
310
|
end
|
|
285
311
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Bitcoin
|
|
4
4
|
module Protocol
|
|
5
|
-
|
|
5
|
+
# https://en.bitcoin.it/wiki/Protocol_documentation#Message_types
|
|
6
6
|
class Handler
|
|
7
7
|
def on_inv_transaction(hash)
|
|
8
8
|
p ['inv transaction', hash.hth]
|
|
@@ -29,15 +29,13 @@ module Bitcoin
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def on_block(block)
|
|
32
|
-
#p ['block', block]
|
|
32
|
+
# p ['block', block]
|
|
33
33
|
puts block.to_json
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def on_error(message, payload)
|
|
37
37
|
p ['error', message, payload]
|
|
38
38
|
end
|
|
39
|
-
|
|
40
39
|
end
|
|
41
|
-
|
|
42
40
|
end
|
|
43
41
|
end
|
|
@@ -2,23 +2,66 @@
|
|
|
2
2
|
|
|
3
3
|
module Bitcoin
|
|
4
4
|
module Protocol
|
|
5
|
-
|
|
5
|
+
# https://en.bitcoin.it/wiki/Protocol_documentation#Message_types
|
|
6
6
|
class Parser
|
|
7
7
|
attr_reader :stats
|
|
8
8
|
|
|
9
|
-
def initialize(handler=nil)
|
|
9
|
+
def initialize(handler = nil)
|
|
10
10
|
@h = handler || Handler.new
|
|
11
|
-
@buf =
|
|
11
|
+
@buf = ''
|
|
12
12
|
@stats = { 'total_packets' => 0, 'total_bytes' => 0, 'total_errors' => 0 }
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
# rubocop:disable CyclomaticComplexity
|
|
16
|
+
def process_pkt(command, payload)
|
|
17
|
+
@stats['total_packets'] += 1
|
|
18
|
+
@stats['total_bytes'] += payload.bytesize
|
|
19
|
+
@stats[command] ? (@stats[command] += 1) : @stats[command] = 1
|
|
20
|
+
case command
|
|
21
|
+
when 'tx' then @h.on_tx(Tx.new(payload))
|
|
22
|
+
when 'block' then @h.on_block(Block.new(payload))
|
|
23
|
+
when 'headers' then parse_headers(payload)
|
|
24
|
+
when 'inv' then parse_inv(payload, :put)
|
|
25
|
+
when 'getdata' then parse_inv(payload, :get)
|
|
26
|
+
when 'addr' then parse_addr(payload)
|
|
27
|
+
when 'getaddr' then @h.on_getaddr if @h.respond_to?(:on_getaddr)
|
|
28
|
+
when 'verack' then parse_verack
|
|
29
|
+
when 'version' then parse_version(payload)
|
|
30
|
+
when 'alert' then parse_alert(payload)
|
|
31
|
+
when 'ping' then @h.on_ping(payload.unpack('Q')[0])
|
|
32
|
+
when 'pong' then @h.on_pong(payload.unpack('Q')[0])
|
|
33
|
+
when 'getblocks' then @h.on_getblocks(*parse_getblocks(payload)) \
|
|
34
|
+
if @h.respond_to?(:on_getblocks)
|
|
35
|
+
when 'getheaders' then @h.on_getheaders(*parse_getblocks(payload)) \
|
|
36
|
+
if @h.respond_to?(:on_getheaders)
|
|
37
|
+
when 'mempool' then handle_mempool_request(payload)
|
|
38
|
+
when 'notfound' then handle_notfound_reply(payload)
|
|
39
|
+
when 'merkleblock' then parse_mrkle_block(payload)
|
|
40
|
+
when 'reject' then handle_reject(payload)
|
|
41
|
+
else
|
|
42
|
+
parse_error :unknown_packet, [command, payload.hth]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
# rubocop:enable CyclomaticComplexity
|
|
46
|
+
|
|
47
|
+
def parse_headers(payload)
|
|
48
|
+
return unless @h.respond_to?(:on_headers)
|
|
49
|
+
buf = StringIO.new(payload)
|
|
50
|
+
count = Protocol.unpack_var_int_from_io(buf)
|
|
51
|
+
headers = Array.new(count) do
|
|
52
|
+
break if buf.eof?
|
|
53
|
+
b = Block.new
|
|
54
|
+
b.parse_data_from_io(buf, true)
|
|
55
|
+
b
|
|
56
|
+
end
|
|
57
|
+
@h.on_headers(headers)
|
|
58
|
+
end
|
|
16
59
|
|
|
17
60
|
# handles inv/getdata packets
|
|
18
|
-
def parse_inv(payload, type
|
|
61
|
+
def parse_inv(payload, type = :put)
|
|
19
62
|
count, payload = Protocol.unpack_var_int(payload)
|
|
20
63
|
payload.each_byte.each_slice(36).with_index do |i, idx|
|
|
21
|
-
hash = i[4..-1].reverse.pack(
|
|
64
|
+
hash = i[4..-1].reverse.pack('C32')
|
|
22
65
|
case i[0]
|
|
23
66
|
when 1
|
|
24
67
|
type == :put ? @h.on_inv_transaction(hash) : @h.on_get_transaction(hash)
|
|
@@ -33,70 +76,27 @@ module Bitcoin
|
|
|
33
76
|
@h.on_get_block(hash)
|
|
34
77
|
end
|
|
35
78
|
else
|
|
36
|
-
parse_error :parse_inv, i.pack(
|
|
79
|
+
parse_error :parse_inv, i.pack('C*')
|
|
37
80
|
end
|
|
38
81
|
end
|
|
39
82
|
end
|
|
40
83
|
|
|
41
84
|
def parse_addr(payload)
|
|
42
|
-
|
|
85
|
+
_, payload = Protocol.unpack_var_int(payload)
|
|
43
86
|
payload.each_byte.each_slice(30) do |i|
|
|
44
|
-
|
|
87
|
+
begin
|
|
88
|
+
@h.on_addr(Addr.new(i.pack('C*')))
|
|
89
|
+
rescue StandardError
|
|
90
|
+
parse_error(:addr, i.pack('C*'))
|
|
91
|
+
end
|
|
45
92
|
end
|
|
46
93
|
end
|
|
47
94
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
count = Protocol.unpack_var_int_from_io(buf)
|
|
52
|
-
headers = count.times.map{
|
|
53
|
-
break if buf.eof?
|
|
54
|
-
b = Block.new; b.parse_data_from_io(buf, header_only=true); b
|
|
55
|
-
}
|
|
56
|
-
@h.on_headers(headers)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def parse_mrkle_block(payload)
|
|
60
|
-
return unless @h.respond_to?(:on_mrkle_block)
|
|
61
|
-
b = Block.new
|
|
62
|
-
b.parse_data_from_io(payload, header_only= :filtered)
|
|
63
|
-
@h.on_mrkle_block(b)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def parse_getblocks(payload)
|
|
67
|
-
version, payload = payload.unpack('Va*')
|
|
68
|
-
count, payload = Protocol.unpack_var_int(payload)
|
|
69
|
-
buf, payload = payload.unpack("a#{count*32}a*")
|
|
70
|
-
hashes = buf.each_byte.each_slice(32).map{|i| hash = i.reverse.pack("C32").hth }
|
|
71
|
-
stop_hash = payload[0..32].reverse_hth
|
|
72
|
-
[version, hashes, stop_hash]
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def process_pkt(command, payload)
|
|
76
|
-
@stats['total_packets'] += 1
|
|
77
|
-
@stats['total_bytes'] += payload.bytesize
|
|
78
|
-
@stats[command] ? (@stats[command] += 1) : @stats[command] = 1
|
|
79
|
-
case command
|
|
80
|
-
when 'tx'; @h.on_tx( Tx.new(payload) )
|
|
81
|
-
when 'block'; @h.on_block( Block.new(payload) )
|
|
82
|
-
when 'headers'; parse_headers(payload)
|
|
83
|
-
when 'inv'; parse_inv(payload, :put)
|
|
84
|
-
when 'getdata'; parse_inv(payload, :get)
|
|
85
|
-
when 'addr'; parse_addr(payload)
|
|
86
|
-
when 'getaddr'; @h.on_getaddr if @h.respond_to?(:on_getaddr)
|
|
87
|
-
when 'verack'; @h.respond_to?(:on_verack) ? @h.on_verack : (@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil)
|
|
88
|
-
when 'version'; parse_version(payload)
|
|
89
|
-
when 'alert'; parse_alert(payload)
|
|
90
|
-
when 'ping'; @h.on_ping(payload.unpack("Q")[0])
|
|
91
|
-
when 'pong'; @h.on_pong(payload.unpack("Q")[0])
|
|
92
|
-
when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload)) if @h.respond_to?(:on_getblocks)
|
|
93
|
-
when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
|
|
94
|
-
when 'mempool'; handle_mempool_request(payload)
|
|
95
|
-
when 'notfound'; handle_notfound_reply(payload)
|
|
96
|
-
when 'merkleblock'; parse_mrkle_block(payload)
|
|
97
|
-
when 'reject'; handle_reject(payload)
|
|
95
|
+
def parse_verack
|
|
96
|
+
if @h.respond_to?(:on_verack)
|
|
97
|
+
@h.on_verack
|
|
98
98
|
else
|
|
99
|
-
|
|
99
|
+
@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
@@ -106,36 +106,53 @@ module Bitcoin
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def parse_alert(payload)
|
|
109
|
-
|
|
110
|
-
@h.on_alert Bitcoin::Protocol::Alert.parse(payload)
|
|
109
|
+
# nop (https://github.com/lian/bitcoin-ruby/issues/268)
|
|
111
110
|
end
|
|
112
111
|
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
def parse_getblocks(payload)
|
|
113
|
+
version, payload = payload.unpack('Va*')
|
|
114
|
+
count, payload = Protocol.unpack_var_int(payload)
|
|
115
|
+
buf, payload = payload.unpack("a#{count * 32}a*")
|
|
116
|
+
hashes = buf.each_byte.each_slice(32).map { |i| i.reverse.pack('C32').hth }
|
|
117
|
+
stop_hash = payload[0..32].reverse_hth
|
|
118
|
+
[version, hashes, stop_hash]
|
|
116
119
|
end
|
|
117
120
|
|
|
118
121
|
# https://en.bitcoin.it/wiki/BIP_0035
|
|
119
|
-
def handle_mempool_request(
|
|
120
|
-
return unless @version.fields[:version] >=
|
|
121
|
-
return unless (
|
|
122
|
+
def handle_mempool_request(*_)
|
|
123
|
+
return unless @version.fields[:version] >= 60_002 # Protocol version >= 60002
|
|
124
|
+
return unless (
|
|
125
|
+
@version.fields[:services] & Bitcoin::Protocol::Version::NODE_NETWORK
|
|
126
|
+
) == 1 # NODE_NETWORK bit set in Services
|
|
122
127
|
@h.on_mempool if @h.respond_to?(:on_mempool)
|
|
123
128
|
end
|
|
124
129
|
|
|
125
130
|
def handle_notfound_reply(payload)
|
|
126
131
|
return unless @h.respond_to?(:on_notfound)
|
|
127
|
-
|
|
132
|
+
_, payload = Protocol.unpack_var_int(payload)
|
|
128
133
|
payload.each_byte.each_slice(36) do |i|
|
|
129
|
-
hash = i[4..-1].reverse.pack(
|
|
134
|
+
hash = i[4..-1].reverse.pack('C32')
|
|
130
135
|
case i[0]
|
|
131
|
-
when 1
|
|
132
|
-
when 2
|
|
136
|
+
when 1 then @h.on_notfound(:tx, hash)
|
|
137
|
+
when 2 then @h.on_notfound(:block, hash)
|
|
133
138
|
else
|
|
134
|
-
parse_error(:notfound, [i.pack(
|
|
139
|
+
parse_error(:notfound, [i.pack('C*'), hash])
|
|
135
140
|
end
|
|
136
141
|
end
|
|
137
142
|
end
|
|
138
143
|
|
|
144
|
+
def parse_mrkle_block(payload)
|
|
145
|
+
return unless @h.respond_to?(:on_mrkle_block)
|
|
146
|
+
b = Block.new
|
|
147
|
+
b.parse_data_from_io(payload, :filtered)
|
|
148
|
+
@h.on_mrkle_block(b)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def handle_reject(payload)
|
|
152
|
+
return unless @h.respond_to?(:on_reject)
|
|
153
|
+
@h.on_reject Bitcoin::Protocol::Reject.parse(payload)
|
|
154
|
+
end
|
|
155
|
+
|
|
139
156
|
def parse(buf)
|
|
140
157
|
@buf += buf
|
|
141
158
|
while parse_buffer; end
|
|
@@ -143,52 +160,51 @@ module Bitcoin
|
|
|
143
160
|
end
|
|
144
161
|
|
|
145
162
|
def parse_buffer
|
|
146
|
-
head_magic = Bitcoin
|
|
163
|
+
head_magic = Bitcoin.network[:magic_head]
|
|
147
164
|
head_size = 24
|
|
148
165
|
return false if @buf.size < head_size
|
|
149
166
|
|
|
150
|
-
magic, cmd, length, checksum = @buf.unpack(
|
|
151
|
-
payload = @buf[head_size...head_size+length]
|
|
167
|
+
magic, cmd, length, checksum = @buf.unpack('a4A12Va4')
|
|
168
|
+
payload = @buf[head_size...head_size + length]
|
|
152
169
|
|
|
153
|
-
|
|
154
|
-
handle_stream_error(:close, "head_magic not found")
|
|
155
|
-
@buf = ''
|
|
156
|
-
else
|
|
170
|
+
if magic == head_magic
|
|
157
171
|
|
|
158
|
-
if Digest::SHA256.digest(Digest::SHA256.digest(
|
|
159
|
-
if (length <
|
|
172
|
+
if Digest::SHA256.digest(Digest::SHA256.digest(payload))[0...4] != checksum
|
|
173
|
+
if (length < 50_000) && (payload.size < length)
|
|
160
174
|
size_info = [payload.size, length].join('/')
|
|
161
175
|
handle_stream_error(:debug, "chunked packet stream (#{size_info})")
|
|
162
176
|
else
|
|
163
|
-
handle_stream_error(:close,
|
|
177
|
+
handle_stream_error(:close, 'checksum mismatch')
|
|
164
178
|
end
|
|
165
179
|
return
|
|
166
180
|
end
|
|
167
|
-
@buf = @buf[head_size+length..-1] ||
|
|
181
|
+
@buf = @buf[head_size + length..-1] || ''
|
|
168
182
|
|
|
169
183
|
process_pkt(cmd, payload)
|
|
184
|
+
else
|
|
185
|
+
handle_stream_error(:close, 'head_magic not found')
|
|
186
|
+
@buf = ''
|
|
170
187
|
end
|
|
171
188
|
|
|
172
189
|
# not empty yet? parse more.
|
|
173
|
-
|
|
190
|
+
!@buf[0].nil?
|
|
174
191
|
end
|
|
175
192
|
|
|
176
193
|
def handle_stream_error(type, msg)
|
|
194
|
+
# TODO: replace by writing a real logger/exception handler
|
|
177
195
|
case type
|
|
178
196
|
when :close
|
|
179
|
-
|
|
197
|
+
puts "closing packet stream (#{msg})"
|
|
180
198
|
else
|
|
181
|
-
|
|
199
|
+
puts [type, msg].inspect
|
|
182
200
|
end
|
|
183
201
|
end
|
|
184
202
|
|
|
185
|
-
def parse_error
|
|
203
|
+
def parse_error(*err)
|
|
186
204
|
@stats['total_errors'] += 1
|
|
187
205
|
return unless @h.respond_to?(:on_error)
|
|
188
|
-
@h.on_error
|
|
206
|
+
@h.on_error(*err)
|
|
189
207
|
end
|
|
190
|
-
|
|
191
|
-
end # Parser
|
|
192
|
-
|
|
208
|
+
end
|
|
193
209
|
end
|
|
194
210
|
end
|