bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. metadata +295 -0
@@ -0,0 +1,20 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe 'Bitcoin::Protocol::Parser (alert)' do
4
+
5
+ it 'parses alert' do
6
+ payload = "s\x01\x00\x00\x007f@O\x00\x00\x00\x00\xB3\x05CO\x00\x00\x00\x00\xF2\x03\x00\x00\xF1\x03\x00\x00\x00\x10'\x00\x00H\xEE\x00\x00\x00d\x00\x00\x00\x00FSee bitcoin.org/feb20 if you have trouble connecting after 20 February\x00G0E\x02!\x00\x83\x89\xDFE\xF0p?9\xEC\x8C\x1C\xC4,\x13\x81\x0F\xFC\xAE\x14\x99[\xB6H4\x02\x19\xE3S\xB6;S\xEB\x02 \t\xECe\xE1\xC1\xAA\xEE\xC1\xFD3LkhK\xDE+?W0`\xD5\xB7\f:Fr3&\xE4\xE8\xA4\xF1"
7
+
8
+ alert = Bitcoin::Protocol::Alert.parse(payload)
9
+ alert.values.should == [1, 1329620535, 1329792435, 1010, 1009, nil, 10000, 61000, nil, 100, nil, "See bitcoin.org/feb20 if you have trouble connecting after 20 February", nil]
10
+ alert.valid_signature?.should == true
11
+
12
+
13
+ payload = "\xAC\x01\x00\x00\x00o\xF2cO\x00\x00\x00\x00k\"EQ\x00\x00\x00\x00\xF4\x03\x00\x00\xF2\x03\x00\x00\x00`\xEA\x00\x00`\xEA\x00\x00\x03\x11/Satoshi:0.6.0.3/\x0F/Satoshi:0.6.0/\x12/bitcoin-qt:0.6.0/\x88\x13\x00\x00\x00JURGENT: security fix for Bitcoin-Qt on Windows: http://bitcoin.org/critfix\x00H0F\x02!\x00\xB7\xB1o\x86\x0F\x9EZ\x87bt\xAE\xB7$u\xD2\xDE\xC3\x86j\xA7\xAF\x82\xAD\x97\\\x83Qd\xA9\x97\xA7\x16\x02!\x00\x86\xB4\x18)\xCB\x84\xBE\xD2\x86\x10\x82G\xBE\xBF;\xE9{\xD9\xB3\x1E\xB4/g\xB4\xD33\xCE\x8B\x1D}\xF8^"
14
+
15
+ alert = Bitcoin::Protocol::Alert.parse(payload)
16
+ alert.values.should == [1, 1331950191, 1363485291, 1012, 1010, nil, 60000, 60000, ["/Satoshi:0.6.0.3/", "/Satoshi:0.6.0/", "/bitcoin-qt:0.6.0/"], 5000, nil, "URGENT: security fix for Bitcoin-Qt on Windows: http://bitcoin.org/critfix", nil]
17
+ alert.valid_signature?.should == true
18
+ end
19
+
20
+ end
@@ -0,0 +1,101 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe 'Bitcoin::Protocol::Block' do
4
+
5
+ @blocks = {
6
+ # block 0: 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048
7
+ '0' => fixtures_file('rawblock-0.bin'),
8
+ # block 1: 000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd
9
+ '1' => fixtures_file('rawblock-1.bin'),
10
+ # block 9: 000000008d9dc510f23c2657fc4f67bea30078cc05a90eb89e84cc475c080805
11
+ '9' => fixtures_file('rawblock-9.bin'),
12
+ # block 170: 00000000d1145790a8694403d4063f323d499e655c83426834d4ce2f8dd4a2ee
13
+ '170' => fixtures_file('rawblock-170.bin'),
14
+ # block 131025: 00000000000007d938dbdd433c5ae12a782de74abf7f566518bc2b2d0a1df145
15
+ '131025' => fixtures_file('rawblock-131025.bin'),
16
+ # block 26478: 000000000214a3f06ee99a033a7f2252762d6a18d27c3cd8c8fe2278190da9f3
17
+ 'testnet-26478' => fixtures_file('rawblock-testnet-26478.bin'),
18
+ }
19
+
20
+
21
+ it '#new' do
22
+ proc{
23
+ Bitcoin::Protocol::Block.new( nil )
24
+ @block = Bitcoin::Protocol::Block.new( @blocks['0'] )
25
+ }.should.not.raise Exception
26
+
27
+ proc{
28
+ Bitcoin::Protocol::Block.new( @blocks['0'][0..20] )
29
+ }.should.raise Exception
30
+
31
+ block = Bitcoin::Protocol::Block.new(nil)
32
+ block.parse_data(@blocks['0']).should == true
33
+ block.header_info[7].should == 215
34
+ block.to_payload.should == @blocks['0']
35
+
36
+ block = Bitcoin::Protocol::Block.new(nil)
37
+ block.parse_data(@blocks['0'] + "AAAA").should == "AAAA"
38
+ block.header_info[7].should == 215
39
+ block.to_payload.should == @blocks['0']
40
+ end
41
+
42
+ it '#hash' do
43
+ @block.hash.should == "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"
44
+ end
45
+
46
+ it '#tx' do
47
+ @block.tx.size.should == 1
48
+ @block.tx[0].is_a?(Bitcoin::Protocol::Tx).should == true
49
+ @block.tx[0].hash.should == "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"
50
+ end
51
+
52
+ it '#to_hash' do
53
+ @block.to_hash.keys.should == ["hash", "ver", "prev_block", "mrkl_root", "time", "bits", "nonce", "n_tx", "size", "tx", "mrkl_tree"]
54
+ end
55
+
56
+ it '#to_json' do
57
+ @block.to_json.should == fixtures_file('rawblock-0.json')
58
+ Bitcoin::Protocol::Block.new( @blocks['1'] ).to_json.should == fixtures_file('rawblock-1.json')
59
+ Bitcoin::Protocol::Block.new( @blocks['131025'] ).to_json.should == fixtures_file('rawblock-131025.json')
60
+ Bitcoin::Protocol::Block.new( @blocks['testnet-26478'] ).to_json.should == fixtures_file('rawblock-testnet-26478.json')
61
+ Bitcoin::P::Block.from_json(@block.to_json).tx[0].in[0].sequence.should == "\xff\xff\xff\xff"
62
+ end
63
+
64
+ it '#to_payload' do
65
+ @block.to_payload.should == @block.payload
66
+ Bitcoin::Protocol::Block.new( @block.to_payload ).to_payload.should == @block.payload
67
+ Bitcoin::Protocol::Block.new( @blocks['1'] ).to_payload.should == @blocks['1']
68
+ Bitcoin::Protocol::Block.new( @blocks['131025'] ).to_payload.should == @blocks['131025']
69
+ Bitcoin::Protocol::Block.new( @blocks['testnet-26478'] ).to_payload.should == @blocks['testnet-26478']
70
+ end
71
+
72
+ it '#from_json' do
73
+ block = Bitcoin::Protocol::Block.from_json(fixtures_file('rawblock-0.json'))
74
+ block.to_payload.should == @blocks['0']
75
+ block.tx[0].in[0].sequence.should == "\xff\xff\xff\xff"
76
+ Bitcoin::Protocol::Block.from_json(fixtures_file('rawblock-1.json')).to_payload.should == @blocks['1']
77
+
78
+ Bitcoin::Protocol::Block.from_json(fixtures_file('rawblock-131025.json'))
79
+ .to_payload.should == @blocks['131025']
80
+
81
+ Bitcoin::Protocol::Block.from_json(fixtures_file('rawblock-testnet-26478.json'))
82
+ .to_payload.should == @blocks['testnet-26478']
83
+ end
84
+
85
+ it '#header_to_json' do
86
+ @block.header_to_json.should == (<<-JSON).chomp
87
+ {
88
+ "hash":"00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048",
89
+ "ver":1,
90
+ "prev_block":"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
91
+ "mrkl_root":"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
92
+ "time":1231469665,
93
+ "bits":486604799,
94
+ "nonce":2573394689,
95
+ "n_tx":1,
96
+ "size":215
97
+ }
98
+ JSON
99
+ end
100
+
101
+ end
@@ -0,0 +1,124 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe 'Bitcoin::Protocol::Parser - Inventory Vectors' do
4
+
5
+ class Test_Handler < Bitcoin::Protocol::Handler
6
+ attr_reader :tx_inv
7
+ attr_reader :block_inv
8
+ def on_inv_transaction(hash); (@tx_inv ||= []) << hth(hash); end
9
+ def on_get_transaction(hash); (@tx_inv ||= []) << hth(hash); end
10
+ def on_inv_block(hash); (@block_inv ||= []) << hth(hash); end
11
+ def on_get_block(hash); (@block_inv ||= []) << hth(hash); end
12
+ end
13
+
14
+
15
+ it 'parses inv (transaction)' do
16
+ pkt = [
17
+ "f9 be b4 d9 69 6e 76 00 00 00 00 00 00 00 00 00 49 00 00 00 11 ea 1c 91 02 01 00 00 00 e0 41 c2 38 f7 32 1a 68 0a 34 06 bf fd 72 12 e3 d1 2c b5 12 2a 8c 0b 52 76 de 82 30 b1 00 7a 42 01 00 00 00 33 00 09 71 a9 70 7b 6c 6d 6e 77 aa 2e ac 43 f3 e5 67 84 cb 61 b2 35 fb 8d fe e0 86 8b 40 7c f3"
18
+ .split(" ").join].pack("H*")
19
+
20
+ parser = Bitcoin::Protocol::Parser.new( handler = Test_Handler.new )
21
+ parser.parse(pkt + "AAAA").should == "AAAA"
22
+
23
+ handler.tx_inv.should == [
24
+ "427a00b13082de76520b8c2a12b52cd1e31272fdbf06340a681a32f738c241e0",
25
+ "f37c408b86e0fe8dfb35b261cb8467e5f343ac2eaa776e6d6c7b70a971090033"
26
+ ]
27
+ end
28
+
29
+
30
+ it 'parses getdata (transaction)' do
31
+ # 000013C4 f9 be b4 d9 67 65 74 64 61 74 61 00 00 00 00 00 ....getd ata.....
32
+ # 000013D4 25 00 00 00 c5 38 cd bb 01 01 00 00 00 b4 02 2d %....8.. .......-
33
+ # 000013E4 9f e5 28 fb 90 70 50 01 16 4b 0c c3 a8 f5 a1 9c ..(..pP. .K......
34
+ # 000013F4 b8 ed 02 bf d4 fc 2c b6 25 66 d1 9d 6e ......,. %f..n
35
+
36
+ pkt = [
37
+ "f9 be b4 d9 67 65 74 64 61 74 61 00 00 00 00 00 25 00 00 00 c5 38 cd bb 01 01 00 00 00 b4 02 2d 9f e5 28 fb 90 70 50 01 16 4b 0c c3 a8 f5 a1 9c b8 ed 02 bf d4 fc 2c b6 25 66 d1 9d 6e"
38
+ .split(" ").join].pack("H*")
39
+
40
+ parser = Bitcoin::Protocol::Parser.new( handler = Test_Handler.new )
41
+ parser.parse(pkt + "AAAA").should == "AAAA"
42
+
43
+ handler.tx_inv.should == [
44
+ "6e9dd16625b62cfcd4bf02edb89ca1f5a8c30c4b1601507090fb28e59f2d02b4"
45
+ ]
46
+ end
47
+
48
+
49
+ it 'parses inv (block)' do
50
+ # 00003C27 f9 be b4 d9 69 6e 76 00 00 00 00 00 00 00 00 00 ....inv. ........
51
+ # 00003C37 25 00 00 00 ae 13 a1 04 01 02 00 00 00 9d b9 c4 %....... ........
52
+ # 00003C47 e2 7c 5b cd 3f 62 e1 af 18 fd 9d 81 6d 6c 6b c7 .|[.?b.. ....mlk.
53
+ # 00003C57 b2 26 0a 39 c5 49 39 00 00 00 00 00 00 .&.9.I9. .....
54
+ pkt = [
55
+ "f9 be b4 d9 69 6e 76 00 00 00 00 00 00 00 00 00 25 00 00 00 ae 13 a1 04 01 02 00 00 00 9d b9 c4 e2 7c 5b cd 3f 62 e1 af 18 fd 9d 81 6d 6c 6b c7 b2 26 0a 39 c5 49 39 00 00 00 00 00 00"
56
+ .split(" ").join].pack("H*")
57
+
58
+ parser = Bitcoin::Protocol::Parser.new( handler = Test_Handler.new )
59
+ parser.parse(pkt + "AAAA").should == "AAAA"
60
+ handler.block_inv.should == [
61
+ "0000000000003949c5390a26b2c76b6c6d819dfd18afe1623fcd5b7ce2c4b99d"
62
+ ]
63
+ end
64
+
65
+
66
+ it 'parses getdata (block)' do
67
+ # 000039E3 f9 be b4 d9 67 65 74 64 61 74 61 00 00 00 00 00 ....getd ata.....
68
+ # 000039F3 25 00 00 00 ae 13 a1 04 01 02 00 00 00 9d b9 c4 %....... ........
69
+ # 00003A03 e2 7c 5b cd 3f 62 e1 af 18 fd 9d 81 6d 6c 6b c7 .|[.?b.. ....mlk.
70
+ # 00003A13 b2 26 0a 39 c5 49 39 00 00 00 00 00 00 .&.9.I9. .....
71
+ pkt = [
72
+ "f9 be b4 d9 67 65 74 64 61 74 61 00 00 00 00 00 25 00 00 00 ae 13 a1 04 01 02 00 00 00 9d b9 c4 e2 7c 5b cd 3f 62 e1 af 18 fd 9d 81 6d 6c 6b c7 b2 26 0a 39 c5 49 39 00 00 00 00 00 00"
73
+ .split(" ").join].pack("H*")
74
+
75
+ parser = Bitcoin::Protocol::Parser.new( handler = Test_Handler.new )
76
+ parser.parse(pkt + "AAAA").should == "AAAA"
77
+
78
+ handler.block_inv.should == [
79
+ "0000000000003949c5390a26b2c76b6c6d819dfd18afe1623fcd5b7ce2c4b99d"
80
+ ]
81
+ end
82
+
83
+
84
+ it 'parses getblocks' do
85
+ class Test_Handler < Bitcoin::Protocol::Handler
86
+ attr_reader :pkt
87
+ def on_getblocks(version, hashes, stop_hash); @pkt = { :version => version, :locator_hashes => hashes, :stop_hash => stop_hash }; end
88
+ end
89
+
90
+ locator_hashes = ["00000000068866924696f410b778911316f92035e9b69b62afa03573974fd750", "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"]
91
+ stop_hash = "0000000000000000000000000000000000000000000000000000000000000000"
92
+ pkt = Bitcoin::Protocol.getblocks_pkt( locator_hashes )
93
+
94
+ parser = Bitcoin::Protocol::Parser.new( handler = Test_Handler.new )
95
+ parser.parse(pkt + "AAAA").should == "AAAA"
96
+
97
+ handler.pkt.should == {
98
+ :version => Bitcoin.network[:magic_head],
99
+ :locator_hashes => locator_hashes,
100
+ :stop_hash => "0000000000000000000000000000000000000000000000000000000000000000"
101
+ }
102
+ end
103
+
104
+ it 'parses getheaders' do
105
+ class Test_Handler < Bitcoin::Protocol::Handler
106
+ attr_reader :pkt
107
+ def on_getheaders(version, hashes, stop_hash); @pkt = { :version => version, :locator_hashes => hashes, :stop_hash => stop_hash }; end
108
+ end
109
+
110
+ locator_hashes = ["00000000068866924696f410b778911316f92035e9b69b62afa03573974fd750", "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"]
111
+ stop_hash = "0000000000000000000000000000000000000000000000000000000000007e57"
112
+ pkt = Bitcoin::Protocol.getblocks_pkt( locator_hashes, stop_hash )
113
+
114
+ parser = Bitcoin::Protocol::Parser.new( handler = Test_Handler.new )
115
+ parser.parse(pkt + "AAAA").should == "AAAA"
116
+
117
+ handler.pkt.should == {
118
+ :version => Bitcoin.network[:magic_head],
119
+ :locator_hashes => locator_hashes,
120
+ :stop_hash => "0000000000000000000000000000000000000000000000000000000000007e57"
121
+ }
122
+ end
123
+
124
+ end
@@ -0,0 +1,49 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe 'Bitcoin::Protocol::Parser (ping/pong)' do
4
+
5
+ class Ping_Handler < Bitcoin::Protocol::Handler
6
+ attr_reader :nonce
7
+ def on_ping(nonce)
8
+ @nonce = nonce
9
+ end
10
+ def on_pong(nonce)
11
+ @nonce = nonce
12
+ end
13
+ end
14
+
15
+ before do
16
+ @parser = Bitcoin::Protocol::Parser.new( @handler = Ping_Handler.new )
17
+ end
18
+
19
+ it 'parses ping without nonce' do
20
+ @parser.parse(Bitcoin::Protocol.pkt("ping", "") + "AAAA").should == "AAAA"
21
+ @handler.nonce.should == nil
22
+ end
23
+
24
+ it 'parses ping with nonce' do
25
+ @parser.parse(Bitcoin::Protocol.pkt("ping", [12345].pack("Q")) + "AAAA").should == "AAAA"
26
+ @handler.nonce.should == 12345
27
+ end
28
+
29
+ it 'builds ping without nonce' do
30
+ @parser.parse(Bitcoin::Protocol::ping_pkt)
31
+ @handler.nonce.should != nil
32
+ end
33
+
34
+ it 'builds ping with nonce' do
35
+ @parser.parse(Bitcoin::Protocol::ping_pkt(12345))
36
+ @handler.nonce.should == 12345
37
+ end
38
+
39
+ it 'parses pong' do
40
+ @parser.parse(Bitcoin::Protocol.pkt("pong", [12345].pack("Q")) + "AAAA").should == "AAAA"
41
+ @handler.nonce.should == 12345
42
+ end
43
+
44
+ it 'builds pong' do
45
+ @parser.parse(Bitcoin::Protocol::pong_pkt(12345))
46
+ @handler.nonce.should == 12345
47
+ end
48
+
49
+ end
@@ -0,0 +1,226 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ include Bitcoin::Protocol
4
+
5
+ describe 'Tx' do
6
+
7
+ @payload = [
8
+ fixtures_file('rawtx-01.bin'),
9
+ fixtures_file('rawtx-02.bin'),
10
+ fixtures_file('rawtx-03.bin'),
11
+ ]
12
+
13
+ @json = [
14
+ fixtures_file('rawtx-01.json'),
15
+ fixtures_file('rawtx-02.json'),
16
+ fixtures_file('rawtx-03.json')
17
+ ]
18
+
19
+
20
+ it '#new' do
21
+ proc{
22
+ Tx.new( nil )
23
+ @payload.each{|payload| Tx.new( payload ) }
24
+ }.should.not.raise Exception
25
+
26
+ proc{
27
+ Tx.new( @payload[0][0..20] )
28
+ }.should.raise Exception
29
+ end
30
+
31
+ it '#parse_data' do
32
+ tx = Tx.new( nil )
33
+
34
+ tx.hash.should == nil
35
+ tx.parse_data( @payload[0] ).should == true
36
+ tx.hash.size.should == 64
37
+
38
+ tx = Tx.new( nil )
39
+ tx.parse_data( @payload[0] + "AAAA" ).should == "AAAA"
40
+ tx.hash.size.should == 64
41
+ end
42
+
43
+ it '#hash' do
44
+ tx = Tx.new( @payload[0] )
45
+ tx.hash.size.should == 64
46
+ tx.hash.should == "6e9dd16625b62cfcd4bf02edb89ca1f5a8c30c4b1601507090fb28e59f2d02b4"
47
+ tx.binary_hash.should == "\xB4\x02-\x9F\xE5(\xFB\x90pP\x01\x16K\f\xC3\xA8\xF5\xA1\x9C\xB8\xED\x02\xBF\xD4\xFC,\xB6%f\xD1\x9Dn"
48
+ end
49
+
50
+ it '#to_payload' do
51
+ tx = Tx.new( @payload[0] )
52
+ tx.to_payload.size.should == @payload[0].size
53
+ tx.to_payload.should == @payload[0]
54
+ end
55
+
56
+ it '#to_hash' do
57
+ tx = Tx.new( @payload[0] )
58
+ tx.to_hash.keys.should == ["hash", "ver", "vin_sz", "vout_sz", "lock_time", "size", "in", "out"]
59
+ end
60
+
61
+ it 'Tx.from_hash' do
62
+ orig_tx = Tx.new( @payload[0] )
63
+ tx = Tx.from_hash( orig_tx.to_hash )
64
+ tx.to_payload.size.should == @payload[0].size
65
+ tx.to_payload.should == @payload[0]
66
+ tx.to_hash.should == orig_tx.to_hash
67
+ Tx.binary_from_hash( orig_tx.to_hash ).should == @payload[0]
68
+ end
69
+
70
+ it 'Tx.binary_from_hash' do
71
+ orig_tx = Tx.new( @payload[0] )
72
+ Tx.binary_from_hash( orig_tx.to_hash ).size.should == @payload[0].size
73
+ Tx.binary_from_hash( orig_tx.to_hash ).should == @payload[0]
74
+ end
75
+
76
+ it '#to_json' do
77
+ tx = Tx.new( @payload[0] )
78
+ tx.to_json.should == @json[0]
79
+
80
+ tx = Tx.new( @payload[1] )
81
+ tx.to_json.should == @json[1]
82
+
83
+ tx = Tx.new( @payload[2] )
84
+ tx.to_json.should == @json[2]
85
+
86
+ tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') )
87
+ tx.to_json.should == fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json')
88
+ end
89
+
90
+ it 'Tx.from_json' do
91
+ tx = Tx.from_json( json_string = fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json') )
92
+ tx.to_json.should == json_string
93
+
94
+ tx = Tx.from_json( json_string = fixtures_file('rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json') )
95
+ tx.to_json.should == json_string
96
+
97
+ tx = Tx.from_json( json_string = fixtures_file('rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json') )
98
+ tx.to_payload.should == fixtures_file('rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin')
99
+ tx.to_json.should == json_string
100
+
101
+ tx = Tx.from_json( fixtures_file('rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json') )
102
+ Tx.new( tx.to_payload ).to_json.should == tx.to_json
103
+ tx.hash.should == 'ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5'
104
+ end
105
+
106
+ it 'Tx.binary_from_json' do
107
+ Tx.binary_from_json( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json') ).should ==
108
+ fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin')
109
+ end
110
+
111
+ it '#verify_input_signature' do
112
+ # transaction-2 of block-170
113
+ tx = Tx.new( fixtures_file('rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin') )
114
+ tx.hash.should == "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"
115
+
116
+ # transaction-1 (coinbase) of block-9
117
+ outpoint_tx = Tx.new( fixtures_file('rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin') )
118
+ outpoint_tx.hash.should == "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"
119
+
120
+ tx.verify_input_signature(0, outpoint_tx).should == true
121
+
122
+
123
+ tx = Tx.from_json( fixtures_file('rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json') )
124
+ tx.hash.should == 'c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73'
125
+
126
+ outpoint_tx = Tx.from_json( fixtures_file('rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json') )
127
+ outpoint_tx.hash.should == '406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602'
128
+
129
+ tx.verify_input_signature(0, outpoint_tx).should == true
130
+ end
131
+
132
+ it '#sign_input_signature' do
133
+ prev_tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') )
134
+ prev_tx.hash.should == "2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a"
135
+
136
+ key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
137
+ pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
138
+ new_tx = Tx.new(nil)
139
+ new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
140
+ new_tx.add_out( TxOut.value_to_address(1000000, "1BVJWLTCtjA8wRivvrCiwjNdL6KjdMUCTZ") )
141
+ signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
142
+ sig = Bitcoin.sign_data(key, signature_hash)
143
+ new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
144
+
145
+ new_tx = Tx.new( new_tx.to_payload )
146
+ new_tx.hash.should != nil
147
+ new_tx.verify_input_signature(0, prev_tx).should == true
148
+
149
+
150
+
151
+ prev_tx = Tx.new( fixtures_file('rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin') )
152
+ prev_tx.hash.should == "14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984"
153
+
154
+ key = Bitcoin.open_key("115ceda6c1e02d41ce65c35a30e82fb325fe3f815898a09e1a5d28bb1cc92c6e",
155
+ pubkey="0409d103127d26ce93ee41f1b9b1ed4c1c243acf48e31eb5c4d88ad0342ccc010a1a8d838846cf7337f2b44bc73986c0a3cb0568fa93d068b2c8296ce8d47b1545")
156
+ new_tx = Tx.new(nil)
157
+ new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
158
+ pk_script = Bitcoin::Script.to_address_script("1FEYAh1x5jeKQMPPuv3bKnKvbgVAqXvqjW")
159
+ new_tx.add_out( TxOut.new(1000000, pk_script) )
160
+ signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
161
+ sig = Bitcoin.sign_data(key, signature_hash)
162
+ new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
163
+
164
+ new_tx = Tx.new( new_tx.to_payload )
165
+ new_tx.hash.should != nil
166
+ new_tx.verify_input_signature(0, prev_tx).should == true
167
+
168
+
169
+
170
+ prev_tx = Tx.new( fixtures_file('rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin') )
171
+ prev_tx.hash.should == "b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d"
172
+
173
+ key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
174
+ pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
175
+ new_tx = Tx.new(nil)
176
+ new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
177
+ new_tx.add_out( TxOut.value_to_address(1000000, "14yz7fob6Q16hZu4nXfmv1kRJpSYaFtet5") )
178
+ signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
179
+ sig = Bitcoin.sign_data(key, signature_hash)
180
+ new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
181
+
182
+ new_tx = Tx.new( new_tx.to_payload )
183
+ new_tx.hash.should != nil
184
+ new_tx.verify_input_signature(0, prev_tx).should == true
185
+
186
+ #File.open("rawtx-#{new_tx.hash}.bin",'wb'){|f| f.print new_tx.to_payload }
187
+ prev_tx = Tx.new( fixtures_file('rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin') )
188
+ prev_tx.hash.should == "52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2"
189
+ #File.open("rawtx-#{prev_tx.hash}.json",'wb'){|f| f.print prev_tx.to_json }
190
+ end
191
+
192
+
193
+ describe "Tx - BIP Scripts" do
194
+
195
+ it "should do OP_CHECKHASHVERIFY (BIP_0017)" do # https://en.bitcoin.it/wiki/BIP_0017
196
+ # scriptSig: [signatures...] OP_CODESEPARATOR 1 [pubkey1] [pubkey2] 2 OP_CHECKMULTISIG
197
+ # scriptPubKey: [20-byte-hash of {1 [pubkey1] [pubkey2] 2 OP_CHECKMULTISIG} ] OP_CHECKHASHVERIFY OP_DROP
198
+
199
+ tx = Tx.from_json(fixtures_file('bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json'))
200
+ tx.hash.should == "bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba"
201
+
202
+ prev_tx1 = Tx.from_json(fixtures_file('477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json'))
203
+ prev_tx1.hash.should == "477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590"
204
+
205
+ prev_tx2 = Tx.from_json(fixtures_file('0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json'))
206
+ prev_tx2.hash.should == "0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd"
207
+
208
+ tx.verify_input_signature(0, prev_tx1).should == true
209
+ tx.verify_input_signature(1, prev_tx2).should == true
210
+ end
211
+
212
+ it "should do OP_CHECKMULTISIG" do
213
+ # checkmultisig without checkhashverify
214
+ tx = Tx.from_json(fixtures_file('23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json'))
215
+ prev_tx = Tx.from_json(fixtures_file('60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json'))
216
+ tx.verify_input_signature(0, prev_tx).should == true
217
+
218
+ # p2sh + multisig transaction from mainnet
219
+ tx = Tx.from_json( fixtures_file('rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json') )
220
+ prev_tx = Tx.from_json( fixtures_file("rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json") )
221
+ tx.verify_input_signature(0, prev_tx).should == true
222
+ end
223
+
224
+ end
225
+
226
+ end