bitcoin-ruby 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +2 -7
  4. data/COPYING +1 -1
  5. data/Gemfile +2 -6
  6. data/Gemfile.lock +34 -0
  7. data/README.rdoc +16 -68
  8. data/Rakefile +3 -6
  9. data/bin/bitcoin_shell +0 -1
  10. data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
  11. data/lib/bitcoin.rb +350 -296
  12. data/lib/bitcoin/builder.rb +3 -1
  13. data/lib/bitcoin/connection.rb +2 -1
  14. data/lib/bitcoin/contracthash.rb +76 -0
  15. data/lib/bitcoin/dogecoin.rb +97 -0
  16. data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
  17. data/lib/bitcoin/ffi/openssl.rb +98 -2
  18. data/lib/bitcoin/ffi/secp256k1.rb +144 -0
  19. data/lib/bitcoin/key.rb +12 -2
  20. data/lib/bitcoin/logger.rb +3 -12
  21. data/lib/bitcoin/protocol/block.rb +3 -9
  22. data/lib/bitcoin/protocol/parser.rb +6 -2
  23. data/lib/bitcoin/protocol/tx.rb +44 -13
  24. data/lib/bitcoin/protocol/txin.rb +4 -2
  25. data/lib/bitcoin/protocol/txout.rb +2 -2
  26. data/lib/bitcoin/script.rb +212 -37
  27. data/lib/bitcoin/trezor/mnemonic.rb +130 -0
  28. data/lib/bitcoin/version.rb +1 -1
  29. data/spec/bitcoin/bitcoin_spec.rb +32 -3
  30. data/spec/bitcoin/builder_spec.rb +18 -0
  31. data/spec/bitcoin/contracthash_spec.rb +45 -0
  32. data/spec/bitcoin/dogecoin_spec.rb +176 -0
  33. data/spec/bitcoin/ffi_openssl.rb +45 -0
  34. data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
  35. data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
  36. data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
  37. data/spec/bitcoin/fixtures/coinbase.json +24 -0
  38. data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
  39. data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
  40. data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
  41. data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
  42. data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
  43. data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
  44. data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
  45. data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
  46. data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
  47. data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
  48. data/spec/bitcoin/protocol/block_spec.rb +0 -22
  49. data/spec/bitcoin/protocol/tx_spec.rb +145 -2
  50. data/spec/bitcoin/script/script_spec.rb +282 -0
  51. data/spec/bitcoin/secp256k1_spec.rb +48 -0
  52. data/spec/bitcoin/spec_helper.rb +0 -51
  53. data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
  54. metadata +48 -98
  55. data/bin/bitcoin_dns_seed +0 -130
  56. data/bin/bitcoin_gui +0 -80
  57. data/bin/bitcoin_node +0 -153
  58. data/bin/bitcoin_node_cli +0 -81
  59. data/bin/bitcoin_wallet +0 -402
  60. data/doc/CONFIG.rdoc +0 -66
  61. data/doc/EXAMPLES.rdoc +0 -13
  62. data/doc/NAMECOIN.rdoc +0 -34
  63. data/doc/NODE.rdoc +0 -225
  64. data/doc/STORAGE.rdoc +0 -33
  65. data/doc/WALLET.rdoc +0 -102
  66. data/examples/balance.rb +0 -66
  67. data/examples/forwarder.rb +0 -73
  68. data/examples/index_nhash.rb +0 -24
  69. data/examples/reindex_p2sh_addrs.rb +0 -44
  70. data/examples/relay_tx.rb +0 -22
  71. data/examples/verify_tx.rb +0 -57
  72. data/lib/bitcoin/config.rb +0 -58
  73. data/lib/bitcoin/gui/addr_view.rb +0 -44
  74. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  75. data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
  76. data/lib/bitcoin/gui/conn_view.rb +0 -38
  77. data/lib/bitcoin/gui/connection.rb +0 -70
  78. data/lib/bitcoin/gui/em_gtk.rb +0 -30
  79. data/lib/bitcoin/gui/gui.builder +0 -1643
  80. data/lib/bitcoin/gui/gui.rb +0 -292
  81. data/lib/bitcoin/gui/helpers.rb +0 -115
  82. data/lib/bitcoin/gui/tree_view.rb +0 -84
  83. data/lib/bitcoin/gui/tx_view.rb +0 -69
  84. data/lib/bitcoin/namecoin.rb +0 -280
  85. data/lib/bitcoin/network/command_client.rb +0 -104
  86. data/lib/bitcoin/network/command_handler.rb +0 -570
  87. data/lib/bitcoin/network/connection_handler.rb +0 -387
  88. data/lib/bitcoin/network/node.rb +0 -565
  89. data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
  90. data/lib/bitcoin/storage/models.rb +0 -171
  91. data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
  92. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
  93. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
  94. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
  95. data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
  96. data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
  97. data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
  98. data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
  99. data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
  100. data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
  101. data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
  102. data/lib/bitcoin/storage/storage.rb +0 -517
  103. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
  104. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
  105. data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
  106. data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
  107. data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
  108. data/lib/bitcoin/validation.rb +0 -400
  109. data/lib/bitcoin/wallet/coinselector.rb +0 -33
  110. data/lib/bitcoin/wallet/keygenerator.rb +0 -77
  111. data/lib/bitcoin/wallet/keystore.rb +0 -207
  112. data/lib/bitcoin/wallet/txdp.rb +0 -118
  113. data/lib/bitcoin/wallet/wallet.rb +0 -281
  114. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  115. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
  116. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  117. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
  118. data/spec/bitcoin/namecoin_spec.rb +0 -182
  119. data/spec/bitcoin/node/command_api_spec.rb +0 -663
  120. data/spec/bitcoin/storage/models_spec.rb +0 -104
  121. data/spec/bitcoin/storage/reorg_spec.rb +0 -236
  122. data/spec/bitcoin/storage/storage_spec.rb +0 -387
  123. data/spec/bitcoin/storage/validation_spec.rb +0 -300
  124. data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
  125. data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
  126. data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
  127. data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
  128. data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
