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,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