zold 0.13.34 → 0.13.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +6 -5
- data/bin/zold +6 -1
- data/fixtures/scripts/_head.sh +4 -2
- data/fixtures/scripts/push-and-pull.sh +1 -1
- data/lib/zold/amount.rb +5 -0
- data/lib/zold/atomic_file.rb +3 -3
- data/lib/zold/backtrace.rb +7 -1
- data/lib/zold/commands/clean.rb +1 -1
- data/lib/zold/commands/create.rb +2 -2
- data/lib/zold/commands/diff.rb +2 -2
- data/lib/zold/commands/fetch.rb +8 -4
- data/lib/zold/commands/merge.rb +3 -3
- data/lib/zold/commands/node.rb +32 -11
- data/lib/zold/commands/propagate.rb +1 -1
- data/lib/zold/commands/push.rb +14 -9
- data/lib/zold/commands/remote.rb +4 -2
- data/lib/zold/commands/routines/bonuses.rb +9 -5
- data/lib/zold/commands/routines/spread.rb +4 -1
- data/lib/zold/commands/show.rb +2 -2
- data/lib/zold/commands/taxes.rb +2 -1
- data/lib/zold/http.rb +9 -1
- data/lib/zold/id.rb +4 -0
- data/lib/zold/json_page.rb +43 -0
- data/lib/zold/metronome.rb +15 -15
- data/lib/zold/node/async_entrance.rb +81 -0
- data/lib/zold/node/entrance.rb +26 -91
- data/lib/zold/node/farm.rb +21 -15
- data/lib/zold/node/front.rb +30 -17
- data/lib/zold/node/safe_entrance.rb +78 -0
- data/lib/zold/node/spread_entrance.rb +105 -0
- data/lib/zold/patch.rb +7 -5
- data/lib/zold/remotes.rb +19 -7
- data/lib/zold/txn.rb +3 -1
- data/lib/zold/version.rb +1 -1
- data/lib/zold/wallet.rb +3 -3
- data/lib/zold/wallets.rb +2 -0
- data/test/commands/routines/test_bonuses.rb +2 -2
- data/test/commands/test_fetch.rb +9 -35
- data/test/commands/test_merge.rb +1 -1
- data/test/commands/test_node.rb +4 -2
- data/test/commands/test_push.rb +4 -2
- data/test/node/fake_entrance.rb +41 -0
- data/test/node/fake_node.rb +37 -29
- data/test/node/test_async_entrance.rb +88 -0
- data/test/node/test_entrance.rb +1 -19
- data/test/node/test_front.rb +3 -2
- data/test/node/test_safe_entrance.rb +56 -0
- data/test/node/test_spread_entrance.rb +60 -0
- data/test/test__helper.rb +0 -1
- data/test/test_atomic_file.rb +39 -0
- data/test/test_backtrace.rb +40 -0
- data/test/test_metronome.rb +0 -1
- data/test/test_patch.rb +17 -0
- data/test/test_remotes.rb +21 -0
- data/test/test_wallet.rb +14 -1
- data/test/test_zold.rb +1 -0
- metadata +16 -2
data/lib/zold/node/farm.rb
CHANGED
@@ -62,6 +62,7 @@ module Zold
|
|
62
62
|
threads: @threads.map do |t|
|
63
63
|
"#{t.name}/#{t.status}/#{t.alive? ? 'A' : 'D'}"
|
64
64
|
end.join(', '),
|
65
|
+
cleanup: @cleanup.status,
|
65
66
|
pipeline: @pipeline.size,
|
66
67
|
best: best.map(&:to_mnemo).join(', ')
|
67
68
|
}
|
@@ -81,9 +82,10 @@ module Zold
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
end
|
84
|
-
|
85
|
+
alive = true
|
86
|
+
@cleanup = Thread.new do
|
85
87
|
Thread.current.name = 'cleanup'
|
86
|
-
|
88
|
+
while alive
|
87
89
|
sleep(60) unless strength == 1 # which will only happen in tests
|
88
90
|
VerboseThread.new(@log).run do
|
89
91
|
cleanup(host, port, strength, threads)
|
@@ -93,21 +95,25 @@ module Zold
|
|
93
95
|
@log.info("Farm started with #{@threads.count} threads at #{host}:#{port}, strength is #{strength}")
|
94
96
|
return unless block_given?
|
95
97
|
begin
|
96
|
-
yield
|
98
|
+
yield(self)
|
97
99
|
ensure
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
@
|
100
|
+
@log.info("Terminating the farm with #{@threads.count} threads...")
|
101
|
+
start = Time.now
|
102
|
+
alive = false
|
103
|
+
if strength == 1
|
104
|
+
@cleanup.join
|
105
|
+
@log.info("Cleanup thread finished in #{(Time.now - start).round(2)}s")
|
106
|
+
else
|
107
|
+
@cleanup.exit
|
108
|
+
@log.info("Cleanup thread killed in #{(Time.now - start).round(2)}s")
|
109
|
+
end
|
110
|
+
@threads.each do |t|
|
111
|
+
tstart = Time.now
|
112
|
+
t.exit
|
113
|
+
@log.info("Thread #{t.name} terminated in #{(Time.now - tstart).round(2)}s")
|
114
|
+
end
|
115
|
+
@log.info("Farm stopped in #{(Time.now - start).round(2)}s")
|
109
116
|
end
|
110
|
-
@log.info("Farm stopped in #{(Time.now - start).round(2)}s")
|
111
117
|
end
|
112
118
|
|
113
119
|
private
|
data/lib/zold/node/front.rb
CHANGED
@@ -43,7 +43,7 @@ module Zold
|
|
43
43
|
configure do
|
44
44
|
set :bind, '0.0.0.0'
|
45
45
|
set :suppress_messages, true
|
46
|
-
set :dump_errors,
|
46
|
+
set :dump_errors, true
|
47
47
|
set :start, Time.now
|
48
48
|
set :lock, false
|
49
49
|
set :show_exceptions, false
|
@@ -58,6 +58,7 @@ module Zold
|
|
58
58
|
set :farm, nil? # to be injected at node.rb
|
59
59
|
set :metronome, nil? # to be injected at node.rb
|
60
60
|
set :entrance, nil? # to be injected at node.rb
|
61
|
+
set :network, nil? # to be injected at node.rb
|
61
62
|
set :wallets, nil? # to be injected at node.rb
|
62
63
|
set :remotes, nil? # to be injected at node.rb
|
63
64
|
set :copies, nil? # to be injected at node.rb
|
@@ -65,22 +66,26 @@ module Zold
|
|
65
66
|
use Rack::Deflater
|
66
67
|
|
67
68
|
before do
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
settings.log.debug("#{request.url}: we are in standalone mode, won't update remotes")
|
69
|
+
check_header(Http::NETWORK_HEADER) do |header|
|
70
|
+
if header != settings.network
|
71
|
+
raise "Network name mismatch, you are in '#{header}', we are in '#{settings.network}'"
|
72
|
+
end
|
73
73
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
74
|
+
check_header(Http::SCORE_HEADER) do |header|
|
75
|
+
if settings.remotes.all.empty?
|
76
|
+
settings.log.debug("#{request.url}: we are in standalone mode, won't update remotes")
|
77
|
+
end
|
78
|
+
s = Score.parse_text(header)
|
79
|
+
error(400, 'The score is invalid') unless s.valid?
|
80
|
+
error(400, 'The score is weak') if s.strength < Score::STRENGTH && !settings.ignore_score_weakness
|
81
|
+
if s.value > 3
|
82
|
+
require_relative '../commands/remote'
|
83
|
+
Remote.new(remotes: settings.remotes, log: settings.log).run(
|
84
|
+
['remote', 'add', s.host, s.port.to_s, '--force']
|
85
|
+
)
|
86
|
+
else
|
87
|
+
settings.log.debug("#{request.url}: the score is too weak: #{s}")
|
88
|
+
end
|
84
89
|
end
|
85
90
|
end
|
86
91
|
|
@@ -121,6 +126,7 @@ module Zold
|
|
121
126
|
content_type 'application/json'
|
122
127
|
JSON.pretty_generate(
|
123
128
|
version: settings.version,
|
129
|
+
network: settings.network,
|
124
130
|
score: score.to_h,
|
125
131
|
pid: Process.pid,
|
126
132
|
cpus: Concurrent.processor_count,
|
@@ -181,7 +187,7 @@ module Zold
|
|
181
187
|
)
|
182
188
|
end
|
183
189
|
settings.log.info("Wallet #{id} is new: #{before.length}b != #{after.length}b")
|
184
|
-
settings.entrance.push(id, after
|
190
|
+
settings.entrance.push(id, after)
|
185
191
|
JSON.pretty_generate(
|
186
192
|
version: settings.version,
|
187
193
|
score: score.to_h
|
@@ -228,6 +234,13 @@ module Zold
|
|
228
234
|
|
229
235
|
private
|
230
236
|
|
237
|
+
def check_header(name)
|
238
|
+
name = "HTTP-#{name}".upcase.tr('-', '_')
|
239
|
+
header = request.env[name]
|
240
|
+
return unless header
|
241
|
+
yield header
|
242
|
+
end
|
243
|
+
|
231
244
|
def score
|
232
245
|
best = settings.farm.best
|
233
246
|
raise 'Score is empty, there is something wrong with the Farm!' if best.empty?
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Copyright (c) 2018 Yegor Bugayenko
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
# SOFTWARE.
|
20
|
+
|
21
|
+
require 'concurrent'
|
22
|
+
require 'tempfile'
|
23
|
+
require_relative 'emission'
|
24
|
+
require_relative '../log'
|
25
|
+
require_relative '../remotes'
|
26
|
+
require_relative '../copies'
|
27
|
+
require_relative '../tax'
|
28
|
+
require_relative '../commands/merge'
|
29
|
+
require_relative '../commands/fetch'
|
30
|
+
require_relative '../commands/push'
|
31
|
+
|
32
|
+
# The entrance thav validate the incoming wallet first.
|
33
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
34
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
35
|
+
# License:: MIT
|
36
|
+
module Zold
|
37
|
+
# The safe entrance
|
38
|
+
class SafeEntrance
|
39
|
+
def initialize(entrance, network: 'test')
|
40
|
+
raise 'Entrance can\'t be nil' if entrance.nil?
|
41
|
+
@entrance = entrance
|
42
|
+
raise 'Network can\'t be nil' if network.nil?
|
43
|
+
@network = network
|
44
|
+
end
|
45
|
+
|
46
|
+
def start
|
47
|
+
@entrance.start { yield(self) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_json
|
51
|
+
@entrance.to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns a list of modifed wallets (as Zold::Id)
|
55
|
+
def push(id, body)
|
56
|
+
raise 'Id can\'t be nil' if id.nil?
|
57
|
+
raise 'Id must be of type Id' unless id.is_a?(Id)
|
58
|
+
raise 'Body can\'t be nil' if body.nil?
|
59
|
+
Tempfile.open do |f|
|
60
|
+
File.write(f.path, body)
|
61
|
+
wallet = Wallet.new(f)
|
62
|
+
unless wallet.network == @network
|
63
|
+
raise "The network name mismatch, the wallet is in '#{wallet.network}', we are in '#{@network}'"
|
64
|
+
end
|
65
|
+
balance = wallet.balance
|
66
|
+
if balance.negative? && !wallet.root?
|
67
|
+
raise "The balance #{balance} of #{wallet.id} is negative and it's not a root wallet"
|
68
|
+
end
|
69
|
+
Emission.new(wallet).check
|
70
|
+
tax = Tax.new(wallet)
|
71
|
+
if tax.in_debt?
|
72
|
+
raise "Taxes are not paid, can't accept the wallet; the debt is #{tax.debt} (#{tax.debt.to_i} zents)"
|
73
|
+
end
|
74
|
+
@entrance.push(id, body)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright (c) 2018 Yegor Bugayenko
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
# SOFTWARE.
|
20
|
+
|
21
|
+
require 'concurrent'
|
22
|
+
require 'tempfile'
|
23
|
+
require_relative 'emission'
|
24
|
+
require_relative '../log'
|
25
|
+
require_relative '../remotes'
|
26
|
+
require_relative '../copies'
|
27
|
+
require_relative '../tax'
|
28
|
+
require_relative '../commands/merge'
|
29
|
+
require_relative '../commands/fetch'
|
30
|
+
require_relative '../commands/push'
|
31
|
+
require_relative '../commands/clean'
|
32
|
+
|
33
|
+
# The entrance that spreads what's been modified.
|
34
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
35
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
36
|
+
# License:: MIT
|
37
|
+
module Zold
|
38
|
+
# The entrance
|
39
|
+
class SpreadEntrance
|
40
|
+
def initialize(entrance, wallets, remotes, address, log: Log::Quiet.new, ignore_score_weakeness: false)
|
41
|
+
raise 'Entrance can\'t be nil' if entrance.nil?
|
42
|
+
@entrance = entrance
|
43
|
+
raise 'Wallets can\'t be nil' if wallets.nil?
|
44
|
+
raise 'Wallets must be of type Wallets' unless wallets.is_a?(Wallets)
|
45
|
+
@wallets = wallets
|
46
|
+
raise 'Remotes can\'t be nil' if remotes.nil?
|
47
|
+
raise 'Remotes must be of type Remotes' unless remotes.is_a?(Remotes)
|
48
|
+
@remotes = remotes
|
49
|
+
raise 'Address can\'t be nil' if address.nil?
|
50
|
+
@address = address
|
51
|
+
raise 'Log can\'t be nil' if log.nil?
|
52
|
+
@log = log
|
53
|
+
@ignore_score_weakeness = ignore_score_weakeness
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_json
|
57
|
+
@entrance.to_json.merge(
|
58
|
+
'modified': @modified.size,
|
59
|
+
'push': @push.status
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def start
|
64
|
+
@entrance.start do
|
65
|
+
@seen = Set.new
|
66
|
+
@modified = Queue.new
|
67
|
+
@push = Thread.start do
|
68
|
+
Thread.current.abort_on_exception = true
|
69
|
+
Thread.current.name = 'push'
|
70
|
+
VerboseThread.new(@log).run do
|
71
|
+
loop do
|
72
|
+
id = @modified.pop
|
73
|
+
if @remotes.all.empty?
|
74
|
+
@log.info("There are no remotes, won\'t spread #{id}")
|
75
|
+
else
|
76
|
+
Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
|
77
|
+
['push', "--ignore-node=#{@address}", id.to_s] +
|
78
|
+
(@ignore_score_weakeness ? ['--ignore-score-weakness'] : [])
|
79
|
+
)
|
80
|
+
end
|
81
|
+
@seen.delete(id)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
begin
|
86
|
+
yield(self)
|
87
|
+
ensure
|
88
|
+
@log.info('Waiting for spread entrance to finish...')
|
89
|
+
@modified.clear
|
90
|
+
@push.exit
|
91
|
+
@log.info('Spread entrance finished, thread killed')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def push(id, body)
|
97
|
+
@entrance.push(id, body).each do |m|
|
98
|
+
next if @seen.include?(m)
|
99
|
+
@seen << m
|
100
|
+
@modified.push(m)
|
101
|
+
@log.debug("Push scheduled for #{m}, queue size is #{@modified.size}")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/zold/patch.rb
CHANGED
@@ -66,14 +66,15 @@ module Zold
|
|
66
66
|
next if @txns.find { |t| t == txn }
|
67
67
|
if txn.amount.negative?
|
68
68
|
if txn.id <= max
|
69
|
-
@log.error("Transaction ID is
|
69
|
+
@log.error("Transaction ID is not greater than max ID #{max}: #{txn.to_text}")
|
70
70
|
next
|
71
71
|
end
|
72
|
-
|
73
|
-
|
72
|
+
dup = @txns.find { |t| t.id == txn.id }
|
73
|
+
if dup
|
74
|
+
@log.error("An attempt to overwrite #{dup.to_text} with this: #{txn.to_text}")
|
74
75
|
next
|
75
76
|
end
|
76
|
-
if
|
77
|
+
if @txns.map(&:amount).map(&:to_i).inject(&:+).to_i < txn.amount.to_i * -1 && !wallet.root?
|
77
78
|
@log.error("Transaction ##{txn.id} attempts to make the balance negative: #{txn.to_text}")
|
78
79
|
next
|
79
80
|
end
|
@@ -92,7 +93,8 @@ module Zold
|
|
92
93
|
next
|
93
94
|
end
|
94
95
|
unless payer.has?(txn.id, wallet.id)
|
95
|
-
@log.error("Paying wallet #{
|
96
|
+
@log.error("Paying wallet #{txn.bnf} doesn't have transaction ##{txn.id} \
|
97
|
+
among #{payer.txns.count} transactions: #{txn.to_text}")
|
96
98
|
next
|
97
99
|
end
|
98
100
|
end
|
data/lib/zold/remotes.rb
CHANGED
@@ -20,6 +20,7 @@
|
|
20
20
|
|
21
21
|
require 'csv'
|
22
22
|
require 'uri'
|
23
|
+
require 'time'
|
23
24
|
require 'fileutils'
|
24
25
|
require_relative 'backtrace'
|
25
26
|
require_relative 'node/farm'
|
@@ -39,7 +40,11 @@ module Zold
|
|
39
40
|
TOLERANCE = 8
|
40
41
|
|
41
42
|
# Empty, for standalone mode
|
42
|
-
class Empty
|
43
|
+
class Empty < Remotes
|
44
|
+
def initialize
|
45
|
+
# Nothing here
|
46
|
+
end
|
47
|
+
|
43
48
|
def all
|
44
49
|
[]
|
45
50
|
end
|
@@ -52,7 +57,7 @@ module Zold
|
|
52
57
|
# One remote.
|
53
58
|
class Remote
|
54
59
|
attr_reader :host, :port
|
55
|
-
def initialize(host, port, score, idx, log: Log::Quiet.new)
|
60
|
+
def initialize(host, port, score, idx, log: Log::Quiet.new, network: 'test')
|
56
61
|
@host = host
|
57
62
|
raise 'Post must be Integer' unless port.is_a?(Integer)
|
58
63
|
@port = port
|
@@ -60,11 +65,13 @@ module Zold
|
|
60
65
|
@score = score
|
61
66
|
raise 'Idx must be of type Integer' unless idx.is_a?(Integer)
|
62
67
|
@idx = idx
|
68
|
+
raise 'Network can\'t be nil' if network.nil?
|
69
|
+
@network = network
|
63
70
|
@log = log
|
64
71
|
end
|
65
72
|
|
66
73
|
def http(path = '/')
|
67
|
-
Http.new("http://#{@host}:#{@port}#{path}", @score)
|
74
|
+
Http.new("http://#{@host}:#{@port}#{path}", @score, network: @network)
|
68
75
|
end
|
69
76
|
|
70
77
|
def to_s
|
@@ -90,7 +97,7 @@ module Zold
|
|
90
97
|
end
|
91
98
|
|
92
99
|
def assert_score_strength(score)
|
93
|
-
raise "Score is too weak
|
100
|
+
raise "Score #{score.strength} is too weak (<#{Score::STRENGTH}): #{score}" if score.strength < Score::STRENGTH
|
94
101
|
end
|
95
102
|
|
96
103
|
def assert_score_value(score, min)
|
@@ -98,8 +105,11 @@ module Zold
|
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
101
|
-
def initialize(file)
|
108
|
+
def initialize(file, network: 'test')
|
109
|
+
raise 'File can\'t be nil' if file.nil?
|
102
110
|
@file = file
|
111
|
+
raise 'Network can\'t be nil' if network.nil?
|
112
|
+
@network = network
|
103
113
|
end
|
104
114
|
|
105
115
|
def all
|
@@ -163,15 +173,17 @@ module Zold
|
|
163
173
|
best = farm.best[0]
|
164
174
|
require_relative 'score'
|
165
175
|
score = best.nil? ? Score::ZERO : best
|
176
|
+
start = Time.now
|
166
177
|
idx = 0
|
167
178
|
all.each do |r|
|
168
179
|
begin
|
169
|
-
yield Remotes::Remote.new(r[:host], r[:port], score, idx, log: log)
|
180
|
+
yield Remotes::Remote.new(r[:host], r[:port], score, idx, log: log, network: @network)
|
170
181
|
idx += 1
|
171
182
|
rescue StandardError => e
|
172
183
|
error(r[:host], r[:port])
|
173
184
|
errors = errors(r[:host], r[:port])
|
174
|
-
log.info("#{Rainbow("#{r[:host]}:#{r[:port]}").red}: #{e.message}
|
185
|
+
log.info("#{Rainbow("#{r[:host]}:#{r[:port]}").red}: #{e.message} \
|
186
|
+
in #{(Time.now - start).round}s; errors=#{errors}")
|
175
187
|
log.debug(Backtrace.new(e).to_s)
|
176
188
|
remove(r[:host], r[:port]) if errors > Remotes::TOLERANCE
|
177
189
|
end
|
data/lib/zold/txn.rb
CHANGED
@@ -68,7 +68,9 @@ module Zold
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def ==(other)
|
71
|
-
id == other.id &&
|
71
|
+
id == other.id && date == other.date && amount == other.amount &&
|
72
|
+
prefix == other.prefix && bnf == other.bnf &&
|
73
|
+
details == other.details && sign == other.sign
|
72
74
|
end
|
73
75
|
|
74
76
|
def to_s
|