zold 0.14.35 → 0.14.36

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d432935c1080d120f3317e2ab42b61deec92b968
4
- data.tar.gz: e744e35fd2eeb18f6dc6249018c353d794888cf5
3
+ metadata.gz: e0ac9a7fc3634c563abae9dce0f68ca709d46490
4
+ data.tar.gz: 59bb7653c11021d3d46c6cab7124480c9db2a103
5
5
  SHA512:
6
- metadata.gz: 02f6761e39313a18d0739ab02f3a0fdb467c12acc2efcc14a1e18815ef4f2afa2453b77ef3c2ab1d4ccc6cfa8c6648390527007595d19f662ac54a9474d08867
7
- data.tar.gz: ab94bcf9c33d7721fa47886e96883dfa1b841478dc4b01944422de03c4b27462f6904689f44691d5b7ea49ec418ae1916e45d6f18f1b41322a3b6e8d9d363187
6
+ metadata.gz: cc4d99b314585fc077c82779463f09505d71f787a560087a2d55f3ab17184580df1aa0f12de8e0afb49914a0f2bff5d5064f343d579a9fe0d289562cacf94efe
7
+ data.tar.gz: 1995bae0601aa6cd84b5de1f3eeb58b27914f2d4d2935a503fa5226c88d12eaa659a6a76bcac58dae8b7103dac813ba90be1e66597490af4bf899e8f77ead092
data/bin/zold CHANGED
@@ -113,6 +113,7 @@ Available options:"
113
113
  default: Zold::Wallet::MAIN_NETWORK
114
114
  o.bool '-h', '--help', 'Show these instructions'
115
115
  o.bool '--trace', 'Show full stack trace in case of a problem'
116
+ o.bool '--skip-upgrades', 'Don\'t upgrade the storage', default: false
116
117
  o.bool '--ignore-global-config', 'Don\'t read options from the ~/.zold file'
117
118
  o.on '--no-colors', 'Disable colors in the ouput' do
118
119
  Rainbow.enabled = false
@@ -146,17 +147,18 @@ Available options:"
146
147
 
147
148
  zoldata = File.expand_path(File.join(Dir.pwd, '.zoldata'))
148
149
 
149
- Zold::Upgrades.new(Zold::VersionFile.new(File.join(zoldata, 'version')), 'upgrades').run
150
-
151
- # @todo #384:30min This is a workaround, move this code into Upgrades, somehow.
152
- # We should find a way to run these arbitrary scripts and pass arguments
153
- # to them. Each of them may need its own set of arguments.
154
- require_relative '../upgrades/2'
155
- Zold::UpgradeTo2.new(Dir.pwd, log).exec
156
- require_relative '../upgrades/protocol_up'
157
- Zold::ProtocolUp.new(Dir.pwd, log).exec
158
- require_relative '../upgrades/rename_foreign_wallets'
159
- Zold::RenameForeignWallets.new(Dir.pwd, opts['network'], log).exec
150
+ unless opts['skip-upgrades']
151
+ Zold::Upgrades.new(Zold::VersionFile.new(File.join(zoldata, 'version')), 'upgrades').run
152
+ # @todo #384:30min This is a workaround, move this code into Upgrades, somehow.
153
+ # We should find a way to run these arbitrary scripts and pass arguments
154
+ # to them. Each of them may need its own set of arguments.
155
+ require_relative '../upgrades/2'
156
+ Zold::UpgradeTo2.new(Dir.pwd, log).exec
157
+ require_relative '../upgrades/protocol_up'
158
+ Zold::ProtocolUp.new(Dir.pwd, log).exec
159
+ require_relative '../upgrades/rename_foreign_wallets'
160
+ Zold::RenameForeignWallets.new(Dir.pwd, opts['network'], log).exec
161
+ end
160
162
 
161
163
  wallets = Zold::SyncWallets.new(Zold::Wallets.new('.'), File.join(zoldata, 'locks'), log: log)
162
164
  remotes = Zold::Remotes.new(file: File.join(zoldata, 'remotes'), network: opts['network'])
@@ -48,6 +48,9 @@ Available options:"
48
48
  o.integer '--length',
49
49
  'The length of the invoice prefix (default: 8)',
50
50
  default: 8
51
+ o.string '--network',
52
+ 'The name of the network we work in',
53
+ default: 'test'
51
54
  o.bool '--help', 'Print instructions'
52
55
  end
53
56
  mine = Args.new(opts, @log).take || return
@@ -51,7 +51,7 @@ Available options:"
51
51
  private
52
52
 
53
53
  def calculate(opts)
