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