zold 0.14.35 → 0.14.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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