54
- @log.info(Score.parse(opts.arguments[1]).next.to_s)
54
+ @log.info(Score.parse(opts.arguments.drop_while { |a| a.start_with?('--') }[1]).next.to_s)
55
55
  end
56
56
  end
57
57
  end
@@ -76,8 +76,8 @@ module Zold
76
76
  "The strength of the score (default: #{Score::STRENGTH})",
77
77
  default: Score::STRENGTH
78
78
  o.integer '--threads',
79
- 'How many threads to use for scores finding (default: 4)',
80
- default: 4
79
+ 'How many threads to use for scores finding (default: 2)',
80
+ default: 2
81
81
  o.bool '--dump-errors',
82
82
  'Make HTTP front-end errors visible in the log (false by default)',
83
83
  default: false
@@ -302,14 +302,17 @@ module Zold
302
302
 
303
303
  def metronome(farm, opts)
304
304
  metronome = Metronome.new(@log)
305
- return metronome if opts['no-metronome']
305
+ if opts['no-metronome']
306
+ @log.info('Metronome hasn\'t been started because of --no-metronome')
307
+ return metronome
308
+ end
306
309
  require_relative 'routines/spread'
307
310
  metronome.add(Routines::Spread.new(opts, @wallets, @remotes, log: @log))
308
311
  unless opts['standalone']
309
312
  require_relative 'routines/reconnect'
310
313
  metronome.add(Routines::Reconnect.new(opts, @remotes, farm, network: opts['network'], log: @log))
311
314
  end
312
- @log.info('Metronome created')
315
+ @log.info('Metronome started (use --no-metronome to disable it)')
313
316
  metronome
314
317
  end
315
318
 
data/lib/zold/http.rb CHANGED
@@ -66,12 +66,8 @@ module Zold
66
66
  # that is already taken care of in another issue. I am leaving a todo
67
67
  # to check that rubocop doesn't complain anymore, otherwise find another
68
68
  # solution
69
- attribute :uri, (Types::Class.constructor do |value|
70
- value.is_a?(URI) ? value : URI(value)
71
- end)
72
- attribute :score, (Types::Class.constructor do |value|
73
- value.nil? ? Score::ZERO : value
74
- end)
69
+ attribute :uri, (Types::Class.constructor { |v| v.is_a?(URI) ? v : URI(v) })
70
+ attribute :score, (Types::Class.constructor { |v| v.nil? ? Score::ZERO : v })
75
71
  attribute :network, Types::Strict::String.optional.default('test')
76
72
 
77
73
  def get
@@ -69,7 +69,7 @@ module Zold
69
69
  )
70
70
  AsyncEntrance::THREADS.times do |t|
71
71
  @pool.post do
72
- Thread.current.name = "async-#{t}"
72
+ Thread.current.name = "async-e##{t}"
73
73
  loop do
74
74
  VerboseThread.new(@log).run(true) { take }
75
75
  break if @pool.shuttingdown?
@@ -42,7 +42,7 @@ module Zold
42
42
  end
43
43
  end
44
44
 
45
- def initialize(invoice, cache, log: Log::Quiet.new)
45
+ def initialize(invoice, cache = File.join(Dir.pwd, 'farm'), log: Log::Quiet.new)
46
46
  @log = log
47
47
  @cache = cache
48
48
  @invoice = invoice
@@ -92,8 +92,15 @@ module Zold
92
92
  @cleanup = Thread.new do
93
93
  Thread.current.abort_on_exception = true
94
94
  Thread.current.name = 'cleanup'
95
- while @alive
96
- sleep(60) unless strength == 1 # which will only happen in tests
95
+ loop do
96
+ a = [0..600].take_while do
97
+ sleep 0.1
98
+ @alive
99
+ end
100
+ unless a.count == 600
101
+ @log.info("It's time to stop the cleanup thread...")
102
+ break
103
+ end
97
104
  VerboseThread.new(@log).run do
98
105
  cleanup(host, port, strength, threads)
99
106
  end
@@ -106,25 +113,31 @@ module Zold
106
113
  ensure
107
114
  @log.info("Terminating the farm with #{@threads.count} threads...")
108
115
  start = Time.now
109
- @alive = false
110
- if strength == 1
111
- @cleanup.join
112
- @log.info("Cleanup thread finished in #{(Time.now - start).round(2)}s")
113
- else
114
- @cleanup.exit
115
- @log.info("Cleanup thread killed in #{(Time.now - start).round(2)}s")
116
- end
117
- @threads.each do |t|
118
- tstart = Time.now
119
- t.join(0.1)
120
- @log.info("Thread #{t.name} finished in #{(Time.now - tstart).round(2)}s")
121
- end
116
+ finish(@cleanup)
117
+ @threads.each { |t| finish(t) }
122
118
  @log.info("Farm stopped in #{(Time.now - start).round(2)}s")
