bitcoin-ruby 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- data/spec/bitcoin/storage_spec.rb +0 -229
@@ -1,3 +1,7 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
1
5
|
# Client to connect to CommandHandler and issue requests or register for events
|
2
6
|
class Bitcoin::Network::CommandClient < EM::Connection
|
3
7
|
|
@@ -35,11 +39,7 @@ class Bitcoin::Network::CommandClient < EM::Connection
|
|
35
39
|
def unbind
|
36
40
|
log.debug { "Disconnected." }
|
37
41
|
callback :disconnected
|
38
|
-
|
39
|
-
log.info { "Trying to start server..." }
|
40
|
-
EM.defer { system("bin/bitcoin_node", "--quiet") }
|
41
|
-
end
|
42
|
-
EM.add_timer(1) do
|
42
|
+
EM.add_timer(@connection_attempts) do
|
43
43
|
@connection_attempts += 1
|
44
44
|
reconnect(@host, @port)
|
45
45
|
post_init
|
@@ -86,7 +86,8 @@ class Bitcoin::Network::CommandClient < EM::Connection
|
|
86
86
|
# register callbacks for monitor
|
87
87
|
def register_monitor_callbacks
|
88
88
|
on_monitor do |type, data|
|
89
|
-
|
89
|
+
type, *params = type.split("_")
|
90
|
+
callback(type, *((data || []) + (params || [])))
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'monitor'
|
3
5
|
|
@@ -29,83 +31,156 @@ class Bitcoin::Network::CommandHandler < EM::Connection
|
|
29
31
|
# receive request from the client
|
30
32
|
def receive_data data
|
31
33
|
@buf.extract(data).each do |packet|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
begin
|
35
|
+
cmd, args = JSON::parse(packet)
|
36
|
+
log.debug { [cmd, args] }
|
37
|
+
if cmd == "relay_tx"
|
38
|
+
handle_relay_tx(*args)
|
39
|
+
return
|
40
|
+
end
|
41
|
+
if respond_to?("handle_#{cmd}")
|
42
|
+
respond(cmd, send("handle_#{cmd}", *args))
|
43
|
+
else
|
44
|
+
respond(cmd, { error: "unknown command: #{cmd}. send 'help' for help." })
|
45
|
+
end
|
46
|
+
rescue ArgumentError
|
47
|
+
respond(cmd, { error: $!.message })
|
38
48
|
end
|
39
49
|
end
|
40
50
|
rescue Exception
|
41
|
-
p
|
51
|
+
p $!; puts *$@
|
42
52
|
end
|
43
53
|
|
44
54
|
# handle +monitor+ command; subscribe client to specified channels
|
45
|
-
# (+block+, +tx+, +connection+)
|
55
|
+
# (+block+, +tx+, +output+, +connection+).
|
56
|
+
# Some commands can have parameters, e.g. the number of confirmations
|
57
|
+
# +tx+ or +output+ should have. Parameters are appended to the command
|
58
|
+
# name after an underscore (_), e.g. subscribe to channel "tx_6" to
|
59
|
+
# receive only transactions with 6 confirmations.
|
60
|
+
#
|
61
|
+
# Receive new blocks:
|
46
62
|
# bitcoin_node monitor block
|
47
|
-
#
|
63
|
+
# Receive new (unconfirmed) transactions:
|
64
|
+
# bitcoin_node monitor tx
|
65
|
+
# Receive transactions with 6 confirmations:
|
66
|
+
# bitcoin_node monitor tx_6
|
67
|
+
# Receive [txhash, address, value] for each output:
|
68
|
+
# bitcoin_node monitor output
|
69
|
+
# Receive peer connections/disconnections:
|
70
|
+
# bitcoin_node monitor connection"
|
71
|
+
# Combine multiple channels:
|
72
|
+
# bitcoin_node monitor "block tx tx_1 tx_6 connection"
|
73
|
+
#
|
74
|
+
# NOTE: When a new block is found, it might include transactions that we
|
75
|
+
# didn't previously receive as unconfirmed. To make sure you receive all
|
76
|
+
# transactions, also subscribe to the tx_1 channel.
|
48
77
|
def handle_monitor *channels
|
49
|
-
channels.each do |channel|
|
50
|
-
@node.
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
78
|
+
channels.map(&:to_sym).each do |channel|
|
79
|
+
@node.subscribe(channel) {|*data| respond("monitor", [channel, *data]) }
|
80
|
+
name, *params = channel.to_s.split("_")
|
81
|
+
send("handle_monitor_#{name}", *params)
|
82
|
+
log.info { "Client subscribed to channel #{channel}" }
|
83
|
+
end
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
# Handle +monitor block+ command; send the current chain head
|
88
|
+
# after client is subscribed to :block channel
|
89
|
+
def handle_monitor_block
|
90
|
+
head = Bitcoin::P::Block.new(@node.store.get_head.to_payload) rescue nil
|
91
|
+
respond("monitor", ["block", [head, @node.store.get_depth]]) if head
|
92
|
+
end
|
93
|
+
|
94
|
+
# Handle +monitor tx+ command.
|
95
|
+
# When +conf+ is given, don't subscribe to the :tx channel for unconfirmed
|
96
|
+
# transactions. Instead, subscribe to the :block channel, and whenever a new
|
97
|
+
# block comes in, send all transactions that now have +conf+ confirmations.
|
98
|
+
def handle_monitor_tx conf = nil
|
99
|
+
return unless conf
|
100
|
+
if conf.to_i == 0 # 'tx_0' is just an alias for 'tx'
|
101
|
+
return @node.subscribe(:tx) {|*a| @node.notifiers[:tx_0].push(*a) }
|
102
|
+
end
|
103
|
+
@node.subscribe(:block) do |block, depth|
|
104
|
+
block = @node.store.get_block_by_depth(depth - conf.to_i + 1)
|
105
|
+
next unless block
|
106
|
+
block.tx.each {|tx| @node.notifiers["tx_#{conf}".to_sym].push([tx, conf.to_i]) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Handle +monitor output+ command.
|
111
|
+
# Receive tx hash, recipient address and value for each output.
|
112
|
+
# This allows easy scanning for new payments without parsing the
|
113
|
+
# tx format and running scripts.
|
114
|
+
# See #handle_monitor_tx for confirmation behavior.
|
115
|
+
def handle_monitor_output conf = 0
|
116
|
+
return unless (conf = conf.to_i) > 0
|
117
|
+
@node.subscribe(:block) do |block, depth|
|
118
|
+
block = @node.store.get_block_by_depth(depth - conf + 1)
|
119
|
+
next unless block
|
120
|
+
block.tx.each do |tx|
|
121
|
+
tx.out.each do |out|
|
122
|
+
addr = Bitcoin::Script.new(out.pk_script).get_address
|
123
|
+
res = [tx.hash, addr, out.value, conf]
|
124
|
+
@node.push_notification("output_#{conf}".to_sym, res)
|
60
125
|
end
|
61
126
|
end
|
62
127
|
end
|
63
|
-
nil
|
64
128
|
end
|
65
129
|
|
66
|
-
#
|
130
|
+
# Handle +monitor connection+ command; send current connections
|
131
|
+
# after client is subscribed to :connection channel.
|
132
|
+
def handle_monitor_connection
|
133
|
+
@node.connections.select {|c| c.connected?}.each do |conn|
|
134
|
+
respond("monitor", [:connection, [:connected, conn.info]])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get various statistics.
|
67
139
|
# bitcoin_node info
|
68
140
|
def handle_info
|
69
|
-
blocks = @node.connections.map(&:version).compact.map(&:
|
70
|
-
{
|
71
|
-
|
141
|
+
blocks = @node.connections.map(&:version).compact.map(&:last_block) rescue nil
|
142
|
+
established = @node.connections.select {|c| c.state == :connected }
|
143
|
+
info = {
|
144
|
+
:blocks => "#{@node.store.get_depth} (#{(blocks.inject{|a,b| a+=b; a } / blocks.size rescue '?' )})#{@node.store.in_sync? ? ' sync' : ''}",
|
72
145
|
:addrs => "#{@node.addrs.select{|a| a.alive?}.size} (#{@node.addrs.size})",
|
73
|
-
:connections => "#{
|
146
|
+
:connections => "#{established.size} established (#{established.select(&:outgoing?).size} out, #{established.select(&:incoming?).size} in), #{@node.connections.size - established.size} connecting",
|
74
147
|
:queue => @node.queue.size,
|
75
148
|
:inv_queue => @node.inv_queue.size,
|
76
149
|
:inv_cache => @node.inv_cache.size,
|
77
150
|
:network => @node.config[:network],
|
78
151
|
:storage => @node.config[:storage],
|
79
|
-
:version => Bitcoin
|
152
|
+
:version => Bitcoin.network[:protocol_version],
|
153
|
+
:external_ip => @node.external_ip,
|
80
154
|
:uptime => format_uptime(@node.uptime),
|
81
155
|
}
|
156
|
+
Bitcoin.namecoin? ? {:names => @node.store.db[:names].count}.merge(info) : info
|
82
157
|
end
|
83
158
|
|
84
|
-
#
|
159
|
+
# Get the currently active configuration.
|
85
160
|
# bitcoin_node config
|
86
161
|
def handle_config
|
87
162
|
@node.config
|
88
163
|
end
|
89
164
|
|
90
|
-
#
|
165
|
+
# Get currently connected peers.
|
91
166
|
# bitcoin_node connections
|
92
167
|
def handle_connections
|
93
168
|
@node.connections.sort{|x,y| y.uptime <=> x.uptime}.map{|c|
|
94
|
-
"#{c.host.rjust(15)}:#{c.port} [state: #{c.state}, " +
|
169
|
+
"#{c.host.rjust(15)}:#{c.port} [#{c.direction}, state: #{c.state}, " +
|
95
170
|
"version: #{c.version.version rescue '?'}, " +
|
96
171
|
"block: #{c.version.block rescue '?'}, " +
|
97
172
|
"uptime: #{format_uptime(c.uptime) rescue 0}, " +
|
98
173
|
"client: #{c.version.user_agent rescue '?'}]" }
|
99
174
|
end
|
100
175
|
|
101
|
-
#
|
176
|
+
# Connect to given peer(s).
|
102
177
|
# bitcoin_node connect <ip>:<port>[,<ip>:<port>]
|
103
178
|
def handle_connect *args
|
104
179
|
args.each {|a| @node.connect_peer(*a.split(':')) }
|
105
180
|
{:state => "Connecting..."}
|
106
181
|
end
|
107
182
|
|
108
|
-
#
|
183
|
+
# Disconnect given peer(s).
|
109
184
|
# bitcoin_node disconnect <ip>:<port>[,<ip>,<port>]
|
110
185
|
def handle_disconnect *args
|
111
186
|
args.each do |c|
|
@@ -116,21 +191,21 @@ class Bitcoin::Network::CommandHandler < EM::Connection
|
|
116
191
|
{:state => "Disconnected"}
|
117
192
|
end
|
118
193
|
|
119
|
-
#
|
194
|
+
# Trigger the node to ask its peers for new blocks.
|
120
195
|
# bitcoin_node getblocks
|
121
196
|
def handle_getblocks
|
122
197
|
@node.connections.sample.send_getblocks
|
123
198
|
{:state => "Sending getblocks..."}
|
124
199
|
end
|
125
200
|
|
126
|
-
#
|
201
|
+
# Trigger the node to ask its for new peer addresses.
|
127
202
|
# bitcoin_node getaddr
|
128
203
|
def handle_getaddr
|
129
204
|
@node.connections.sample.send_getaddr
|
130
205
|
{:state => "Sending getaddr..."}
|
131
206
|
end
|
132
207
|
|
133
|
-
#
|
208
|
+
# Get known peer addresses (used by bin/bitcoin_dns_seed).
|
134
209
|
# bitcoin_node addrs [count]
|
135
210
|
def handle_addrs count = 32
|
136
211
|
@node.addrs.weighted_sample(count.to_i) do |addr|
|
@@ -140,28 +215,139 @@ class Bitcoin::Network::CommandHandler < EM::Connection
|
|
140
215
|
end.compact
|
141
216
|
end
|
142
217
|
|
143
|
-
#
|
144
|
-
# bitcoin_node
|
145
|
-
def
|
146
|
-
|
147
|
-
|
218
|
+
# Trigger a rescan operation when used with a UtxoStore.
|
219
|
+
# bitcoin_node rescan
|
220
|
+
def handle_rescan
|
221
|
+
EM.defer { @node.store.rescan }
|
222
|
+
{:state => "Rescanning ..."}
|
223
|
+
end
|
224
|
+
|
225
|
+
# Get Time Since Last Block.
|
226
|
+
# bitcoin_node tslb
|
227
|
+
def handle_tslb
|
228
|
+
{ tslb: (Time.now - @node.last_block_time).to_i }
|
229
|
+
end
|
230
|
+
|
231
|
+
# Create a transaction, collecting outputs from given +keys+, spending to +recipients+
|
232
|
+
# with an optional +fee+.
|
233
|
+
# Keys is an array that can contain either privkeys, pubkeys or addresses.
|
234
|
+
# When a privkey is given, the corresponding inputs are signed. If not, the
|
235
|
+
# signature_hash is computed and passed along with the response.
|
236
|
+
# After creating an unsigned transaction, one just needs to sign the sig_hashes
|
237
|
+
# and send everything to #assemble_tx, to receive the complete transaction that
|
238
|
+
# can be relayed to the network.
|
239
|
+
def handle_create_tx keys, recipients, fee = 0
|
240
|
+
keystore = Bitcoin::Wallet::SimpleKeyStore.new(file: StringIO.new("[]"))
|
241
|
+
keys.each do |k|
|
242
|
+
begin
|
243
|
+
key = Bitcoin::Key.from_base58(k)
|
244
|
+
key = { addr: key.addr, key: key }
|
245
|
+
rescue
|
246
|
+
if Bitcoin.valid_address?(k)
|
247
|
+
key = { addr: k }
|
248
|
+
else
|
249
|
+
begin
|
250
|
+
key = Bitcoin::Key.new(nil, k)
|
251
|
+
key = { addr: key.addr, key: key }
|
252
|
+
rescue
|
253
|
+
return { error: "Input not valid address, pub- or privkey: #{k}" }
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
keystore.add_key(key)
|
258
|
+
end
|
259
|
+
wallet = Bitcoin::Wallet::Wallet.new(@node.store, keystore)
|
260
|
+
tx = wallet.new_tx(recipients.map {|r| [:address, r[0], r[1]]}, fee)
|
261
|
+
return { error: "Error creating tx." } unless tx
|
262
|
+
[ tx.to_payload.hth, tx.in.map {|i| [i.sig_hash.hth, i.sig_address] rescue nil } ]
|
148
263
|
rescue
|
149
|
-
{:
|
264
|
+
{ error: "Error creating tx: #{$!.message}" }
|
150
265
|
end
|
151
266
|
|
152
|
-
#
|
267
|
+
# Assemble an unsigned transaction from the +tx_hex+ and +sig_pubkeys+.
|
268
|
+
# The +tx_hex+ is the regular transaction structure, with empty input scripts
|
269
|
+
# (as returned by #create_tx when called without privkeys).
|
270
|
+
# +sig_pubkeys+ is an array of [signature, pubkey] pairs used to build the
|
271
|
+
# input scripts.
|
272
|
+
def handle_assemble_tx tx_hex, sig_pubs
|
273
|
+
tx = Bitcoin::P::Tx.new(tx_hex.htb)
|
274
|
+
sig_pubs.each.with_index do |sig_pub, idx|
|
275
|
+
sig, pub = *sig_pub.map(&:htb)
|
276
|
+
script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, pub)
|
277
|
+
tx.in[idx].script_sig_length = script_sig.bytesize
|
278
|
+
tx.in[idx].script_sig = script_sig
|
279
|
+
end
|
280
|
+
tx = Bitcoin::P::Tx.new(tx.to_payload)
|
281
|
+
tx.validator(@node.store).validate(raise_errors: true)
|
282
|
+
tx.to_payload.hth
|
283
|
+
rescue
|
284
|
+
{ error: "Error assembling tx: #{$!.message}" }
|
285
|
+
end
|
286
|
+
|
287
|
+
# Relay given transaction (in hex).
|
288
|
+
# bitcoin_node relay_tx <tx in hex>
|
289
|
+
def handle_relay_tx hex, send = 3, wait = 3
|
290
|
+
begin
|
291
|
+
tx = Bitcoin::P::Tx.new(hex.htb)
|
292
|
+
rescue
|
293
|
+
return respond("relay_tx", { error: "Error decoding transaction." })
|
294
|
+
end
|
295
|
+
|
296
|
+
validator = tx.validator(@node.store)
|
297
|
+
unless validator.validate(rules: [:syntax])
|
298
|
+
return respond("relay_tx", { error: "Transaction syntax invalid.",
|
299
|
+
details: validator.error })
|
300
|
+
end
|
301
|
+
unless validator.validate(rules: [:context])
|
302
|
+
return respond("relay_tx", { error: "Transaction context invalid.",
|
303
|
+
details: validator.error })
|
304
|
+
end
|
305
|
+
|
306
|
+
#@node.store.store_tx(tx)
|
307
|
+
@node.relay_tx[tx.hash] = tx
|
308
|
+
@node.relay_propagation[tx.hash] = 0
|
309
|
+
@node.connections.select(&:connected?).sample(send).each {|c| c.send_inv(:tx, tx.hash) }
|
310
|
+
|
311
|
+
EM.add_timer(wait) do
|
312
|
+
received = @node.relay_propagation[tx.hash]
|
313
|
+
total = @node.connections.select(&:connected?).size - send
|
314
|
+
percent = 100.0 / total * received
|
315
|
+
respond("relay_tx", { success: true, hash: tx.hash, propagation: {
|
316
|
+
received: received, sent: 1, percent: percent } })
|
317
|
+
end
|
318
|
+
rescue
|
319
|
+
respond("relay_tx", { error: $!.message, backtrace: $@ })
|
320
|
+
end
|
321
|
+
|
322
|
+
# Stop the bitcoin node.
|
153
323
|
# bitcoin_node stop
|
154
324
|
def handle_stop
|
155
325
|
Thread.start { sleep 0.1; @node.stop }
|
156
326
|
{:state => "Stopping..."}
|
157
327
|
end
|
158
328
|
|
159
|
-
#
|
329
|
+
# List all available commands.
|
160
330
|
# bitcoin_node help
|
161
331
|
def handle_help
|
162
332
|
self.methods.grep(/^handle_(.*?)/).map {|m| m.to_s.sub(/^(.*?)_/, '')}
|
163
333
|
end
|
164
334
|
|
335
|
+
# Validate and store given block (in hex) as if it was received by a peer.
|
336
|
+
# bitcoin_node store_block <block in hex>
|
337
|
+
def handle_store_block hex
|
338
|
+
block = Bitcoin::P::Block.new(hex.htb)
|
339
|
+
@node.queue << [:block, block]
|
340
|
+
{ queued: [ :block, block.hash ] }
|
341
|
+
end
|
342
|
+
|
343
|
+
# Store given transaction (in hex) as if it was received by a peer.
|
344
|
+
# bitcoin_node store_tx <tx in hex>
|
345
|
+
def handle_store_tx hex
|
346
|
+
tx = Bitcoin::P::Tx.new(hex.htb)
|
347
|
+
@node.queue << [:tx, tx]
|
348
|
+
{ queued: [ :tx, tx.hash ] }
|
349
|
+
end
|
350
|
+
|
165
351
|
# format node uptime
|
166
352
|
def format_uptime t
|
167
353
|
mm, ss = t.divmod(60) #=> [4515, 21]
|
@@ -1,18 +1,24 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
1
3
|
require 'eventmachine'
|
2
4
|
|
3
5
|
module Bitcoin::Network
|
4
6
|
|
5
7
|
# Node network connection to a peer. Handles all the communication with a specific peer.
|
6
|
-
# TODO: incoming/outgoing?
|
7
8
|
class ConnectionHandler < EM::Connection
|
8
9
|
|
10
|
+
LATENCY_MAX = (5*60*1000) # 5min in ms
|
11
|
+
|
9
12
|
include Bitcoin
|
10
13
|
include Bitcoin::Storage
|
11
14
|
|
12
|
-
attr_reader :host, :port, :
|
15
|
+
attr_reader :host, :port, :version, :direction
|
13
16
|
|
14
|
-
|
15
|
-
|
17
|
+
# :new, :handshake, :connected, :disconnected
|
18
|
+
attr_reader :state
|
19
|
+
|
20
|
+
# latency of this connection based on last ping/pong
|
21
|
+
attr_reader :latency_ms
|
16
22
|
|
17
23
|
def log
|
18
24
|
@log ||= Logger::LogWrapper.new("#@host:#@port", @node.log)
|
@@ -24,12 +30,17 @@ module Bitcoin::Network
|
|
24
30
|
end
|
25
31
|
|
26
32
|
# create connection to +host+:+port+ for given +node+
|
27
|
-
def initialize node, host, port
|
28
|
-
@node, @host, @port = node, host, port
|
33
|
+
def initialize node, host, port, direction
|
34
|
+
@node, @host, @port, @direction = node, host, port, direction
|
29
35
|
@parser = Bitcoin::Protocol::Parser.new(self)
|
30
36
|
@state = :new
|
31
37
|
@version = nil
|
32
38
|
@started = nil
|
39
|
+
@port, @host = *Socket.unpack_sockaddr_in(get_peername) if get_peername
|
40
|
+
@ping_nonce = nil
|
41
|
+
@latency_ms = nil
|
42
|
+
@lock = Monitor.new
|
43
|
+
@last_getblocks = [] # the last few getblocks messages received
|
33
44
|
rescue Exception
|
34
45
|
log.fatal { "Error in #initialize" }
|
35
46
|
p $!; puts $@; exit
|
@@ -37,36 +48,73 @@ module Bitcoin::Network
|
|
37
48
|
|
38
49
|
# check if connection is wanted, begin handshake if it is, disconnect if not
|
39
50
|
def post_init
|
40
|
-
if
|
41
|
-
|
51
|
+
if incoming?
|
52
|
+
begin_handshake
|
42
53
|
end
|
43
|
-
log.info { "Connected to #{@host}:#{@port}" }
|
44
|
-
@state = :established
|
45
|
-
@node.connections << self
|
46
|
-
on_handshake_begin
|
47
54
|
rescue Exception
|
48
55
|
log.fatal { "Error in #post_init" }
|
49
|
-
p $!; puts
|
56
|
+
p $!; puts *$@
|
57
|
+
end
|
58
|
+
|
59
|
+
# only called for outgoing connection
|
60
|
+
def connection_completed
|
61
|
+
begin_handshake
|
62
|
+
rescue Exception
|
63
|
+
log.fatal { "Error in #connection_completed" }
|
64
|
+
p $!; puts *$@
|
50
65
|
end
|
51
66
|
|
52
67
|
# receive data from peer and invoke Protocol::Parser
|
53
68
|
def receive_data data
|
54
69
|
#log.debug { "Receiving data (#{data.size} bytes)" }
|
55
|
-
@parser.parse(data)
|
70
|
+
@lock.synchronize { @parser.parse(data) }
|
71
|
+
rescue
|
72
|
+
log.warn { "Error handling data: #{data.hth}" }
|
73
|
+
p $!; puts *$@
|
56
74
|
end
|
57
75
|
|
58
76
|
# connection closed; notify listeners and cleanup connection from node
|
59
77
|
def unbind
|
60
|
-
log.info { "Disconnected
|
61
|
-
@node.
|
78
|
+
log.info { "Disconnected" }
|
79
|
+
@node.push_notification(:connection, [:disconnected, [@host, @port]])
|
62
80
|
@state = :disconnected
|
63
81
|
@node.connections.delete(self)
|
64
82
|
end
|
65
83
|
|
84
|
+
# begin handshake
|
85
|
+
# TODO: disconnect if we don't complete within a reasonable time
|
86
|
+
def begin_handshake
|
87
|
+
if incoming? && !@node.accept_connections?
|
88
|
+
return close_connection unless @node.config[:connect].include?([@host, @port.to_s])
|
89
|
+
end
|
90
|
+
log.info { "Established #{@direction} connection" }
|
91
|
+
@node.connections << self
|
92
|
+
@state = :handshake
|
93
|
+
# incoming connections wait to receive a version
|
94
|
+
send_version if outgoing?
|
95
|
+
rescue Exception
|
96
|
+
log.fatal { "Error in #begin_handshake" }
|
97
|
+
p $!; puts *$@
|
98
|
+
end
|
99
|
+
|
100
|
+
# complete handshake; set state, started time, notify listeners and add address to Node
|
101
|
+
def complete_handshake
|
102
|
+
if @state == :handshake
|
103
|
+
log.debug { 'Handshake completed' }
|
104
|
+
@state = :connected
|
105
|
+
@started = Time.now
|
106
|
+
@node.push_notification(:connection, [:connected, info])
|
107
|
+
@node.addrs << addr
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
66
111
|
# received +inv_tx+ message for given +hash+.
|
67
112
|
# add to inv_queue, unlesss maximum is reached
|
68
113
|
def on_inv_transaction(hash)
|
69
|
-
log.debug { ">> inv transaction: #{hth
|
114
|
+
log.debug { ">> inv transaction: #{hash.hth}" }
|
115
|
+
if @node.relay_propagation.keys.include?(hash.hth)
|
116
|
+
@node.relay_propagation[hash.hth] += 1
|
117
|
+
end
|
70
118
|
return if @node.inv_queue.size >= @node.config[:max][:inv]
|
71
119
|
@node.queue_inv([:tx, hash, self])
|
72
120
|
end
|
@@ -74,7 +122,7 @@ module Bitcoin::Network
|
|
74
122
|
# received +inv_block+ message for given +hash+.
|
75
123
|
# add to inv_queue, unless maximum is reached
|
76
124
|
def on_inv_block(hash)
|
77
|
-
log.debug { ">> inv block: #{hth
|
125
|
+
log.debug { ">> inv block: #{hash.hth}" }
|
78
126
|
return if @node.inv_queue.size >= @node.config[:max][:inv]
|
79
127
|
@node.queue_inv([:block, hash, self])
|
80
128
|
end
|
@@ -82,8 +130,9 @@ module Bitcoin::Network
|
|
82
130
|
# received +get_tx+ message for given +hash+.
|
83
131
|
# send specified tx if we have it
|
84
132
|
def on_get_transaction(hash)
|
85
|
-
log.debug { ">> get transaction: #{hash.
|
86
|
-
tx = @node.store.get_tx(hash.
|
133
|
+
log.debug { ">> get transaction: #{hash.hth}" }
|
134
|
+
tx = @node.store.get_tx(hash.hth)
|
135
|
+
tx ||= @node.relay_tx[hash.hth]
|
87
136
|
return unless tx
|
88
137
|
pkt = Bitcoin::Protocol.pkt("tx", tx.to_payload)
|
89
138
|
log.debug { "<< tx: #{tx.hash}" }
|
@@ -92,16 +141,13 @@ module Bitcoin::Network
|
|
92
141
|
|
93
142
|
# received +get_block+ message for given +hash+.
|
94
143
|
# send specified block if we have it
|
95
|
-
# TODO
|
96
144
|
def on_get_block(hash)
|
97
|
-
log.debug { ">> get block: #{hth
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
pkt
|
103
|
-
log.debug { "<< inv #{type}: #{obj.hash}" }
|
104
|
-
send_data(pkt)
|
145
|
+
log.debug { ">> get block: #{hash.hth}" }
|
146
|
+
blk = @node.store.get_block(hash.hth)
|
147
|
+
return unless blk
|
148
|
+
pkt = Bitcoin::Protocol.pkt("block", blk.to_payload)
|
149
|
+
log.debug { "<< block: #{blk.hash}" }
|
150
|
+
send_data pkt
|
105
151
|
end
|
106
152
|
|
107
153
|
# received +addr+ message for given +addr+.
|
@@ -109,7 +155,7 @@ module Bitcoin::Network
|
|
109
155
|
def on_addr(addr)
|
110
156
|
log.debug { ">> addr: #{addr.ip}:#{addr.port} alive: #{addr.alive?}, service: #{addr.service}" }
|
111
157
|
@node.addrs << addr
|
112
|
-
@node.
|
158
|
+
@node.push_notification(:addr, addr)
|
113
159
|
end
|
114
160
|
|
115
161
|
# received +tx+ message for given +tx+.
|
@@ -136,18 +182,23 @@ module Bitcoin::Network
|
|
136
182
|
# received +version+ message for given +version+.
|
137
183
|
# send +verack+ message and complete handshake
|
138
184
|
def on_version(version)
|
139
|
-
log.
|
185
|
+
log.debug { ">> version: #{version.version}" }
|
186
|
+
@node.external_ips << version.to.split(":")[0]
|
140
187
|
@version = version
|
141
|
-
log.
|
188
|
+
log.debug { "<< verack" }
|
142
189
|
send_data( Protocol.verack_pkt )
|
143
|
-
|
190
|
+
|
191
|
+
# sometimes other nodes don't bother to send a verack back,
|
192
|
+
# but we can consider the handshake complete once we sent ours.
|
193
|
+
# apparently it can happen on incoming and outgoing connections alike
|
194
|
+
complete_handshake
|
144
195
|
end
|
145
196
|
|
146
197
|
# received +verack+ message.
|
147
198
|
# complete handshake if it isn't completed already
|
148
199
|
def on_verack
|
149
|
-
log.
|
150
|
-
|
200
|
+
log.debug { ">> verack" }
|
201
|
+
complete_handshake if outgoing?
|
151
202
|
end
|
152
203
|
|
153
204
|
# received +alert+ message for given +alert+.
|
@@ -156,25 +207,80 @@ module Bitcoin::Network
|
|
156
207
|
log.warn { ">> alert: #{alert.inspect}" }
|
157
208
|
end
|
158
209
|
|
210
|
+
# received +getblocks+ message.
|
211
|
+
# TODO: locator fallback
|
212
|
+
def on_getblocks(version, hashes, stop_hash)
|
213
|
+
# remember the last few received getblocks messages and ignore duplicate ones
|
214
|
+
# fixes unexplained issue where remote node is bombarding us with the same getblocks
|
215
|
+
# message over and over (probably related to missing locator fallback handling)
|
216
|
+
return if @last_getblocks && @last_getblocks.include?([version, hashes, stop_hash])
|
217
|
+
@last_getblocks << [version, hashes, stop_hash]
|
218
|
+
@last_getblocks.shift if @last_getblocks.size > 3
|
219
|
+
|
220
|
+
blk = @node.store.db[:blk][hash: hashes[0].htb.blob]
|
221
|
+
depth = blk[:depth] if blk
|
222
|
+
log.info { ">> getblocks #{hashes[0]} (#{depth || 'unknown'})" }
|
223
|
+
|
224
|
+
return unless depth && depth <= @node.store.get_depth
|
225
|
+
range = (depth+1..depth+500)
|
226
|
+
blocks = @node.store.db[:blk].where(chain: 0, depth: range).select(:hash).all +
|
227
|
+
[@node.store.db[:blk].select(:hash)[chain: 0, depth: depth+502]]
|
228
|
+
send_inv(:block, *blocks.map {|b| b[:hash].hth })
|
229
|
+
end
|
230
|
+
|
231
|
+
# received +getaddr+ message.
|
232
|
+
# send +addr+ message with peer addresses back.
|
233
|
+
def on_getaddr
|
234
|
+
addrs = @node.addrs.select{|a| a.time > Time.now.to_i - 10800 }.shuffle[0..250]
|
235
|
+
log.debug { "<< addr (#{addrs.size})" }
|
236
|
+
send_data P::Addr.pkt(*addrs)
|
237
|
+
end
|
238
|
+
|
239
|
+
# begin handshake; send +version+ message
|
240
|
+
def send_version
|
241
|
+
from = "#{@node.external_ip}:#{@node.config[:listen][1]}"
|
242
|
+
version = Bitcoin::Protocol::Version.new({
|
243
|
+
:version => 70001,
|
244
|
+
:last_block => @node.store.get_depth,
|
245
|
+
:from => from,
|
246
|
+
:to => @host,
|
247
|
+
:user_agent => "/bitcoin-ruby:#{Bitcoin::VERSION}/",
|
248
|
+
#:user_agent => "/Satoshi:0.8.3/",
|
249
|
+
})
|
250
|
+
send_data(version.to_pkt)
|
251
|
+
log.debug { "<< version: #{Bitcoin.network[:protocol_version]}" }
|
252
|
+
end
|
253
|
+
|
254
|
+
# send +inv+ message with given +type+ for given +obj+
|
255
|
+
def send_inv type, *hashes
|
256
|
+
hashes.each_slice(251) do |slice|
|
257
|
+
pkt = Protocol.inv_pkt(type, slice.map(&:htb))
|
258
|
+
log.debug { "<< inv #{type}: #{slice[0][0..16]}" + (slice.size > 1 ? "..#{slice[-1][0..16]}" : "") }
|
259
|
+
send_data(pkt)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
159
263
|
# send +getdata tx+ message for given tx +hash+
|
160
264
|
def send_getdata_tx(hash)
|
161
265
|
pkt = Protocol.getdata_pkt(:tx, [hash])
|
162
|
-
log.debug { "<< getdata tx: #{hth
|
266
|
+
log.debug { "<< getdata tx: #{hash.hth}" }
|
163
267
|
send_data(pkt)
|
164
268
|
end
|
165
269
|
|
166
270
|
# send +getdata block+ message for given block +hash+
|
167
271
|
def send_getdata_block(hash)
|
168
272
|
pkt = Protocol.getdata_pkt(:block, [hash])
|
169
|
-
log.debug { "<< getdata block: #{hth
|
273
|
+
log.debug { "<< getdata block: #{hash.hth}" }
|
170
274
|
send_data(pkt)
|
171
275
|
end
|
172
276
|
|
173
277
|
# send +getblocks+ message
|
174
278
|
def send_getblocks locator = @node.store.get_locator
|
175
|
-
|
176
|
-
|
177
|
-
|
279
|
+
if @node.store.get_depth == -1
|
280
|
+
EM.add_timer(3) { send_getblocks }
|
281
|
+
return get_genesis_block
|
282
|
+
end
|
283
|
+
pkt = Protocol.getblocks_pkt(@version.version, locator)
|
178
284
|
log.info { "<< getblocks: #{locator.first}" }
|
179
285
|
send_data(pkt)
|
180
286
|
end
|
@@ -183,7 +289,7 @@ module Bitcoin::Network
|
|
183
289
|
def send_getheaders locator = @node.store.get_locator
|
184
290
|
return get_genesis_block if @node.store.get_depth == -1
|
185
291
|
pkt = Protocol.pkt("getheaders", [Bitcoin::network[:magic_head],
|
186
|
-
locator.size.chr, *locator.map{|l|
|
292
|
+
locator.size.chr, *locator.map{|l| l.htb_reverse}, "\x00"*32].join)
|
187
293
|
log.debug { "<< getheaders: #{locator.first}" }
|
188
294
|
send_data(pkt)
|
189
295
|
end
|
@@ -197,30 +303,27 @@ module Bitcoin::Network
|
|
197
303
|
# send +ping+ message
|
198
304
|
# TODO: wait for pong and disconnect if it doesn't arrive (and version is new enough)
|
199
305
|
def send_ping
|
200
|
-
|
201
|
-
|
202
|
-
|
306
|
+
if @version.version > Bitcoin::Protocol::BIP0031_VERSION
|
307
|
+
@latency_ms = LATENCY_MAX
|
308
|
+
@ping_nonce = rand(0xffffffff)
|
309
|
+
@ping_time = Time.now
|
310
|
+
log.debug { "<< ping (#{@ping_nonce})" }
|
311
|
+
send_data(Protocol.ping_pkt(@ping_nonce))
|
312
|
+
else
|
313
|
+
# set latency to 5 seconds, terrible but this version should be obsolete now
|
314
|
+
@latency_ms = (5*1000)
|
315
|
+
log.debug { "<< ping" }
|
316
|
+
send_data(Protocol.ping_pkt)
|
317
|
+
end
|
203
318
|
end
|
204
319
|
|
205
320
|
# ask for the genesis block
|
206
321
|
def get_genesis_block
|
207
322
|
log.info { "Asking for genesis block" }
|
208
|
-
pkt = Protocol.getdata_pkt(:block, [
|
323
|
+
pkt = Protocol.getdata_pkt(:block, [Bitcoin::network[:genesis_hash].htb])
|
209
324
|
send_data(pkt)
|
210
325
|
end
|
211
326
|
|
212
|
-
# complete handshake; set state, started time, notify listeners and add address to Node
|
213
|
-
def on_handshake_complete
|
214
|
-
return unless handshake?
|
215
|
-
log.debug { "handshake complete" }
|
216
|
-
@state = :connected
|
217
|
-
@started = Time.now
|
218
|
-
@node.notifiers[:connection].push([:connected, info])
|
219
|
-
@node.addrs << addr
|
220
|
-
# send_getaddr
|
221
|
-
# EM.add_periodic_timer(15) { send_ping }
|
222
|
-
end
|
223
|
-
|
224
327
|
# received +ping+ message with given +nonce+.
|
225
328
|
# send +pong+ message back, if +nonce+ is set.
|
226
329
|
# network versions <=60000 don't set the nonce and don't expect a pong.
|
@@ -230,28 +333,33 @@ module Bitcoin::Network
|
|
230
333
|
end
|
231
334
|
|
232
335
|
# received +pong+ message with given +nonce+.
|
233
|
-
# TODO: see #send_ping
|
234
336
|
def on_pong nonce
|
235
|
-
|
337
|
+
if @ping_nonce == nonce
|
338
|
+
@latency_ms = (Time.now - @ping_time) * 1000.0
|
339
|
+
end
|
340
|
+
log.debug { ">> pong (#{nonce}), latency: #{@latency_ms.to_i}ms" }
|
236
341
|
end
|
237
342
|
|
238
343
|
# begin handshake; send +version+ message
|
239
344
|
def on_handshake_begin
|
240
345
|
@state = :handshake
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
346
|
+
from = "#{@node.external_ip}:#{@node.config[:listen][1]}"
|
347
|
+
version = Bitcoin::Protocol::Version.new({
|
348
|
+
:version => 70001,
|
349
|
+
:last_block => @node.store.get_depth,
|
350
|
+
:from => from,
|
351
|
+
:to => @host,
|
352
|
+
:user_agent => "/bitcoin-ruby:#{Bitcoin::VERSION}/",
|
353
|
+
#:user_agent => "/Satoshi:0.8.1/",
|
354
|
+
})
|
355
|
+
send_data(version.to_pkt)
|
356
|
+
log.debug { "<< version (#{Bitcoin.network[:protocol_version]})" }
|
249
357
|
end
|
250
358
|
|
251
359
|
# get Addr object for this connection
|
252
360
|
def addr
|
253
361
|
return @addr if @addr
|
254
|
-
@addr =
|
362
|
+
@addr = P::Addr.new
|
255
363
|
@addr.time, @addr.service, @addr.ip, @addr.port =
|
256
364
|
Time.now.tv_sec, @version.services, @host, @port
|
257
365
|
@addr
|
@@ -265,10 +373,14 @@ module Bitcoin::Network
|
|
265
373
|
def info
|
266
374
|
{
|
267
375
|
:host => @host, :port => @port, :state => @state,
|
268
|
-
:version => @version.version, :block => @version.
|
269
|
-
:user_agent => @version.user_agent
|
376
|
+
:version => (@version.version rescue 0), :block => @version.last_block,
|
377
|
+
:started => @started.to_i, :user_agent => @version.user_agent
|
270
378
|
}
|
271
379
|
end
|
380
|
+
|
381
|
+
def incoming?; @direction == :in; end
|
382
|
+
def outgoing?; @direction == :out; end
|
383
|
+
|
272
384
|
end
|
273
385
|
|
274
386
|
end
|