bitcoin-ruby 0.0.1

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.
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,266 @@
1
+ module Bitcoin
2
+
3
+ # Optional DSL to help create blocks and transactions.
4
+ #
5
+ # see also BlockBuilder, TxBuilder, TxInBuilder, TxOutBuilder, ScriptBuilder
6
+ module Builder
7
+
8
+ # build a Bitcoin::Protocol::Block matching the given +target+.
9
+ # see BlockBuilder for details.
10
+ def blk(target = "00".ljust(32, 'f'))
11
+ c = BlockBuilder.new
12
+ yield c
13
+ c.block(target)
14
+ end
15
+
16
+ # build a Bitcoin::Protocol::Tx.
17
+ # see TxBuilder for details.
18
+ def tx
19
+ c = TxBuilder.new
20
+ yield c
21
+ c.tx
22
+ end
23
+
24
+ # build a Bitcoin::Script.
25
+ # see ScriptBuilder for details.
26
+ def script
27
+ c = ScriptBuilder.new
28
+ yield c
29
+ c.script
30
+ end
31
+
32
+ # DSL to create a Bitcoin::Protocol::Block used by Builder#blk.
33
+ # block = blk("00".ljust(32, 'f')) do |b|
34
+ # b.prev_block "\x00"*32
35
+ # b.tx do |t|
36
+ # t.input {|i| i.coinbase }
37
+ # t.output do |o|
38
+ # o.value 5000000000;
39
+ # o.script do |s|
40
+ # s.type :address
41
+ # s.recipient Bitcoin::Key.generate.addr
42
+ # end
43
+ # end
44
+ # end
45
+ # end
46
+ class BlockBuilder
47
+
48
+ def initialize
49
+ @block = Bitcoin::P::Block.new(nil)
50
+ end
51
+
52
+ # specify block version. this is usually not necessary. defaults to 1.
53
+ def version v
54
+ @version = v
55
+ end
56
+
57
+ # set the hash of the previous block.
58
+ def prev_block hash
59
+ @prev_block = hash
60
+ end
61
+
62
+ # add transactions to the block (see TxBuilder).
63
+ def tx
64
+ c = TxBuilder.new
65
+ yield c
66
+ @block.tx << c.tx
67
+ end
68
+
69
+ # create the block according to values specified via DSL.
70
+ def block target
71
+ @block.ver = @version || 1
72
+ @block.prev_block = [@prev_block].pack("H*").reverse
73
+ @block.mrkl_root = @mrkl_root
74
+ @block.time = Time.now.to_i
75
+ @block.nonce = 0
76
+ @block.mrkl_root = [Bitcoin.hash_mrkl_tree(@block.tx.map {|t|
77
+ t.hash }).last].pack("H*")
78
+ find_hash(target)
79
+ Bitcoin::P::Block.new(@block.to_payload)
80
+ end
81
+
82
+ private
83
+
84
+ # increment nonce/time to find a block hash matching the +target+.
85
+ def find_hash target
86
+ @block.bits = Bitcoin.encode_compact_bits(target)
87
+ t = Time.now
88
+ @block.recalc_block_hash
89
+ until @block.hash < target
90
+ @block.nonce += 1
91
+ @block.recalc_block_hash
92
+ if @block.nonce == 100000
93
+ if t
94
+ tt = 1 / ((Time.now - t) / 100000) / 1000
95
+ print "\r%.2f khash/s" % tt
96
+ end
97
+ t = Time.now
98
+ @block.time = Time.now.to_i
99
+ @block.nonce = 0
100
+ $stdout.flush
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ # DSL to create Bitcoin::Protocol::Tx used by Builder#tx.
108
+ # tx = tx do |t|
109
+ # t.input do |i|
110
+ # i.prev_out prev_tx # previous transaction
111
+ # i.prev_out_index 0 # index of previous output
112
+ # i.signature_key key # Bitcoin::Key used to sign the input
113
+ # end
114
+ # t.output do |o|
115
+ # o.value 12345 # 0.00012345 BTC
116
+ # o.script {|s| s.type :address; s.recipient key.addr }
117
+ # end
118
+ # end
119
+ class TxBuilder
120
+
121
+ def initialize
122
+ @tx = Bitcoin::P::Tx.new(nil)
123
+ @tx.ver, @tx.lock_time = 1, 0
124
+ @ins, @outs = [], []
125
+ end
126
+
127
+ # specify tx version. this is usually not necessary. defaults to 1.
128
+ def version n
129
+ @tx.ver = n
130
+ end
131
+
132
+ # specify tx lock_time. this is usually not necessary. defaults to 0.
133
+ def lock_time n
134
+ @tx.lock_time = n
135
+ end
136
+
137
+ # add an input to the transaction (see TxInBuilder).
138
+ def input
139
+ c = TxInBuilder.new
140
+ yield c
141
+ @ins << c
142
+ end
143
+
144
+ # add an output to the transaction (see TxOutBuilder).
145
+ def output
146
+ c = TxOutBuilder.new
147
+ yield c
148
+ @outs << c
149
+ end
150
+
151
+ # create the transaction according to values specified via DSL and sign inputs.
152
+ def tx
153
+ @ins.each {|i| @tx.add_in(i.txin) }
154
+ @outs.each {|o| @tx.add_out(o.txout) }
155
+ @ins.each_with_index do |inc, i|
156
+ if @tx.in[i].coinbase?
157
+ script_sig = [inc.coinbase_data].pack("H*")
158
+ @tx.in[i].script_sig_length = script_sig.bytesize
159
+ @tx.in[i].script_sig = script_sig
160
+ next
161
+ end
162
+ prev_tx = inc.instance_variable_get(:@prev_out)
163
+ sig_hash = @tx.signature_hash_for_input(i, prev_tx)
164
+ sig = inc.key.sign(sig_hash)
165
+ script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
166
+ @tx.in[i].script_sig_length = script_sig.bytesize
167
+ @tx.in[i].script_sig = script_sig
168
+ raise "Signature error" unless @tx.verify_input_signature(i, prev_tx)
169
+ end
170
+ Bitcoin::P::Tx.new(@tx.to_payload)
171
+ end
172
+ end
173
+
174
+ # create a Bitcoin::Protocol::TxIn used by TxBuilder#input.
175
+ #
176
+ # inputs can be either 'coinbase', in which case they only need to specify #coinbase,
177
+ # or they have to define a #prev_out, #prev_out_index and #signature key.
178
+ class TxInBuilder
179
+ attr_reader :key, :coinbase_data
180
+
181
+ def initialize
182
+ @txin = Bitcoin::P::TxIn.new
183
+ end
184
+
185
+ # previous transaction that contains the output we want to use.
186
+ def prev_out tx
187
+ @prev_out = tx
188
+ end
189
+
190
+ # index of the output in the #prev_out transaction.
191
+ def prev_out_index i
192
+ @prev_out_index = i
193
+ end
194
+
195
+ # specify sequence. this is usually not needed.
196
+ def sequence s
197
+ @sequence = s
198
+ end
199
+
200
+ # Bitcoin::Key used to sign the signature_hash for the input.
201
+ # see Bitcoin::Script.signature_hash_for_input and Bitcoin::Key.sign.
202
+ def signature_key key
203
+ @key = key
204
+ end
205
+
206
+ # specify that this is a coinbase input. optionally set +data+.
207
+ def coinbase data = nil
208
+ @coinbase_data = data || OpenSSL::Random.random_bytes(32)
209
+ @prev_out = nil
210
+ @prev_out_index = 4294967295
211
+ end
212
+
213
+ # create the txin according to values specified via DSL
214
+ def txin
215
+ @txin.prev_out = (@prev_out ? @prev_out.binary_hash : "\x00"*32)
216
+ @txin.prev_out_index = @prev_out_index
217
+ @txin.sequence = @sequence || "\xff\xff\xff\xff"
218
+ @txin
219
+ end
220
+ end
221
+
222
+ # create a Bitcoin::Script used by TxOutBuilder#script.
223
+ class ScriptBuilder
224
+ attr_reader :script
225
+
226
+ def initialize
227
+ @type = nil
228
+ @script = nil
229
+ end
230
+
231
+ # script type (:pubkey, :address/hash160, :multisig).
232
+ def type type
233
+ @type = type.to_sym
234
+ end
235
+
236
+ # recipient(s) of the script.
237
+ # depending on the #type, either an address, hash160 pubkey, etc.
238
+ def recipient *data
239
+ @script = Bitcoin::Script.send("to_#{@type}_script", *data)
240
+ end
241
+ end
242
+
243
+ # create a Bitcoin::Protocol::TxOut used by TxBuilder#output.
244
+ class TxOutBuilder
245
+ attr_reader :txout
246
+
247
+ def initialize
248
+ @txout = Bitcoin::P::TxOut.new
249
+ end
250
+
251
+ # set output value (in base units / "satoshis")
252
+ def value value
253
+ @txout.value = value
254
+ end
255
+
256
+ # add a script to the output (see ScriptBuilder).
257
+ def script &block
258
+ c = ScriptBuilder.new
259
+ yield c
260
+ @txout.pk_script = c.script
261
+ end
262
+
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,56 @@
1
+ require 'yaml'
2
+ module Bitcoin
3
+
4
+ # Load config files, merge options, etc.
5
+ #
6
+ # Multiple config files are loaded in order, and their settings merged into an
7
+ # existing +options+ hash.
8
+ #
9
+ # Each config file defines one or more +categories+ which hold the actual settings.
10
+ # Which categories are loaded, and in what order, is specified when you load
11
+ # the config (ie. the order in the file doesn't matter).
12
+ # The default category "all" is always used, and is always the first
13
+ # (gets overridden by all others)
14
+ module Config
15
+
16
+ CONFIG_PATHS = "./bitcoin-ruby.yml:~/.bitcoin-ruby.yml:/etc/bitcoin-ruby.yml"
17
+
18
+ # Load +categories+ from all files at +paths+ into given +options+ hash.
19
+ def self.load(options, categories = [], paths = CONFIG_PATHS)
20
+ paths.split(":").reverse.each do |path|
21
+ path.sub!("~", ENV["HOME"])
22
+ next unless File.exist?(path)
23
+ options = load_file(options, path, categories)
24
+ end
25
+ options
26
+ end
27
+
28
+ # Load categories +c+ of a single config +file+ into given +options+ hash.
29
+ def self.load_file(options, file, c = [])
30
+ categories = YAML::load_file(file)
31
+ [:all, *(c.is_a?(Array) ? c : [c])].each do |category|
32
+ options = merge(options, categories[category.to_s]) if categories[category.to_s]
33
+ end
34
+ options
35
+ end
36
+
37
+ # Deep-merge hash +b+ into +a+.
38
+ def self.merge(a, b)
39
+ return a unless b
40
+ symbolize(a).merge(symbolize(b)) do |k, o, n|
41
+ if o.is_a?(Hash) && n.is_a?(Hash)
42
+ merge(symbolize(o), symbolize(n))
43
+ else
44
+ n
45
+ end
46
+ end
47
+ end
48
+
49
+ # Turn all keys in +hash+ into symbols.
50
+ def self.symbolize(hash)
51
+ Hash[hash.map{|k,v|[k.to_sym,v]}]
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,126 @@
1
+ require 'socket'
2
+ require 'eventmachine'
3
+ require 'bitcoin'
4
+
5
+ module Bitcoin
6
+
7
+ module ConnectionHandler
8
+ def hth(h); h.unpack("H*")[0]; end
9
+ def htb(h); [h].pack("H*"); end
10
+
11
+ def on_inv_transaction(hash)
12
+ p ['inv transaction', hth(hash)]
13
+ pkt = Protocol.getdata_pkt(:tx, [hash])
14
+ send_data(pkt)
15
+ end
16
+
17
+ def on_inv_block(hash)
18
+ p ['inv block', hth(hash)]
19
+ pkt = Protocol.getdata_pkt(:block, [hash])
20
+ send_data(pkt)
21
+ end
22
+
23
+ def on_get_transaction(hash)
24
+ p ['get transaction', hth(hash)]
25
+ end
26
+
27
+ def on_get_block(hash)
28
+ p ['get block', hth(hash)]
29
+ end
30
+
31
+ def on_addr(addr)
32
+ p ['addr', addr, addr.alive?]
33
+ end
34
+
35
+ def on_tx(tx)
36
+ p ['tx', tx.hash]
37
+ end
38
+
39
+ def on_block(block)
40
+ p ['block', block.hash]
41
+ #p block.payload.each_byte.map{|i| "%02x" % [i] }.join(" ")
42
+ #puts block.to_json
43
+ end
44
+
45
+ def on_version(version)
46
+ p [@sockaddr, 'version', version, version.timestamp - Time.now.to_i]
47
+ send_data( Protocol.verack_pkt )
48
+ end
49
+
50
+ def on_handshake_complete
51
+ p [@sockaddr, 'handshake complete']
52
+ @connected = true
53
+
54
+ query_blocks
55
+ end
56
+
57
+ def query_blocks
58
+ start = ("\x00"*32)
59
+ stop = ("\x00"*32)
60
+ pkt = Protocol.pkt("getblocks", "\x00" + start + stop )
61
+ send_data(pkt)
62
+ end
63
+
64
+ def on_handshake_begin
65
+ block = 127953
66
+ from = "127.0.0.1:8333"
67
+ from_id = Bitcoin::Protocol::Uniq
68
+ to = @sockaddr.reverse.join(":")
69
+ # p "==", from_id, from, to, block
70
+ pkt = Protocol.version_pkt(from_id, from, to, block)
71
+ p ['sending version pkt', pkt]
72
+ send_data(pkt)
73
+ end
74
+ end
75
+
76
+
77
+ class Connection < EM::Connection
78
+ include ConnectionHandler
79
+
80
+ def initialize(host, port, connections)
81
+ @sockaddr = [port, host]
82
+ @connections = connections
83
+ @parser = Bitcoin::Protocol::Parser.new( self )
84
+ end
85
+
86
+ def post_init
87
+ p ['connected', @sockaddr]
88
+ EM.schedule{ on_handshake_begin }
89
+ end
90
+
91
+ def receive_data(data)
92
+ @parser.parse(data)
93
+ end
94
+
95
+ def unbind
96
+ p ['disconnected', @sockaddr]
97
+ self.class.connect_random_from_dns(@connections)
98
+ end
99
+
100
+ def self.connect(host, port, connections)
101
+ EM.connect(host, port, self, host, port, connections)
102
+ end
103
+
104
+ def self.connect_random_from_dns(connections)
105
+ seeds = Bitcoin.network[:dns_seeds]
106
+ if seeds.any?
107
+ host = `nslookup #{seeds.sample}`.scan(/Address\: (.+)$/).flatten.sample
108
+ connect(host, Bitcoin::network[:default_port], connections)
109
+ else
110
+ raise "No DNS seeds available. Provide IP, configure seeds, or use different network."
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+
117
+ if $0 == __FILE__
118
+ EM.run do
119
+
120
+ connections = []
121
+ #Bitcoin::Connection.connect('127.0.0.1', 8333, connections)
122
+ #Bitcoin::Connection.connect('217.157.1.202', 8333, connections)
123
+ Bitcoin::Connection.connect_random_from_dns(connections)
124
+
125
+ end
126
+ end