bitcoin-ruby 0.0.6 → 0.0.7

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