123
119
  end
124
120
  end
125
121
 
126
122
  private
127
123
 
124
+ def finish(thread)
125
+ start = Time.now
126
+ @alive = false
127
+ @log.info("Attempting to terminate the thread \"#{thread.name}\"...")
128
+ loop do
129
+ delay = (Time.now - start).round(2)
130
+ if thread.join(0.1)
131
+ @log.info("Thread \"#{thread.name}\" finished in #{delay}s")
132
+ break
133
+ end
134
+ if delay > 10
135
+ thread.exit
136
+ @log.error("Thread \"#{thread.name}\" forcefully terminated after #{delay}s")
137
+ end
138
+ end
139
+ end
140
+
128
141
  def cleanup(host, port, strength, threads)
129
142
  scores = load
130
143
  before = scores.map(&:value).max.to_i
@@ -141,14 +154,25 @@ module Zold
141
154
  end
142
155
 
143
156
  def cycle(host, port, strength, threads)
144
- s = @pipeline.pop
157
+ s = []
158
+ loop do
159
+ return unless @alive
160
+ begin
161
+ s << @pipeline.pop(true)
162
+ rescue ThreadError => _
163
+ sleep 0.25
164
+ end
165
+ s.compact!
166
+ break unless s.empty?
167
+ end
168
+ s = s[0]
145
169
  return unless s.valid?
146
170
  return unless s.host == host
147
171
  return unless s.port == port
148
172
  return unless s.strength >= strength
149
173
  Thread.current.name = s.to_mnemo
150
174
  bin = File.expand_path(File.join(File.dirname(__FILE__), '../../../bin/zold'))
151
- Open3.popen2e("ruby #{bin} next \"#{s}\"") do |stdin, stdout, thr|
175
+ Open3.popen2e("ruby #{bin} --skip-upgrades next \"#{s}\"") do |stdin, stdout, thr|
152
176
  @log.debug("Score counting started in process ##{thr.pid}")
153
177
  begin
154
178
  stdin.close
@@ -157,18 +181,21 @@ module Zold
157
181
  begin
158
182
  buffer << stdout.read_nonblock(1024)
159
183
  rescue IO::WaitReadable => e
160
- @log.debug("Still waiting for data from the score provider: #{e.message}")
184
+ @log.debug("Still waiting for the data from the process ##{thr.pid}: #{e.message}")
161
185
  end
162
- if buffer.end_with?("\n")
186
+ if buffer.end_with?("\n") && thr.value.to_i.zero?
163
187
  score = Score.parse(buffer.strip)
164
188
  @log.debug("New score discovered: #{score}")
165
189
  save(threads, [score])
166
190
  cleanup(host, port, strength, threads)
167
191
  break
168
192
  end
169
- break if stdout.eof?
193
+ if stdout.closed?
194
+ raise "Failed to calculate the score (##{thr.value}): #{buffer}" unless thr.value.to_i.zero?
195
+ break
196
+ end
170
197
  break unless @alive
171
- sleep 0.1
198
+ sleep 0.25
172
199
  end
173
200
  rescue StandardError => e
174
201
  @log.error(Backtrace.new(e).to_s)
@@ -27,6 +27,7 @@ require 'sinatra/base'
27
27
  require 'webrick'
28
28
  require 'get_process_mem'
29
29
  require 'diffy'
30
+ require 'usagewatch_ext'
30
31
  require 'concurrent'
31
32
  require_relative '../backtrace'
32
33
  require_relative '../version'
@@ -66,7 +67,7 @@ module Zold
66
67
  set :farm, nil? # to be injected at node.rb
67
68
  set :metronome, nil? # to be injected at node.rb
68
69
  set :entrance, nil? # to be injected at node.rb
69
- set :network, nil? # to be injected at node.rb
70
+ set :network, 'test' # to be injected at node.rb
70
71
  set :wallets, nil? # to be injected at node.rb
71
72
  set :remotes, nil? # to be injected at node.rb
72
73
  set :copies, nil? # to be injected at node.rb
@@ -162,13 +163,14 @@ while #{settings.address} is in '#{settings.network}'"
162
163
  score: score.to_h,
163
164
  pid: Process.pid,
164
165
  cpus: Concurrent.processor_count,
165
- memory: GetProcessMem.new.bytes,
166
+ memory: GetProcessMem.new.bytes.to_i,
166
167
  platform: RUBY_PLATFORM,
