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,570 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- require 'json'
4
- require 'monitor'
5
-
6
- # Started by the Node, accepts connections from CommandClient and answers requests or
7
- # registers for events and notifies the clients when they happen.
8
- class Bitcoin::Network::CommandHandler < EM::Connection
9
-
10
- # create new CommandHandler
11
- def initialize node
12
- @node = node
13
- @node.command_connections << self
14
- @buf = BufferedTokenizer.new("\x00")
15
- @lock = Monitor.new
16
- @monitors = []
17
- end
18
-
19
- # wrap logger and append prefix
20
- def log
21
- @log ||= Bitcoin::Logger::LogWrapper.new("command:", @node.log)
22
- end
23
-
24
- # respond to a command; send serialized response to the client
25
- def respond(request, data)
26
- return unless data
27
- request[:result] = data
28
- request.delete(:params)
29
- @lock.synchronize do
30
- send_data(request.to_json + "\x00")
31
- end
32
- end
33
-
34
- # receive request from the client
35
- def receive_data data
36
- @buf.extract(data).each do |packet|
37
- begin
38
- request = symbolize_keys(JSON::parse(packet))
39
- log.debug { request }
40
- case request[:method]
41
- when "relay_tx"
42
- return handle_relay_tx(request, request[:params])
43
- when "monitor"
44
- respond(request, handle_monitor(request, request[:params]))
45
- else
46
- if respond_to?("handle_#{request[:method]}")
47
- if request[:params] && request[:params].any?
48
- respond(request, send("handle_#{request[:method]}", request[:params]))
49
- else
50
- respond(request, send("handle_#{request[:method]}"))
51
- end
52
- else
53
- respond(request, { error: "unknown command: #{request[:method]}. send 'help' for help." })
54
- end
55
- end
56
- rescue
57
- respond(request, { error: $!.message })
58
- p $!; puts *$@
59
- end
60
- end
61
- rescue
62
- p $!; puts *$@
63
- end
64
-
65
- def handle_connected
66
- "connected"
67
- end
68
-
69
- # Handle +monitor+ command; subscribe client to specified channels
70
- # (+block+, +tx+, +output+, +connection+).
71
- # Parameters can be appended to the channel name, e.g. the number of confirmations
72
- # +tx+ or +output+ should have. Parameters are appended to the command
73
- # name after an underscore (_), e.g. subscribe to channel "tx_6" to
74
- # receive only transactions with 6 confirmations.
75
- # You can send the last block/tx/output you know about, and it will also send you
76
- # all the objects you're missing.
77
- #
78
- # Receive new blocks:
79
- # bitcoin_node monitor block
80
- # Receive blocks since block 123, and new ones as they come in:
81
- # bitcoin_node monitor block_123
82
- # Receive new (unconfirmed) transactions:
83
- # bitcoin_node monitor tx
84
- # Receive transactions with 6 confirmations:
85
- # bitcoin_node monitor tx_6
86
- # Receive transactions since <txhash>, and new ones as they come in:
87
- # bitcoin_node monitor tx_1_<txhash>
88
- # Receive [txhash, idx, address, value] for each output:
89
- # bitcoin_node monitor output
90
- # Receive outputs since <txhash>:<idx>, and new ones as they come in:
91
- # bitcoin_node monitor output_1_<txhash>:<idx>
92
- # Receive peer connections/disconnections:
93
- # bitcoin_node monitor connection"
94
- # Combine multiple channels:
95
- # bitcoin_node monitor "block tx tx_1 tx_6 connection"
96
- #
97
- # NOTE: When a new block is found, it might include transactions that we
98
- # didn't previously receive as unconfirmed. To make sure you receive all
99
- # transactions, also subscribe to the tx_1 channel.
100
- def handle_monitor request, params
101
- log.info { "Client subscribed to channel #{params[:channel].inspect}" }
102
- { id: send("handle_monitor_#{params[:channel]}", request, params) }
103
- end
104
-
105
- # Handle +unmonitor+ command; cancel given subscription.
106
- # Parameter +id+ must be the subscription ID that was returned when calling +monitor+.
107
- def handle_unmonitor request
108
- id = request[:id]
109
- raise "Monitor #{id} not found." unless @monitors[id]
110
- @monitors[id][:channels].each {|name, id| @node.unsubscribe(name, id) }
111
- { id: id }
112
- end
113
-
114
- # Handle +monitor block+ command;
115
- def handle_monitor_block request, params
116
- monitor_id = @monitors.size
117
- id = @node.subscribe(:block) {|blk, depth| respond_monitor_block(request, blk, depth) }
118
- add_monitor(params, [[:block, id]])
119
- respond_missed_blocks(request, monitor_id) if params[:last]
120
- monitor_id
121
- end
122
-
123
- def respond_missed_blocks request, monitor_id
124
- params = @monitors[monitor_id][:params]
125
- blk = @node.store.get_block(params[:last])
126
- respond_monitor_block(request, blk)
127
- while blk = blk.get_next_block
128
- respond_monitor_block(request, blk)
129
- end
130
- end
131
-
132
- def respond_monitor_block request, block, depth = nil
133
- depth ||= block.depth
134
- respond(request, { hash: block.hash, hex: block.to_payload.hth, depth: depth })
135
- end
136
-
137
- # TODO: params (min reorg depth)
138
- def handle_monitor_reorg request, params
139
- id = @node.subscribe(:reorg) do |new_main, new_side|
140
- respond(request, { new_main: new_main, new_side: new_side })
141
- end
142
-
143
- add_monitor(params, [[:reorg, id]])
144
- end
145
-
146
- # Handle +monitor tx+ command.
147
- # When +conf+ is given, don't subscribe to the :tx channel for unconfirmed
148
- # transactions. Instead, subscribe to the :block channel, and whenever a new
149
- # block comes in, send all transactions that now have +conf+ confirmations.
150
- def handle_monitor_tx request, params
151
- monitor_id = @monitors.size
152
- tx_id = @node.subscribe(:tx) {|tx, conf| respond_monitor_tx(request, monitor_id, tx, conf) }
153
-
154
- conf = params[:conf].to_i
155
- block_id = @node.subscribe(:block) do |block, depth|
156
- next unless block = @node.store.get_block_by_depth(depth - conf + 1)
157
- block.tx.each {|tx| respond_monitor_tx(request, monitor_id, tx, conf) }
158
- end
159
-
160
- add_monitor(params, [[:tx, tx_id], [:block, block_id]])
161
-
162
- respond_missed_txs(request, params, monitor_id) if params[:last]
163
-
164
- monitor_id
165
- end
166
-
167
- def respond_missed_txs request, params, monitor_id
168
- return unless last_tx = @node.store.get_tx(params[:last])
169
- notify = false; depth = @node.store.get_depth
170
- (last_tx.get_block.depth..depth).each do |i|
171
- blk = @node.store.get_block_by_depth(i)
172
- blk.tx.each do |tx|
173
- respond_monitor_tx(request, monitor_id, tx, (depth - blk.depth + 1)) if notify
174
- notify = true if tx.hash == last_tx.hash
175
- end
176
- end
177
- end
178
-
179
- def respond_monitor_tx request, monitor_id, tx, conf = nil
180
- conf ||= tx.confirmations
181
-
182
- params = @monitors[monitor_id][:params]
183
-
184
- # filter by addresses
185
- if params[:addresses]
186
- addrs = tx.out.map(&:parsed_script).map(&:get_address)
187
- return unless (params[:addresses] & addrs).any?
188
- end
189
-
190
- respond(request, { hash: tx.hash, nhash: tx.nhash, hex: tx.to_payload.hth, conf: conf })
191
- end
192
-
193
- # Handle +monitor output+ command.
194
- # Receive tx hash, recipient address and value for each output.
195
- # This allows easy scanning for new payments without parsing the
196
- # tx format and running scripts.
197
- # See #handle_monitor_tx for confirmation behavior.
198
- def handle_monitor_output request, params
199
- monitor_id = @monitors.size
200
-
201
- tx_id = @node.subscribe(:tx) do |tx, conf|
202
- tx.out.each.with_index do |out, idx|
203
- respond_monitor_output(request, monitor_id, tx, out, idx, conf)
204
- end
205
- end
206
-
207
- if (conf = params[:conf].to_i) > 0
208
- block_id = @node.subscribe(:block) do |block, depth|
209
- block = @node.store.get_block_by_depth(depth - conf + 1)
210
- next unless block
211
- block.tx.each do |tx|
212
- tx.out.each.with_index do |out, idx|
213
- respond_monitor_output(request, monitor_id, tx, out, idx, conf)
214
- end
215
- end
216
- end
217
- end
218
-
219
- add_monitor(params, [[:tx, tx_id], [:block, block_id]])
220
-
221
- respond_missed_outputs(request, monitor_id) if params[:last]
222
-
223
- monitor_id
224
- end
225
-
226
- def respond_missed_outputs request, monitor_id
227
- params = @monitors[monitor_id][:params]
228
- last_hash, last_idx = *params[:last].split(":"); last_idx = last_idx.to_i
229
- return unless last_tx = @node.store.get_tx(last_hash)
230
- return unless last_out = last_tx.out[last_idx]
231
- notify = false
232
- depth = @node.store.get_depth
233
- (last_tx.get_block.depth..depth).each do |i|
234
- blk = @node.store.get_block_by_depth(i)
235
- blk.tx.each do |tx|
236
- tx.out.each.with_index do |out, idx|
237
- if notify
238
- respond_monitor_output(request, monitor_id, tx, out, idx, (depth - blk.depth + 1))
239
- else
240
- notify = true if tx.hash == last_hash && idx == last_idx
241
- end
242
- end
243
- end
244
- end
245
- end
246
-
247
- def respond_monitor_output request, monitor_id, tx, out, idx, conf
248
- addr = out.parsed_script.get_address
249
-
250
- params = @monitors[monitor_id][:params]
251
-
252
- # filter by addresses
253
- return if params[:addresses] && !params[:addresses].include?(addr)
254
-
255
- respond(request, { nhash: tx.nhash, hash: tx.hash, idx: idx,
256
- address: addr, value: out.value, conf: conf })
257
- end
258
-
259
- # Handle +filter monitor output+ command; add given +address+ to the list of
260
- # filtered addresses in the params of the given monitor.
261
- def handle_filter_monitor_output request
262
- @monitors[request[:id]][:params][:addresses] << request[:address]
263
- { id: request[:id] }
264
- end
265
-
266
- # Handle +monitor connection+ command; send current connections
267
- # after client is subscribed to :connection channel.
268
- def handle_monitor_connection request, params
269
- id = @node.subscribe(:connection) {|data| respond(request, data) }
270
- @node.connections.select {|c| c.connected?}.each do |conn|
271
- respond(request, conn.info.merge(type: :connected))
272
- end
273
- add_monitor(params, [[:connection, id]])
274
- end
275
-
276
- # Get various statistics.
277
- # bitcoin_node info
278
- def handle_info
279
- blocks = @node.connections.map(&:version).compact.map(&:last_block) rescue nil
280
- established = @node.connections.select {|c| c.state == :connected }
281
- info = {
282
- blocks: {
283
- depth: @node.store.get_depth,
284
- peers: (blocks.inject{|a,b| a+=b; a } / blocks.size rescue '?' ),
285
- sync: @node.store.in_sync?,
286
- },
287
- addrs: {
288
- alive: @node.addrs.select{|a| a.alive?}.size,
289
- total: @node.addrs.size,
290
- },
291
- connections: {
292
- established: established.size,
293
- outgoing: established.select(&:outgoing?).size,
294
- incoming: established.select(&:incoming?).size,
295
- connecting: @node.connections.size - established.size,
296
- },
297
- queue: @node.queue.size,
298
- inv_queue: @node.inv_queue.size,
299
- inv_cache: @node.inv_cache.size,
300
- network: @node.config[:network],
301
- storage: @node.config[:storage],
302
- version: Bitcoin.network[:protocol_version],
303
- external_ip: @node.external_ip,
304
- uptime: @node.uptime,
305
- }
306
- Bitcoin.namecoin? ? {names: @node.store.db[:names].count}.merge(info) : info
307
- end
308
-
309
- def add_monitor params, channels
310
- @monitors << { params: params, channels: channels }
311
- @monitors.size - 1
312
- end
313
-
314
-
315
- # Get the currently active configuration.
316
- # bitcoin_node config
317
- def handle_config
318
- @node.config
319
- end
320
-
321
- # Get currently connected peers.
322
- # bitcoin_node connections
323
- def handle_connections
324
- @node.connections.sort{|x,y| y.uptime <=> x.uptime}.map{|c|
325
- {
326
- type: c.direction, host: c.host, port: c.port, state: c.state,
327
- uptime: c.uptime,
328
- version: {
329
- version: c.version.version,
330
- services: c.version.services,
331
- time: c.version.time,
332
- nonce: c.version.nonce,
333
- block: c.version.last_block,
334
- client: (c.version.user_agent rescue '?'),
335
- relay: c.version.relay,
336
- }
337
- }
338
- }
339
- end
340
-
341
- # Connect to given peer(s).
342
- # { method: "connect", params: {host: "localhost", port: 12345 }
343
- def handle_connect params
344
- @node.connect_peer(params[:host], params[:port])
345
- { state: :connecting }
346
- end
347
-
348
- # Disconnect given peer(s).
349
- # { method: "disconnect", params: {host: "localhost", port: 12345 }
350
- def handle_disconnect params
351
- conn = @node.connections.find {|c| c.host == params[:host] && c.port == params[:port].to_i}
352
- conn.close_connection if conn
353
- { state: :disconnected }
354
- end
355
-
356
- # Trigger the node to ask its peers for new blocks.
357
- # { method: "getblocks", params: {} }
358
- def handle_getblocks
359
- conn = @node.connections.sample
360
- if conn
361
- conn.send_getblocks
362
- { state: :sent, peer: { host: conn.host, port: conn.port } }
363
- else
364
- raise "No peer connected"
365
- end
366
- end
367
-
368
- # Trigger the node to ask its for new peer addresses.
369
- # { method: "getaddr", params: {} }
370
- def handle_getaddr
371
- conn = @node.connections.sample
372
- if conn
373
- conn.send_getaddr
374
- { state: :sent, peer: { host: conn.host, port: conn.port } }
375
- else
376
- raise "No peer connected"
377
- end
378
- end
379
-
380
- # Get known peer addresses (used by bin/bitcoin_dns_seed).
381
- # { method: "getaddr", params: { count: 32 } }
382
- def handle_addrs params = { count: 32 }
383
- @node.addrs.weighted_sample(params[:count].to_i) do |addr|
384
- Time.now.tv_sec + 7200 - addr.time
385
- end.map do |addr|
386
- [addr.ip, addr.port, Time.now.tv_sec - addr.time] rescue nil
387
- end.compact
388
- end
389
-
390
- # Trigger a rescan operation when used with a UtxoStore.
391
- # { method: "rescan" }
392
- def handle_rescan
393
- EM.defer {
394
- begin
395
- @node.store.rescan
396
- rescue
397
- puts "rescan: #{$!}"
398
- end
399
- }
400
- { state: :rescanning }
401
- end
402
-
403
- # Get Time Since Last Block.
404
- # bitcoin_node tslb
405
- def handle_tslb
406
- { tslb: (Time.now - @node.last_block_time).to_i }
407
- end
408
-
409
- # Create a transaction, collecting outputs from given +keys+, spending to +recipients+
410
- # with an optional +fee+.
411
- # Keys is an array that can contain either privkeys, pubkeys or addresses.
412
- # When a privkey is given, the corresponding inputs are signed. If not, the
413
- # signature_hash is computed and passed along with the response.
414
- # After creating an unsigned transaction, one just needs to sign the sig_hashes
415
- # and send everything to #assemble_tx, to receive the complete transaction that
416
- # can be relayed to the network.
417
- def handle_create_tx params = {}
418
- params[:fee] ||= 0
419
- #keys, recipients, fee = 0
420
- keystore = Bitcoin::Wallet::SimpleKeyStore.new(file: StringIO.new("[]"))
421
- params[:keys].each do |k|
422
- begin
423
- key = Bitcoin::Key.from_base58(k)
424
- key = { addr: key.addr, key: key }
425
- rescue
426
- if Bitcoin.valid_address?(k)
427
- key = { addr: k }
428
- else
429
- begin
430
- key = Bitcoin::Key.new(nil, k)
431
- key = { addr: key.addr, key: key }
432
- rescue
433
- return { error: "Input not valid address, pub- or privkey: #{k}" }
434
- end
435
- end
436
- end
437
- keystore.add_key(key)
438
- end
439
- wallet = Bitcoin::Wallet::Wallet.new(@node.store, keystore)
440
-
441
- tx = wallet.new_tx(params[:recipients].map {|r| [:address, r[0], r[1]]}, params[:fee])
442
- return { error: "Error creating tx." } unless tx
443
- { hash: tx.hash, hex: tx.to_payload.hth,
444
- missing_sigs: tx.in.map {|i| [i.sig_hash.hth, i.sig_address] rescue nil } }
445
- rescue
446
- { error: "Error creating tx: #{$!.message}" }
447
- p $!; puts *$@
448
- end
449
-
450
- # Assemble an unsigned transaction from the +tx+ and +sig_pubkeys+ params.
451
- # The +tx+ is the regular transaction structure, with empty input scripts
452
- # (as returned by #create_tx when called without privkeys).
453
- # +sig_pubkeys+ is an array of [signature, pubkey] pairs used to build the
454
- # input scripts.
455
- def handle_assemble_tx params = {}
456
- # tx_hex, sig_pubs
457
- tx = Bitcoin::P::Tx.new(params[:tx].htb)
458
- params[:sig_pubs].each.with_index do |sig_pub, idx|
459
- sig, pub = *sig_pub.map(&:htb)
460
- script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, pub)
461
- tx.in[idx].script_sig_length = script_sig.bytesize
462
- tx.in[idx].script_sig = script_sig
463
- end
464
- tx = Bitcoin::P::Tx.new(tx.to_payload)
465
- tx.validator(@node.store).validate(raise_errors: true)
466
- { hash: tx.hash, hex: tx.to_payload.hth }
467
- rescue
468
- { error: "Error assembling tx: #{$!.message}" }
469
- p $!; puts *$@
470
- end
471
-
472
- # Relay given transaction (in hex).
473
- # bitcoin_node relay_tx <tx in hex>
474
- def handle_relay_tx request, params = {}
475
- params[:send] ||= 3
476
- params[:wait] ||= 3
477
- # request, hex, send = 3, wait = 3
478
- begin
479
- tx = Bitcoin::P::Tx.new(params[:hex].htb)
480
- rescue
481
- return respond(request, { error: "Error decoding transaction." })
482
- end
483
-
484
- validator = tx.validator(@node.store)
485
- unless validator.validate(rules: [:syntax])
486
- return respond(request, { error: "Transaction syntax invalid.",
487
- details: validator.error })
488
- end
489
- unless validator.validate(rules: [:context])
490
- return respond(request, { error: "Transaction context invalid.",
491
- details: validator.error })
492
- end
493
-
494
- #@node.store.store_tx(tx)
495
- @node.relay_tx[tx.hash] = tx
496
- @node.relay_propagation[tx.hash] = 0
497
- @node.connections.select(&:connected?).sample(params[:send]).each {|c| c.send_inv(:tx, tx.hash) }
498
-
499
- EM.add_timer(params[:wait]) do
500
- received = @node.relay_propagation[tx.hash]
501
- total = @node.connections.select(&:connected?).size - params[:send]
502
- percent = 100.0 / total * received
503
- respond(request, { success: true, hash: tx.hash, propagation: {
504
- received: received, sent: 1, percent: percent } })
505
- end
506
- rescue
507
- respond(request, { error: $!.message, backtrace: $@ })
508
- p $!; puts *$@
509
- end
510
-
511
- # Stop the bitcoin node.
512
- # bitcoin_node stop
513
- def handle_stop
514
- Thread.start { sleep 0.1; @node.stop }
515
- { state: :stopping }
516
- end
517
-
518
- # List all available commands.
519
- # bitcoin_node help
520
- def handle_help
521
- self.methods.grep(/^handle_(.*?)/).map {|m| m.to_s.sub(/^(.*?)_/, '')}
522
- end
523
-
524
- # Validate and store given block (in hex) as if it was received by a peer.
525
- # { method: "store_block", params: { hex: <block data in hex> } }
526
- def handle_store_block params
527
- block = Bitcoin::P::Block.new(params[:hex].htb)
528
- @node.queue << [:block, block]
529
- { queued: block.hash }
530
- end
531
-
532
- # Store given transaction (in hex) as if it was received by a peer.
533
- # { method: "store_tx", params: { hex: <tx data in hex> } }
534
- def handle_store_tx params
535
- tx = Bitcoin::P::Tx.new(params[:hex].htb)
536
- @node.queue << [:tx, tx]
537
- { queued: tx.hash }
538
- end
539
-
540
- # # format node uptime
541
- # def format_uptime t
542
- # mm, ss = t.divmod(60) #=> [4515, 21]
543
- # hh, mm = mm.divmod(60) #=> [75, 15]
544
- # dd, hh = hh.divmod(24) #=> [3, 3]
545
- # "%02d:%02d:%02d:%02d" % [dd, hh, mm, ss]
546
- # end
547
-
548
- # disconnect notification clients when connection is closed
549
- def unbind
550
- #@node.notifiers.unsubscribe(@notify_sid) if @notify_sid
551
- @node.command_connections.delete(self)
552
- end
553
-
554
- private
555
-
556
- def symbolize_keys(obj)
557
- return obj unless [Hash, Array].include?(obj.class)
558
- return obj.map {|v| symbolize_keys(v) } if obj.is_a?(Array)
559
- obj.inject({}){|result, (key, value)|
560
- new_key = key.is_a?(String) ? key.to_sym : key
561
- new_value = case value
562
- when Hash then symbolize_keys(value)
563
- when Array then value.map {|v| symbolize_keys(v) }
564
- else value; end
565
- result[new_key] = new_value
566
- result
567
- }
568
- end
569
-
570
- end