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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +2 -7
- data/COPYING +1 -1
- data/Gemfile +2 -6
- data/Gemfile.lock +34 -0
- data/README.rdoc +16 -68
- data/Rakefile +3 -6
- data/bin/bitcoin_shell +0 -1
- data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
- data/lib/bitcoin.rb +350 -296
- data/lib/bitcoin/builder.rb +3 -1
- data/lib/bitcoin/connection.rb +2 -1
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
- data/lib/bitcoin/ffi/openssl.rb +98 -2
- data/lib/bitcoin/ffi/secp256k1.rb +144 -0
- data/lib/bitcoin/key.rb +12 -2
- data/lib/bitcoin/logger.rb +3 -12
- data/lib/bitcoin/protocol/block.rb +3 -9
- data/lib/bitcoin/protocol/parser.rb +6 -2
- data/lib/bitcoin/protocol/tx.rb +44 -13
- data/lib/bitcoin/protocol/txin.rb +4 -2
- data/lib/bitcoin/protocol/txout.rb +2 -2
- data/lib/bitcoin/script.rb +212 -37
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +32 -3
- data/spec/bitcoin/builder_spec.rb +18 -0
- data/spec/bitcoin/contracthash_spec.rb +45 -0
- data/spec/bitcoin/dogecoin_spec.rb +176 -0
- data/spec/bitcoin/ffi_openssl.rb +45 -0
- data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
- data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
- data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
- data/spec/bitcoin/fixtures/coinbase.json +24 -0
- data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
- data/spec/bitcoin/protocol/block_spec.rb +0 -22
- data/spec/bitcoin/protocol/tx_spec.rb +145 -2
- data/spec/bitcoin/script/script_spec.rb +282 -0
- data/spec/bitcoin/secp256k1_spec.rb +48 -0
- data/spec/bitcoin/spec_helper.rb +0 -51
- data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
- metadata +48 -98
- data/bin/bitcoin_dns_seed +0 -130
- data/bin/bitcoin_gui +0 -80
- data/bin/bitcoin_node +0 -153
- data/bin/bitcoin_node_cli +0 -81
- data/bin/bitcoin_wallet +0 -402
- data/doc/CONFIG.rdoc +0 -66
- data/doc/EXAMPLES.rdoc +0 -13
- data/doc/NAMECOIN.rdoc +0 -34
- data/doc/NODE.rdoc +0 -225
- data/doc/STORAGE.rdoc +0 -33
- data/doc/WALLET.rdoc +0 -102
- data/examples/balance.rb +0 -66
- data/examples/forwarder.rb +0 -73
- data/examples/index_nhash.rb +0 -24
- data/examples/reindex_p2sh_addrs.rb +0 -44
- data/examples/relay_tx.rb +0 -22
- data/examples/verify_tx.rb +0 -57
- data/lib/bitcoin/config.rb +0 -58
- data/lib/bitcoin/gui/addr_view.rb +0 -44
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
- data/lib/bitcoin/gui/conn_view.rb +0 -38
- data/lib/bitcoin/gui/connection.rb +0 -70
- data/lib/bitcoin/gui/em_gtk.rb +0 -30
- data/lib/bitcoin/gui/gui.builder +0 -1643
- data/lib/bitcoin/gui/gui.rb +0 -292
- data/lib/bitcoin/gui/helpers.rb +0 -115
- data/lib/bitcoin/gui/tree_view.rb +0 -84
- data/lib/bitcoin/gui/tx_view.rb +0 -69
- data/lib/bitcoin/namecoin.rb +0 -280
- data/lib/bitcoin/network/command_client.rb +0 -104
- data/lib/bitcoin/network/command_handler.rb +0 -570
- data/lib/bitcoin/network/connection_handler.rb +0 -387
- data/lib/bitcoin/network/node.rb +0 -565
- data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
- data/lib/bitcoin/storage/models.rb +0 -171
- data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
- data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
- data/lib/bitcoin/storage/storage.rb +0 -517
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
- data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
- data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
- data/lib/bitcoin/validation.rb +0 -400
- data/lib/bitcoin/wallet/coinselector.rb +0 -33
- data/lib/bitcoin/wallet/keygenerator.rb +0 -77
- data/lib/bitcoin/wallet/keystore.rb +0 -207
- data/lib/bitcoin/wallet/txdp.rb +0 -118
- data/lib/bitcoin/wallet/wallet.rb +0 -281
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
- data/spec/bitcoin/namecoin_spec.rb +0 -182
- data/spec/bitcoin/node/command_api_spec.rb +0 -663
- data/spec/bitcoin/storage/models_spec.rb +0 -104
- data/spec/bitcoin/storage/reorg_spec.rb +0 -236
- data/spec/bitcoin/storage/storage_spec.rb +0 -387
- data/spec/bitcoin/storage/validation_spec.rb +0 -300
- data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
- data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
- data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
- data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
- 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
|