168
+ load: Usagewatch.uw_load.to_f,
167
169
  uptime: `uptime`.strip,
168
170
  threads: "#{Thread.list.select { |t| t.status == 'run' }.count}/#{Thread.list.count}",
169
171
  wallets: settings.wallets.all.count,
170
172
  remotes: settings.remotes.all.count,
171
- nscore: settings.remotes.all.map { |r| r[:score] }.inject(&:+),
173
+ nscore: settings.remotes.all.map { |r| r[:score] }.inject(&:+) || 0,
172
174
  farm: settings.farm.to_json,
173
175
  entrance: settings.entrance.to_json,
174
176
  date: Time.now.utc.iso8601,
data/lib/zold/version.rb CHANGED
@@ -25,6 +25,6 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.14.35'
28
+ VERSION = '0.14.36'
29
29
  PROTOCOL = 2
30
30
  end
data/resources/remotes CHANGED
@@ -1 +1,6 @@
1
1
  b1.zold.io,80,0,0
2
+ b2.zold.io,4096,0,0
3
+ 159.203.63.90,4096,0,0
4
+ 167.99.77.100,4096,0,0
5
+ 159.203.19.189,4096,0,0
6
+ 138.197.140.42,4096,0,0
@@ -155,7 +155,7 @@ class TestRemote < Minitest::Test
155
155
  )