@@ -1,69 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- module Bitcoin::Gui
4
- class TxView < TreeView
5
-
6
- def initialize gui, replace = nil
7
- super(gui, :tx_view, [
8
- [GObject::TYPE_STRING, "Type"],
9
- [GObject::TYPE_STRING, "Hash"],
10
- [GObject::TYPE_STRING, "Value", :format_value_col],
11
- [GObject::TYPE_INT, "Confirmations"],
12
- [GObject::TYPE_STRING, "Direction"],
13
- ])
14
- GObject.signal_connect(@view, "row-activated") do |view, path, column|
15
- res, iter = @model.get_iter(path)
16
- next unless res
17
- tx_hash = @model.get_value(iter, 1).get_string
18
- @gui.display_tx(tx_hash)
19
- end
20
- end
21
-
22
- def update txouts
23
- EM.defer do
24
- @model.clear
25
- txouts.each do |txout|
26
- row = @model.append(nil)
27
- @model.set_value(row, 0, txout.type.to_s)
28
- @model.set_value(row, 1, txout.get_tx.hash)
29
- @model.set_value(row, 2, txout.value.to_s)
30
- @model.set_value(row, 3, txout.get_tx.confirmations)
31
- @model.set_value(row, 4, "incoming")
32
- if txin = txout.get_next_in
33
- row = @model.append(nil)
34
- @model.set_value(row, 0, txout.type.to_s)
35
- @model.set_value(row, 1, txin.get_tx.hash)
36
- @model.set_value(row, 2, (0 - txout.value).to_s)
37
- @model.set_value(row, 3, txin.get_tx.confirmations)
38
- @model.set_value(row, 4, "outgoing")
39
- end
40
- end
41
- @view.set_model @model
42
- end
43
- end
44
- end
45
-
46
- class TxInView < TreeView
47
- def initialize gui, replace = nil
48
- super(gui, [
49
- [GObject::TYPE_STRING, "Type"],
50
- [GObject::TYPE_STRING, "From"],
51
- [GObject::TYPE_STRING, "Value", :format_value_col]
52
- ])
53
- old = @gui.builder.get_object("tx_view")
54
- end
55
-
56
- def update txins
57
- @model.clear
58
- txins.each do |txin|
59
- txout = txin.get_prev_out
60
- row = @model.append(nil)
61
- @model.set_value(row, 0, txout.type.to_s)
62
- @model.set_value(row, 1, txout.get_addresses.join(", "))
63
- @model.set_value(row, 2, txout.value.to_s)
64
- end
65
- @view.set_model @model
66
- end
67
- end
68
-
69
- end
@@ -1,280 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- # This module includes (almost) everything necessary to add namecoin support
4
- # to bitcoin-ruby. When switching to a :namecoin network, it will load its
5
- # functionality into the Script class and the Storage backend.
6
- # The only things not included here should be parsing the AuxPow, which is
7
- # done in Protocol::Block directly, and passing the txout to #store_name from
8
- # the storage backend.
9
- module Bitcoin::Namecoin
10
-
11
- def self.load
12
- Bitcoin::Script.class_eval { include Script }
13
- Bitcoin::Storage::Backends::StoreBase.class_eval { include Storage::Backend }
14
- Bitcoin::Storage::Models.class_eval { include Storage::Models }
15
- end
16
-
17
- # name_new must have 12 confirmations before corresponding name_firstupdate is valid.
18
- FIRSTUPDATE_LIMIT = 12
19
-
20
- # number of blocks after which a name expires.
21
- EXPIRATION_DEPTH = 36000
22
-
23
- # Namecoin-specific Script methods for parsing and creating of namecoin scripts,
24
- # as well as methods to extract address, name_hash, name and value.
25
- module Script
26
-
27
- def self.included(base)
28
- base.constants.each {|c| const_set(c, base.const_get(c)) unless constants.include?(c) }
29
- base.class_eval do
30
-
31
- # get the hash160 for this hash160, namecoin or pubkey script
32
- def get_hash160
33
- return @chunks[2..-3][0].unpack("H*")[0] if is_hash160?
34
- return @chunks[-3].unpack("H*")[0] if is_namecoin?
35
- return @chunks[-2].unpack("H*")[0] if is_p2sh?
36
- return Bitcoin.hash160(get_pubkey) if is_pubkey?
37
- end
38
-
39
- # get all addresses this script corresponds to (if possible)
40
- def get_addresses
41
- return [get_pubkey_address] if is_pubkey?
42
- return [get_hash160_address] if is_hash160? || is_namecoin?
43
- return get_multisig_addresses if is_multisig?
44
- end
45
-
46
- # get type of this tx
47
- def type
48
- if is_name_new?; :name_new
49
- elsif is_name_firstupdate?; :name_firstupdate
50
- elsif is_name_update?; :name_update
51
- elsif is_hash160?; :hash160
52
- elsif is_pubkey?; :pubkey
53
- elsif is_multisig?; :multisig
54
- elsif is_p2sh?; :p2sh
55
- else; :unknown
56
- end
57
- end
58
-
59
- # is namecoin name_new script
60
- # OP_1 name_hash OP_2DROP <hash160_script>
61
- def is_name_new?
62
- return false if @chunks.size < 8
63
- [-8, -6, -5, -4, -2, -1].map {|i| @chunks[i] } ==
64
- [OP_1, OP_2DROP, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG]
65
- end
66
-
67
- # is namecoin name_firstupdate script
68
- # OP_2 name rand value OP_2DROP OP_2DROP <hash160_script>
69
- def is_name_firstupdate?
70
- return false if @chunks.size < 11
71
- [-11, -7, -6, -5, -4, -2, -1].map {|i| @chunks[i] } ==
72
- [82, OP_2DROP, OP_2DROP, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG]
73
- end
74
-
75
- # is namecoin name_update script
76
- # OP_3 name value OP_2DROP OP_DROP <hash160_script>
77
- def is_name_update?
78
- return false if @chunks.size < 10
79
- [-10, -7, -6, -5, -4, -2, -1].map {|i| @chunks[i] } ==
80
- [83, OP_2DROP, OP_DROP, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG]
81
- end
82
-
83
- # is any kind of namecoin script
84
- def is_namecoin?
85
- is_name_new? || is_name_firstupdate? || is_name_update?
86
- end
87
-
88
- # get the name_hash of a namecoin name_new script
89
- def get_namecoin_hash
90
- return @chunks[-7].hth if is_name_new?
91
- if is_name_firstupdate?
92
- name = @chunks[-10].to_s.hth
93
- rand = @chunks[-9].to_s.hth
94
- return Bitcoin.hash160(rand + name)
95
- end
96
- rescue
97
- nil
98
- end
99
-
100
- # get the name of a namecoin name_firstupdate or name_update script
101
- def get_namecoin_name
102
- return @chunks[-10] if is_name_firstupdate?
103
- return @chunks[-9] if is_name_update?
104
- end
105
-
106
- # get the value of a namecoin name_firstupdate or name_update script
107
- def get_namecoin_value
108
- @chunks[-8] if is_name_firstupdate? || is_name_update?
109
- end
110
-
111
- # generate name_new tx for given +name+ and +address+.
112
- # the +caller+ should be the object that creates the script.
113
- # it gets the used random value passed to #set_rand.
114
- # OP_1 name_hash OP_2DROP <hash160_script>
115
- def self.to_name_new_script(caller, name, address)
116
- rand = rand(2**64).to_s(16).rjust(16, '0')
117
- name_hash = Bitcoin.hash160(rand + name.unpack("H*")[0])
118
- caller.set_rand rand # TODO: this is still ugly
119
- [ [ "51", "14", name_hash, "6d" ].join ].pack("H*") + to_address_script(address)
120
- end
121
-
122
- # generate name_firstupdate tx for given +name+, +rand+, +value+ and +address+.
123
- # OP_2 name rand value OP_2DROP OP_2DROP <hash160_script>
124
- def self.to_name_firstupdate_script(name, rand, value, address)
125
- [ [ "52", name.bytesize.to_s(16).rjust(2, '0'), name.hth,
126
- rand.htb.bytesize.to_s(16).rjust(2, '0'), rand,
127
- value.bytesize.to_s(16).rjust(2, '0'), value.hth,
128
- "6d", "6d" ].join ].pack("H*") + to_address_script(address)
129
- end
130
-
131
- # generate name_update script for given +name+, +value+ and +address+.
132
- # OP_3 name value OP_2DROP OP_DROP <hash160_script>
133
- def self.to_name_update_script(name, value, address)
134
- [ [ "53", name.bytesize.to_s(16).rjust(2, '0'), name.hth,
135
- value.bytesize.to_s(16).rjust(2, '0'), value.hth,
136
- "6d", "75" ].join ].pack("H*") + to_address_script(address)
137
- end
138
-
139
- end
140
- end
141
-
142
- end
143
-
144
- # Namecoin-specific storage methods.
145
- # The storage backend only needs to check txout scripts with #is_namecoin? and
146
- # pass them to #store_name.
147
- # TODO: move rules into Validation
148
- module Storage
149
-
150
- module Backend
151
-
152
- def self.included(base)
153
- base.constants.each {|c| const_set(c, base.const_get(c)) unless constants.include?(c) }
154
- base.class_eval do
155
-
156
- # if this is a namecoin script, update the names index
157
- def store_name(script, txout_id)
158
- if script.type == :name_new
159
- log.debug { "name_new #{script.get_namecoin_hash}" }
160
- @db[:names].insert({
161
- :txout_id => txout_id,
162
- :hash => script.get_namecoin_hash })
163
- elsif script.type == :name_firstupdate
164
- name_hash = script.get_namecoin_hash
165
- name_new = @db[:names].where(:hash => name_hash).order(:txout_id).first
166
- if self.class.name =~ /UtxoStore/
167
- txout = @db[:utxo][id: name_new[:txout_id]] if name_new
168
- blk = @db[:blk][id: txout[:blk_id]] if txout
169
- else
170
- txout = @db[:txout][id: name_new[:txout_id]] if name_new
171
- tx = @db[:tx][id: txout[:tx_id]] if txout
172
- blk_tx = @db[:blk_tx][tx_id: tx[:id]] if tx
173
- blk = @db[:blk][id: blk_tx[:blk_id]] if blk_tx
174
- end
175
-
176
- unless name_new && blk && blk[:chain] == 0
177
- log.debug { "name_new not found: #{name_hash}" }
178
- return nil
179
- end
180
-
181
- unless blk[:depth] <= get_depth - Bitcoin::Namecoin::FIRSTUPDATE_LIMIT
182
- log.debug { "name_new not yet valid: #{name_hash}" }
183
- return nil
184
- end
185
-
186
- log.debug { "#{script.type}: #{script.get_namecoin_name}" }
187
- @db[:names].where(:txout_id => name_new[:txout_id], :name => nil).update({
188
- :name => script.get_namecoin_name.to_s.to_sequel_blob })
189
- @db[:names].insert({
190
- :txout_id => txout_id,
191
- :hash => name_hash,
192
- :name => script.get_namecoin_name.to_s.to_sequel_blob,
193
- :value => script.get_namecoin_value.to_s.to_sequel_blob })
194
- elsif script.type == :name_update
195
- log.debug { "#{script.type}: #{script.get_namecoin_name}" }
196
- @db[:names].insert({
197
- :txout_id => txout_id,
198
- :name => script.get_namecoin_name.to_s.to_sequel_blob,
199
- :value => script.get_namecoin_value.to_s.to_sequel_blob })
200
- end
201
- end
202
-
203
- def name_show name
204
- names = @db[:names].where(:name => name.to_sequel_blob).order(:txout_id).reverse
205
- return nil unless names.any?
206
- wrap_name(names.first)
207
- end
208
- alias :get_name :name_show
209
-
210
- def name_history name
211
- history = @db[:names].where(:name => name.to_sequel_blob)
212
- .where("value IS NOT NULL").order(:txout_id).map {|n| wrap_name(n) }
213
- history.select! {|n| n.get_tx.blk_id } unless self.class.name =~ /Utxo/
214
- history
215
- end
216
-
217
- def get_name_by_txout_id txout_id
218
- wrap_name(@db[:names][:txout_id => txout_id])
219
- end
220
-
221
- def wrap_name(data)
222
- return nil unless data
223
- Bitcoin::Storage::Models::Name.new(self, data)
224
- end
225
-
226
- end
227
- end
228
-
229
- end
230
-
231
- module Models
232
-
233
- class Name
234
-
235
- attr_reader :store, :txout_id, :hash, :name, :value
236
-
237
- def initialize store, data
238
- @store = store
239
- @txout_id = data[:txout_id]
240
- @hash = data[:hash]
241
- @name = data[:name]
242
- @value = data[:value]
243
- end
244
-
245
- def get_txout
246
- if @txout_id.is_a?(Array)
247
- @store.get_tx(@txout_id[0]).out[@txout_id[1]]
248
- else
249
- @store.get_txout_by_id(@txout_id)
250
- end
251
- end
252
-
253
- def get_address
254
- get_txout.get_address
255
- end
256
-
257
- def get_tx
258
- get_txout.get_tx rescue nil
259
- end
260
-
261
- def get_block
262
- get_tx.get_block rescue nil
263
- end
264
-
265
- def expires_in
266
- Bitcoin::Namecoin::EXPIRATION_DEPTH - (@store.get_depth - get_block.depth) rescue nil
267
- end
268
-
269
- def as_json(opts = {})
270
- { name: @name, value: @value, txid: get_tx.hash,
271
- address: get_address, expires_in: expires_in }
272
- end
273
-
274
- end
275
-
276
- end
277
-
278
- end
279
-
280
- end
@@ -1,104 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- require 'json'
4
-
5
- # Client to connect to CommandHandler and issue requests or register for events
6
- class Bitcoin::Network::CommandClient < EM::Connection
7
-
8
- # create new client connecting to +host+:+port+ and executing callbacks from +block+,
9
- # passing +args+ in.
10
- # CommandClient.connect(host, port) do
11
- # on_connected { request("info") }
12
- # on_info {|i| p i}
13
- # end
14
- def initialize host, port, block, *args
15
- @host, @port = host, port
16
- @args = args
17
- @callbacks = {}
18
- @block = block
19
- instance_eval &block if block
20
- @buffer = BufferedTokenizer.new("\x00")
21
- @connection_attempts = 0
22
- @requests = {}
23
- @i = 0
24
- end
25
-
26
- def log;
27
- @log ||= Bitcoin::Logger.create(:client)
28
- end
29
-
30
- def self.connect host, port, *args, &block
31
- client = EM.connect(host, port.to_i, self, host, port.to_i, block, *args)
32
- end
33
-
34
- # call +connected+ callback
35
- def post_init
36
- log.debug { "Connected" }
37
- request(:connected) { callback(:connected) }
38
- end
39
-
40
- # call +disconnected+ callback and try to reconnect
41
- def unbind
42
- log.debug { "Disconnected." }
43
- callback :disconnected
44
- EM.add_timer(@connection_attempts) do
45
- @connection_attempts += 1
46
- reconnect(@host, @port)
47
- post_init
48
- end
49
- end
50
-
51
- # request command +cmd+ with +args+ from the server
52
- def request cmd, params = nil, &block
53
- id = @i += 1
54
- @requests[id] = block if block
55
- log.debug { "request: #{cmd} #{args.inspect}" }
56
- register_monitor_callbacks(params) if cmd.to_sym == :monitor
57
- request = { id: id, method: cmd }
58
- request[:params] = params if params
59
- send_data(request.to_json + "\x00")
60
- end
61
-
62
- # receive response from server
63
- def receive_data data
64
- @connection_attempts = 0
65
- @buffer.extract(data).each do |packet|
66
- response = JSON.parse(packet)
67
- log.debug { d = response['result'].inspect
68
- "response: #{response['method']} #{d[0...50]}#{d.size > 50 ? '...' : ''}" }
69
- if cb = @requests[response['id']]
70
- cb.call(response['result'])
71
- else
72
- callback(:response, response['method'], response['result'])
73
- callback(response['method'].to_sym, response['result'])
74
- end
75
- end
76
- end
77
-
78
- # call the callback specified by +name+ passing in +args+
79
- def callback name, *args
80
- cb = @callbacks[name.to_sym]
81
- return unless cb
82
- log.debug { "callback: #{name}" }
83
- cb.call(*args)
84
- end
85
-
86
- # register callback methods
87
- def method_missing(name, *args, &block)
88
- if name =~ /^on_/
89
- @callbacks[name.to_s.split("on_")[1].to_sym] = block
90
- log.debug { "callback #{name} registered" }
91
- else
92
- super(name, *args)
93
- end
94
- end
95
-
96
- # register callbacks for monitor
97
- def register_monitor_callbacks params
98
- on_monitor do |data|
99
- next if data.is_a?(Hash) && data.keys == ["id"]
100
- callback(params["channel"], data)
101
- end
102
- end
103
-
104
- end