bitcoin-ruby 0.0.1 → 0.0.2
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.
- 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
data/lib/bitcoin/network/node.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
Bitcoin.require_dependency :eventmachine
|
|
2
4
|
Bitcoin.require_dependency :json
|
|
3
5
|
require 'fileutils'
|
|
@@ -36,95 +38,130 @@ module Bitcoin::Network
|
|
|
36
38
|
# clients to be notified for new block/tx events
|
|
37
39
|
attr_reader :notifiers
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
# our external ip addresses we got told by peers
|
|
42
|
+
attr_accessor :external_ips
|
|
43
|
+
|
|
44
|
+
# time when the last main chain block was added
|
|
45
|
+
attr_reader :last_block_time
|
|
46
|
+
|
|
47
|
+
attr_accessor :relay_tx
|
|
48
|
+
attr_accessor :relay_propagation
|
|
49
|
+
|
|
40
50
|
|
|
41
51
|
DEFAULT_CONFIG = {
|
|
52
|
+
:network => :bitcoin,
|
|
42
53
|
:listen => ["0.0.0.0", Bitcoin.network[:default_port]],
|
|
43
54
|
:connect => [],
|
|
44
|
-
:command => "",
|
|
45
|
-
:storage =>
|
|
46
|
-
:
|
|
55
|
+
:command => ["127.0.0.1", 9999],
|
|
56
|
+
:storage => "utxo::sqlite://~/.bitcoin-ruby/<network>/blocks.db",
|
|
57
|
+
:mode => :full,
|
|
47
58
|
:dns => true,
|
|
48
|
-
:epoll => false,
|
|
49
59
|
:epoll_limit => 10000,
|
|
50
60
|
:epoll_user => nil,
|
|
51
|
-
:addr_file => "
|
|
61
|
+
:addr_file => "~/.bitcoin-ruby/<network>/peers.json",
|
|
52
62
|
:log => {
|
|
53
63
|
:network => :info,
|
|
54
64
|
:storage => :info,
|
|
55
65
|
},
|
|
56
66
|
:max => {
|
|
67
|
+
:connections_out => 8,
|
|
68
|
+
:connections_in => 32,
|
|
57
69
|
:connections => 8,
|
|
58
70
|
:addr => 256,
|
|
59
|
-
:queue =>
|
|
60
|
-
:inv =>
|
|
61
|
-
:inv_cache =>
|
|
71
|
+
:queue => 501,
|
|
72
|
+
:inv => 501,
|
|
73
|
+
:inv_cache => 0,
|
|
74
|
+
:unconfirmed => 100,
|
|
62
75
|
},
|
|
63
76
|
:intervals => {
|
|
64
|
-
:queue =>
|
|
65
|
-
:inv_queue =>
|
|
77
|
+
:queue => 1,
|
|
78
|
+
:inv_queue => 1,
|
|
66
79
|
:addrs => 5,
|
|
67
|
-
:connect =>
|
|
68
|
-
:relay =>
|
|
80
|
+
:connect => 5,
|
|
81
|
+
:relay => 0,
|
|
69
82
|
},
|
|
83
|
+
:import => nil,
|
|
84
|
+
:skip_validation => false,
|
|
85
|
+
:check_blocks => 1000,
|
|
70
86
|
}
|
|
71
87
|
|
|
72
88
|
def initialize config = {}
|
|
73
89
|
@config = DEFAULT_CONFIG.deep_merge(config)
|
|
74
90
|
@log = Bitcoin::Logger.create(:network, @config[:log][:network])
|
|
75
|
-
@connections = []
|
|
76
|
-
@
|
|
77
|
-
@queue = []
|
|
78
|
-
@queue_thread = nil
|
|
79
|
-
@inv_queue = []
|
|
80
|
-
@inv_queue_thread = nil
|
|
91
|
+
@connections, @command_connections = [], []
|
|
92
|
+
@queue, @queue_thread, @inv_queue, @inv_queue_thread = [], nil, [], nil
|
|
81
93
|
set_store
|
|
82
94
|
load_addrs
|
|
83
95
|
@timers = {}
|
|
84
96
|
@inv_cache = []
|
|
85
|
-
@notifiers =
|
|
86
|
-
@
|
|
97
|
+
@notifiers = {}
|
|
98
|
+
@relay_propagation, @last_block_time, @external_ips = {}, Time.now, []
|
|
99
|
+
@unconfirmed, @relay_tx = {}, {}
|
|
87
100
|
end
|
|
88
101
|
|
|
89
102
|
def set_store
|
|
90
103
|
backend, config = @config[:storage].split('::')
|
|
91
|
-
@store = Bitcoin::Storage.send(backend, {
|
|
104
|
+
@store = Bitcoin::Storage.send(backend, {
|
|
105
|
+
db: config, mode: @config[:mode], cache_head: true,
|
|
106
|
+
skip_validation: @config[:skip_validation],
|
|
107
|
+
log_level: @config[:log][:storage]}, ->(locator) {
|
|
92
108
|
peer = @connections.select(&:connected?).sample
|
|
93
109
|
peer.send_getblocks(locator)
|
|
94
110
|
})
|
|
95
111
|
@store.log.level = @config[:log][:storage]
|
|
112
|
+
@store.check_consistency(@config[:check_blocks])
|
|
113
|
+
if @config[:import]
|
|
114
|
+
@importing = true
|
|
115
|
+
EM.defer do
|
|
116
|
+
begin
|
|
117
|
+
@store.import(@config[:import]); @importing = false
|
|
118
|
+
rescue
|
|
119
|
+
log.fatal { $!.message }
|
|
120
|
+
puts *$@
|
|
121
|
+
stop
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
96
125
|
end
|
|
97
126
|
|
|
98
127
|
def load_addrs
|
|
99
|
-
|
|
128
|
+
file = @config[:addr_file].sub("~", ENV["HOME"])
|
|
129
|
+
.sub("<network>", Bitcoin.network_name.to_s)
|
|
130
|
+
unless File.exist?(file)
|
|
100
131
|
@addrs = []
|
|
132
|
+
FileUtils.mkdir_p(File.dirname(file))
|
|
101
133
|
return
|
|
102
134
|
end
|
|
103
|
-
@addrs = JSON.load(File.read(
|
|
135
|
+
@addrs = JSON.load(File.read(file)).map do |a|
|
|
104
136
|
addr = Bitcoin::P::Addr.new
|
|
105
137
|
addr.time, addr.service, addr.ip, addr.port =
|
|
106
138
|
a['time'], a['service'], a['ip'], a['port']
|
|
107
139
|
addr
|
|
108
140
|
end
|
|
109
|
-
log.info { "Initialized #{@addrs.size} addrs from #{
|
|
141
|
+
log.info { "Initialized #{@addrs.size} addrs from #{file}." }
|
|
142
|
+
rescue
|
|
143
|
+
@addrs = []
|
|
144
|
+
log.warn { "Error loading addrs from #{file}." }
|
|
110
145
|
end
|
|
111
146
|
|
|
112
147
|
def store_addrs
|
|
113
148
|
return if !@addrs || !@addrs.any?
|
|
114
|
-
file = @config[:addr_file]
|
|
149
|
+
file = @config[:addr_file].sub("~", ENV["HOME"])
|
|
150
|
+
.sub("<network>", Bitcoin.network_name.to_s)
|
|
115
151
|
FileUtils.mkdir_p(File.dirname(file))
|
|
116
152
|
File.open(file, 'w') do |f|
|
|
117
153
|
addrs = @addrs.map {|a|
|
|
118
154
|
Hash[[:time, :service, :ip, :port].zip(a.entries)] rescue nil }.compact
|
|
119
155
|
f.write(JSON.pretty_generate(addrs))
|
|
120
156
|
end
|
|
121
|
-
log.info { "Stored #{@addrs.size} addrs to #{file}" }
|
|
157
|
+
log.info { "Stored #{@addrs.size} addrs to #{file}." }
|
|
122
158
|
rescue
|
|
123
159
|
log.warn { "Error storing addrs to #{file}." }
|
|
124
160
|
end
|
|
125
161
|
|
|
126
162
|
def stop
|
|
127
|
-
|
|
163
|
+
puts "Shutting down..."
|
|
164
|
+
stop_timers
|
|
128
165
|
EM.stop
|
|
129
166
|
end
|
|
130
167
|
|
|
@@ -132,6 +169,30 @@ module Bitcoin::Network
|
|
|
132
169
|
(Time.now - @started).to_i
|
|
133
170
|
end
|
|
134
171
|
|
|
172
|
+
def start_timers
|
|
173
|
+
return EM.add_timer(1) { start_timers } if @importing
|
|
174
|
+
[:queue, :inv_queue, :addrs, :connect, :relay].each do |name|
|
|
175
|
+
interval = @config[:intervals][name].to_f
|
|
176
|
+
next if !interval || interval == 0.0
|
|
177
|
+
@timers[name] = EM.add_periodic_timer(interval, method("work_#{name}"))
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def stop_timers
|
|
182
|
+
@timers.each {|n, t| EM.cancel_timer t }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# initiate epoll with given file descriptor and set effective user
|
|
186
|
+
def epoll_init
|
|
187
|
+
log.info { "EPOLL: Available file descriptors: " +
|
|
188
|
+
EM.set_descriptor_table_size(@config[:epoll_limit]).to_s }
|
|
189
|
+
if @config[:epoll_user]
|
|
190
|
+
EM.set_effective_user(@config[:epoll_user])
|
|
191
|
+
log.info { "EPOLL: Effective user set to: #{@config[:epoll_user]}" }
|
|
192
|
+
end
|
|
193
|
+
EM.epoll = true
|
|
194
|
+
end
|
|
195
|
+
|
|
135
196
|
def run
|
|
136
197
|
@started = Time.now
|
|
137
198
|
|
|
@@ -140,61 +201,90 @@ module Bitcoin::Network
|
|
|
140
201
|
log.info { "Bye" }
|
|
141
202
|
end
|
|
142
203
|
|
|
143
|
-
|
|
204
|
+
# enable kqueue (BSD, OS X)
|
|
205
|
+
if EM.kqueue?
|
|
206
|
+
log.info { 'Using BSD kqueue' }
|
|
207
|
+
EM.kqueue = true
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# enable epoll (Linux)
|
|
211
|
+
if EM.epoll?
|
|
212
|
+
log.info { 'Using Linux epoll' }
|
|
213
|
+
epoll_init
|
|
214
|
+
end
|
|
144
215
|
|
|
145
216
|
EM.run do
|
|
146
|
-
[:addrs, :connect, :relay].each do |name|
|
|
147
|
-
interval = @config[:intervals][name]
|
|
148
|
-
next if !interval || interval == 0
|
|
149
|
-
@timers[name] = EM.add_periodic_timer(interval, method("work_#{name}"))
|
|
150
|
-
end
|
|
151
217
|
|
|
152
|
-
|
|
153
|
-
|
|
218
|
+
start_timers
|
|
219
|
+
|
|
220
|
+
host, port = *@config[:command]
|
|
221
|
+
port ||= Bitcoin.network[:default_port]
|
|
222
|
+
if host
|
|
223
|
+
log.debug { "Trying to bind command socket to #{host}:#{port}" }
|
|
154
224
|
EM.start_server(host, port, CommandHandler, self)
|
|
155
225
|
log.info { "Command socket listening on #{host}:#{port}" }
|
|
156
226
|
end
|
|
157
227
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
228
|
+
host, port = *@config[:listen]
|
|
229
|
+
port ||= Bitcoin.network[:default_port]
|
|
230
|
+
if host
|
|
231
|
+
log.debug { "Trying to bind server socket to #{host}:#{port}" }
|
|
232
|
+
EM.start_server(host, port.to_i, ConnectionHandler, self, host, port.to_i, :in)
|
|
161
233
|
log.info { "Server socket listening on #{host}:#{port}" }
|
|
162
234
|
end
|
|
163
235
|
|
|
164
|
-
|
|
165
|
-
|
|
236
|
+
@config[:connect].each do |host, port|
|
|
237
|
+
port ||= Bitcoin.network[:default_port]
|
|
238
|
+
connect_peer(host, port)
|
|
239
|
+
log.info { "Connecting to #{host}:#{port}" }
|
|
166
240
|
end
|
|
167
241
|
|
|
168
242
|
work_connect if @addrs.any?
|
|
169
243
|
connect_dns if @config[:dns]
|
|
170
|
-
|
|
171
|
-
|
|
244
|
+
|
|
245
|
+
Signal.trap("INT") do
|
|
246
|
+
puts "Shutting down. You can force-quit by pressing Ctrl-C again, but it might corrupt your database!"
|
|
247
|
+
Signal.trap("INT") do
|
|
248
|
+
puts "Force Quit"
|
|
249
|
+
exit 1
|
|
250
|
+
end
|
|
251
|
+
self.stop
|
|
252
|
+
end
|
|
253
|
+
|
|
172
254
|
end
|
|
173
255
|
end
|
|
174
256
|
|
|
175
257
|
# connect to peer at given +host+ / +port+
|
|
176
258
|
def connect_peer host, port
|
|
177
|
-
return if @connections.map{|c| c.host}.include?(host)
|
|
178
|
-
log.
|
|
179
|
-
EM.connect(host, port.to_i, ConnectionHandler, self, host, port.to_i)
|
|
259
|
+
return if @connections.map{|c| c.host }.include?(host)
|
|
260
|
+
log.debug { "Attempting to connect to #{host}:#{port}" }
|
|
261
|
+
EM.connect(host, port.to_i, ConnectionHandler, self, host, port.to_i, :out)
|
|
180
262
|
rescue
|
|
181
|
-
log.
|
|
263
|
+
log.debug { "Error connecting to #{host}:#{port}" }
|
|
182
264
|
log.debug { $!.inspect }
|
|
183
265
|
end
|
|
184
266
|
|
|
185
267
|
# query addrs from dns seed and connect
|
|
186
268
|
def connect_dns
|
|
187
269
|
unless Bitcoin.network[:dns_seeds].any?
|
|
188
|
-
|
|
270
|
+
log.warn { "No DNS seed nodes available" }
|
|
271
|
+
return connect_known_peers
|
|
189
272
|
end
|
|
190
273
|
connect_dns_resolver(Bitcoin.network[:dns_seeds].sample) do |addrs|
|
|
191
274
|
log.debug { "DNS returned addrs: #{addrs.inspect}" }
|
|
192
|
-
addrs.sample(@config[:max][:
|
|
275
|
+
addrs.sample(@config[:max][:connections_out] / 2).uniq.each do |addr|
|
|
193
276
|
connect_peer(addr, Bitcoin.network[:default_port])
|
|
194
277
|
end
|
|
195
278
|
end
|
|
196
279
|
end
|
|
197
280
|
|
|
281
|
+
def connect_known_peers
|
|
282
|
+
log.debug { "Attempting to connecting to known nodes" }
|
|
283
|
+
Bitcoin.network[:known_nodes].shuffle[0..3].each do |node|
|
|
284
|
+
connect_peer node, Bitcoin.network[:default_port]
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
198
288
|
# get peer addrs from given dns +seed+ using em/dns_resolver.
|
|
199
289
|
# fallback to using `nslookup` if it is not installed or fails.
|
|
200
290
|
def connect_dns_resolver(seed)
|
|
@@ -225,7 +315,7 @@ module Bitcoin::Network
|
|
|
225
315
|
# establish new ones if needed
|
|
226
316
|
def work_connect
|
|
227
317
|
log.debug { "Connect worker running" }
|
|
228
|
-
desired = @config[:max][:
|
|
318
|
+
desired = @config[:max][:connections_out] - @connections.select(&:outgoing?).size
|
|
229
319
|
return if desired <= 0
|
|
230
320
|
desired = 32 if desired > 32 # connect to max 32 peers at once
|
|
231
321
|
if addrs.any?
|
|
@@ -246,9 +336,10 @@ module Bitcoin::Network
|
|
|
246
336
|
peer = @connections.select(&:connected?).sample
|
|
247
337
|
return unless peer
|
|
248
338
|
log.info { "querying blocks from #{peer.host}:#{peer.port}" }
|
|
249
|
-
|
|
339
|
+
case @config[:mode]
|
|
340
|
+
when /lite/
|
|
250
341
|
peer.send_getheaders locator unless @queue.size >= @config[:max][:queue]
|
|
251
|
-
|
|
342
|
+
when /full|pruned/
|
|
252
343
|
peer.send_getblocks locator unless @inv_queue.size >= @config[:max][:inv]
|
|
253
344
|
end
|
|
254
345
|
end
|
|
@@ -268,87 +359,121 @@ module Bitcoin::Network
|
|
|
268
359
|
# check for new items in the queue and process them
|
|
269
360
|
def work_queue
|
|
270
361
|
@log.debug { "queue worker running" }
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
362
|
+
return getblocks if @queue.size == 0
|
|
363
|
+
|
|
364
|
+
# switch off utxo cache once there aren't tons of new blocks coming in
|
|
365
|
+
if @store.in_sync?
|
|
366
|
+
if @store.is_a?(Bitcoin::Storage::Backends::UtxoStore) && @store.config[:utxo_cache] > 0
|
|
367
|
+
log.debug { "switching off utxo cache" }
|
|
368
|
+
@store.config[:utxo_cache] = 0
|
|
275
369
|
end
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
370
|
+
@config[:intervals].each do |name, value|
|
|
371
|
+
if value <= 1
|
|
372
|
+
log.debug { "setting #{name} interval to 5 seconds" }
|
|
373
|
+
@config[:intervals][name] = 5
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
while obj = @queue.shift
|
|
379
|
+
begin
|
|
380
|
+
if obj[0].to_sym == :block
|
|
381
|
+
if res = @store.send("new_#{obj[0]}", obj[1])
|
|
382
|
+
if res[1] == 0 && obj[1].hash == @store.get_head.hash
|
|
383
|
+
@last_block_time = Time.now
|
|
384
|
+
push_notification(:block, [obj[1], res[0]])
|
|
385
|
+
obj[1].tx.each {|tx| @unconfirmed.delete(tx.hash) }
|
|
386
|
+
end
|
|
387
|
+
getblocks if res[1] == 2 && @store.in_sync?
|
|
388
|
+
end
|
|
389
|
+
else
|
|
390
|
+
drop = @unconfirmed.size - @config[:max][:unconfirmed] + 1
|
|
391
|
+
drop.times { @unconfirmed.shift } if drop > 0
|
|
392
|
+
unless @unconfirmed[obj[1].hash]
|
|
393
|
+
@unconfirmed[obj[1].hash] = obj[1]
|
|
394
|
+
push_notification(:tx, [obj[1], 0])
|
|
395
|
+
|
|
396
|
+
if @notifiers[:output]
|
|
397
|
+
obj[1].out.each do |out|
|
|
398
|
+
address = Bitcoin::Script.new(out.pk_script).get_address
|
|
399
|
+
push_notification(:output, [obj[1].hash, address, out.value, 0])
|
|
400
|
+
end
|
|
284
401
|
end
|
|
285
402
|
end
|
|
286
|
-
rescue
|
|
287
|
-
@log.warn { $!.inspect }
|
|
288
|
-
puts *$@
|
|
289
403
|
end
|
|
404
|
+
rescue Bitcoin::Validation::ValidationError
|
|
405
|
+
@log.warn { "ValiationError storing #{obj[0]} #{obj[1].hash}: #{$!.message}" }
|
|
406
|
+
# File.open("./validation_error_#{obj[0]}_#{obj[1].hash}.bin", "w") {|f|
|
|
407
|
+
# f.write(obj[1].to_payload) }
|
|
408
|
+
# EM.stop
|
|
409
|
+
rescue
|
|
410
|
+
@log.warn { $!.inspect }
|
|
411
|
+
puts *$@
|
|
290
412
|
end
|
|
291
|
-
@in_sync = (@store.get_head && (Time.now - @store.get_head.time).to_i < 3600) ? true : false
|
|
292
413
|
end
|
|
293
414
|
end
|
|
294
415
|
|
|
295
416
|
# check for new items in the inv queue and process them,
|
|
296
417
|
# unless the queue is already full
|
|
297
418
|
def work_inv_queue
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
next if !@in_sync && inv[0] == :tx
|
|
306
|
-
next if @queue.map{|i|i[1]}.map(&:hash).include?(inv[1])
|
|
307
|
-
# next if @store.send("has_#{inv[0]}", inv[1])
|
|
308
|
-
inv[2].send("send_getdata_#{inv[0]}", inv[1])
|
|
309
|
-
end
|
|
310
|
-
end
|
|
419
|
+
@log.debug { "inv queue worker running" }
|
|
420
|
+
return if @inv_queue.size == 0
|
|
421
|
+
return if @queue.size >= @config[:max][:queue]
|
|
422
|
+
while inv = @inv_queue.shift
|
|
423
|
+
next if !@store.in_sync? && inv[0] == :tx && @notifiers.empty?
|
|
424
|
+
next if @queue.map{|i|i[1]}.map(&:hash).include?(inv[1])
|
|
425
|
+
inv[2].send("send_getdata_#{inv[0]}", inv[1])
|
|
311
426
|
end
|
|
312
427
|
end
|
|
313
428
|
|
|
314
429
|
# queue inv, caching the most current ones
|
|
315
430
|
def queue_inv inv
|
|
316
|
-
|
|
317
|
-
return if @
|
|
318
|
-
@inv_queue.size >= @config[:max][:inv] ||
|
|
319
|
-
(!@in_sync && inv[0] == :tx)
|
|
320
|
-
@inv_cache << [inv[0], inv[1]]
|
|
321
|
-
@inv_queue << inv
|
|
322
|
-
end
|
|
431
|
+
hash = inv[1].unpack("H*")[0]
|
|
432
|
+
return if @inv_queue.include?(inv) || @queue.select {|i| i[1].hash == hash }.any?
|
|
323
433
|
|
|
434
|
+
return if @store.send("has_#{inv[0]}", hash)
|
|
324
435
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
log.info { "EPOLL: Effective user set to: #{@config[:epoll_user]}" }
|
|
332
|
-
end
|
|
333
|
-
EM.epoll
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def relay_tx(tx)
|
|
337
|
-
return false unless @in_sync
|
|
338
|
-
@store.store_tx(tx)
|
|
339
|
-
@connections.select(&:connected?).sample((@connections.size / 2) + 1).each do |peer|
|
|
340
|
-
peer.send_inv(:tx, tx)
|
|
341
|
-
end
|
|
436
|
+
# @inv_cache.shift(128) if @inv_cache.size > @config[:max][:inv_cache]
|
|
437
|
+
# return if @inv_cache.include?([inv[0], inv[1]]) ||
|
|
438
|
+
# @inv_queue.size >= @config[:max][:inv] ||
|
|
439
|
+
# (!@store.in_sync? && inv[0] == :tx)
|
|
440
|
+
# @inv_cache << [inv[0], inv[1]]
|
|
441
|
+
@inv_queue << inv
|
|
342
442
|
end
|
|
343
443
|
|
|
344
444
|
def work_relay
|
|
345
445
|
log.debug { "relay worker running" }
|
|
346
446
|
@store.get_unconfirmed_tx.each do |tx|
|
|
347
|
-
log.info { "relaying tx #{tx.hash}" }
|
|
348
447
|
relay_tx(tx)
|
|
349
448
|
end
|
|
350
449
|
end
|
|
351
450
|
|
|
451
|
+
# get the external ip that was suggested in version messages
|
|
452
|
+
# from other peers most often.
|
|
453
|
+
def external_ip
|
|
454
|
+
@external_ips.group_by(&:dup).values.max_by(&:size).first
|
|
455
|
+
rescue
|
|
456
|
+
@config[:listen][0]
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# push notification +message+ to +channel+
|
|
460
|
+
def push_notification channel, message
|
|
461
|
+
@notifiers[channel.to_sym].push(message) if @notifiers[channel.to_sym]
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# subscribe to notification +channel+.
|
|
465
|
+
# available channels are: block, tx, output, connection.
|
|
466
|
+
# see CommandHandler for details.
|
|
467
|
+
def subscribe channel
|
|
468
|
+
@notifiers[channel.to_sym] ||= EM::Channel.new
|
|
469
|
+
@notifiers[channel.to_sym].subscribe {|*data| yield(*data) }
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# should the node accept new incoming connections?
|
|
473
|
+
def accept_connections?
|
|
474
|
+
connections.select(&:incoming?).size >= config[:max][:connections_in]
|
|
475
|
+
end
|
|
476
|
+
|
|
352
477
|
end
|
|
353
478
|
end
|
|
354
479
|
|