156
156
  cmd.run(%W[remote add localhost #{port}])
157
157
  end
158
- assert_equal(12, remotes.all.count)
158
+ assert_equal(11 + File.readlines('resources/remotes').count, remotes.all.count)
159
159
  cmd.run(%w[remote select --max-nodes=5])
160
160
  assert_equal(5, remotes.all.count)
161
161
  end
@@ -68,11 +68,13 @@ class FakeNode
68
68
  end
69
69
  end
70
70
  uri = "http://localhost:#{port}/"
71
- while Zold::Http.new(uri: uri, score: nil).get.code == '599' && node.alive?
72
- @log.debug("Waiting for #{uri}...")
73
- sleep 1
71
+ loop do
72
+ ping = Zold::Http.new(uri: uri, score: nil, network: Zold::Front.network).get
73
+ break unless ping.code == '599' && node.alive?
74
+ @log.debug("Waiting for #{uri}: ##{ping.code}...")
75
+ sleep 0.5
74
76
  end
75
- raise 'The node is dead' unless node.alive?
77
+ raise "The node is dead at #{uri}" unless node.alive?
76
78
  begin
77
79
  yield port
78
80
  ensure
@@ -40,7 +40,7 @@ class FarmTest < Minitest::Test
40
40
  def test_renders_in_json
41
41
  Dir.mktmpdir do |dir|
42
42
  farm = Zold::Farm.new('NOPREFIX6@ffffffffffffffff', File.join(dir, 'f'), log: test_log)
43
- farm.start('localhost', 80, threads: 4, strength: 2) do
43
+ farm.start('localhost', 80, threads: 2, strength: 2) do
44
44
  assert_wait { !farm.best.empty? && !farm.best[0].value.zero? }
45
45
  count = 0
46
46
  100.times { count += farm.to_json[:best].length }
@@ -61,7 +61,7 @@ class FarmTest < Minitest::Test
61
61
  def test_makes_best_score_in_background
62
62
  Dir.mktmpdir do |dir|
63
63
  farm = Zold::Farm.new('NOPREFIX1@ffffffffffffffff', File.join(dir, 'f'), log: test_log)
64
- farm.start('localhost', 80, threads: 4, strength: 3) do
64
+ farm.start('localhost', 80, threads: 1, strength: 3) do
65
65
  assert_wait { !farm.best.empty? && farm.best[0].value >= 3 }
66
66
  score = farm.best[0]
67
67
  assert(!score.expired?)
@@ -143,4 +143,10 @@ class FarmTest < Minitest::Test
143
143
  end
144
144
  end
145
145
  end
146
+
147
+ def test_terminates_farm_entirely
148
+ Zold::Farm.new('NOPREFIX4@ffffffffffffffff', log: test_log).start('localhost', 4096, threads: 1, strength: 10) do
149
+ sleep 1
150
+ end
151
+ end
146
152
  end
@@ -32,6 +32,23 @@ require_relative '../../lib/zold/json_page'
32
32
  require_relative '../../lib/zold/score'
33
33
 
34
34
  class FrontTest < Minitest::Test
35
+ def test_renders_front_json
36
+ FakeNode.new(log: test_log).run(['--no-metronome', '--network=foo', '--threads=0']) do |port|
37
+ res = Zold::Http.new(uri: "http://localhost:#{port}/", network: 'foo', score: nil).get
38
+ json = JSON.parse(res.body)
39
+ assert_equal(Zold::VERSION, json['version'])
40
+ assert_equal(Zold::PROTOCOL, json['protocol'])
41
+ assert_equal('foo', json['network'])
42
+ assert(json['pid'].positive?)
43
+ assert(json['cpus'].positive?)
44
+ assert(json['memory'].positive?)
45
+ assert(json['load'].positive?)
46
+ assert(json['wallets'].positive?)
47
+ assert(json['remotes'].zero?)
48
+ assert(json['nscore'].zero?)
49
+ end
50
+ end
51
+
35
52
  def test_renders_public_pages
36
53
  FakeNode.new(log: test_log).run(['--ignore-score-weakness']) do |port|
37
54
  {
@@ -219,13 +236,7 @@ class FrontTest < Minitest::Test
219
236
  '*',
220
237
  response.header['Access-Control-Allow-Origin']
221
238
  )
222
- score = Zold::Score.new(
223
- time: Time.now, host: 'localhost', port: port, invoice: 'NOPREFIX@ffffffffffffffff', strength: 2
224
- )
225
- assert_equal(
226
- score.to_s,
227
- response.header[Zold::Http::SCORE_HEADER]
228
- )
239
+ assert(!response.header[Zold::Http::SCORE_HEADER].nil?)
229
240
  end
230
241
  end
231
242
  end
data/test/test__helper.rb CHANGED
@@ -85,7 +85,7 @@ module Minitest
85
85
  require_relative '../lib/zold/log'
86
86
  @test_log = Zold::Log::Verbose.new
87
87
  @test_log = Zold::Log::Quiet.new if ENV['TEST_QUIET_LOG']
88
- @test_log
88
+ Zold::Log::Sync.new(@test_log)
89
89
  end
90
90
  end
91
91
  end
data/test/test_http.rb CHANGED
@@ -51,4 +51,28 @@ class TestHttp < Minitest::Test
51
51
  res = Zold::Http.new(uri: 'http://good-host/', score: nil).get
52
52
  assert_equal('200', res.code)
53
53
  end
54
+
55
+ def test_sends_valid_network_header
56
+ stub_request(:get, 'http://some-host-1/')
57
+ .with(headers: { 'X-Zold-Network' => 'xyz' })
58
+ .to_return(status: 200)
59
+ res = Zold::Http.new(uri: 'http://some-host-1/', score: nil, network: 'xyz').get
60
+ assert_equal('200', res.code)
61
+ end
62
+
63
+ def test_sends_valid_protocol_header
64
+ stub_request(:get, 'http://some-host-2/')
65
+ .with(headers: { 'X-Zold-Protocol' => Zold::PROTOCOL })
66
+ .to_return(status: 200)
67
+ res = Zold::Http.new(uri: 'http://some-host-2/', score: nil).get
68
+ assert_equal('200', res.code)
69
+ end
70
+
71
+ def test_sends_valid_version_header
72
+ stub_request(:get, 'http://some-host-3/')
73
+ .with(headers: { 'X-Zold-Version' => Zold::VERSION })
74
+ .to_return(status: 200)
75
+ res = Zold::Http.new(uri: 'http://some-host-3/', score: nil).get
76
+ assert_equal('200', res.code)
77
+ end
54
78
  end
data/zold.gemspec CHANGED
@@ -71,6 +71,7 @@ and suggests a different architecture for digital wallet maintenance.'
71
71
  s.add_runtime_dependency 'sinatra', '~>2.0'
72
72
  s.add_runtime_dependency 'slop', '~>4.4'
73
73
  s.add_runtime_dependency 'sys-proctable', '1.1.5'
74
+ s.add_runtime_dependency 'usagewatch_ext', '0.2.0'
74
75
  s.add_runtime_dependency 'xcop', '~>0.5'
75
76
  s.add_development_dependency 'codecov', '0.1.10'
76
77
  s.add_development_dependency 'minitest', '5.11.3'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.35
4
+ version: 0.14.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -248,6 +248,20 @@ dependencies:
248
248
  - - '='
249
249
  - !ruby/object:Gem::Version
250
250
  version: 1.1.5
251
+ - !ruby/object:Gem::Dependency
252
+ name: usagewatch_ext
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - '='
256
+ - !ruby/object:Gem::Version
257
+ version: 0.2.0
258
+ type: :runtime
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - '='
263
+ - !ruby/object:Gem::Version
264
+ version: 0.2.0
251
265
  - !ruby/object:Gem::Dependency
252
266
  name: xcop
253
267
  requirement: !ruby/object:Gem::Requirement