zold 0.14.13 → 0.14.14

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/fixtures/scripts/distribute-wallet.sh +3 -2
  3. data/lib/zold/amount.rb +0 -0
  4. data/lib/zold/commands/calculate.rb +0 -0
  5. data/lib/zold/commands/node.rb +18 -12
  6. data/lib/zold/commands/pay.rb +1 -1
  7. data/lib/zold/commands/remote.rb +7 -6
  8. data/lib/zold/commands/routines/reconnect.rb +8 -8
  9. data/lib/zold/commands/routines/spread.rb +13 -11
  10. data/lib/zold/copies.rb +0 -0
  11. data/lib/zold/http.rb +10 -4
  12. data/lib/zold/metronome.rb +1 -0
  13. data/lib/zold/node/async_entrance.rb +2 -0
  14. data/lib/zold/node/entrance.rb +2 -2
  15. data/lib/zold/node/farm.rb +3 -0
  16. data/lib/zold/node/front.rb +26 -8
  17. data/lib/zold/node/nodup_entrance.rb +73 -0
  18. data/lib/zold/remotes.rb +3 -2
  19. data/lib/zold/score.rb +0 -0
  20. data/lib/zold/tax.rb +1 -1
  21. data/lib/zold/txn.rb +0 -0
  22. data/lib/zold/type.rb +0 -0
  23. data/lib/zold/upgrades.rb +0 -0
  24. data/lib/zold/version.rb +1 -1
  25. data/lib/zold/version_file.rb +0 -0
  26. data/lib/zold/wallet.rb +3 -1
  27. data/test/commands/routines/test_spread.rb +1 -4
  28. data/test/commands/test_node.rb +0 -0
  29. data/test/node/fake_node.rb +0 -0
  30. data/test/node/test_farm.rb +0 -0
  31. data/test/node/test_front.rb +0 -0
  32. data/test/node/test_nodup_entrance.rb +49 -0
  33. data/test/test_http.rb +0 -0
  34. data/test/test_score.rb +0 -0
  35. data/test/test_tax.rb +0 -0
  36. data/test/test_upgrades.rb +0 -0
  37. data/test/test_version.rb +0 -0
  38. data/test/test_wallet.rb +12 -0
  39. data/test/upgrades/test_protocol_up.rb +0 -0
  40. data/upgrades/2.rb +0 -0
  41. data/upgrades/protocol_up.rb +0 -0
  42. data/upgrades/rename_foreign_wallets.rb +0 -0
  43. metadata +5 -5
  44. data/lib/zold/commands/routines/bonuses.rb +0 -78
  45. data/test/commands/routines/test_bonuses.rb +0 -76
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 15c46c2740b4034dae78391ab88d747116977858
4
- data.tar.gz: 89bcbf8445379dddbff308e0dfb0a60fb11157e6
3
+ metadata.gz: 71369b8c56264180688b1f2e6fa6f9ce74bdbfd7
4
+ data.tar.gz: ae47e1e2501eab7fbb340b061f87c075884da2f0
5
5
  SHA512:
6
- metadata.gz: 1236cdbd4219b2b61dd11ffe556f26e867a2a91985d14c1d670ed2388393451e38fef70baefbaa0dd699c56b754798350a9e9afbdd116fdb7f5e21b0074ee586
7
- data.tar.gz: ae55db8d0224a10c1facebf9a3e52dc2f7be2baf9285138af232b808bba340171f74335237abf0a13909e24bf772671016de48873ec04858d552c8fc44905975
6
+ metadata.gz: 0445e6669bb843b41ffee95c9ea388ec452551fc7c833d3ddc3cfd7fbdd0bbba646974289500ed47ab7ed93e4928ac1e4e1d146fbfcd0bbff5f4e5218153b560
7
+ data.tar.gz: 4a1ecaea86ffa87784b43c92ec23415032bfd41e929f86759512beea2c778a333a4e05b4c64dbd892fe7c530c4d012e99505ddf67abdde88d1a70639c6d284a5
@@ -33,7 +33,8 @@ zold --home=${second} remote add localhost ${first}
33
33
  # the wallet to the remote, expecting it to distribute it to the second
34
34
  # wallet automatically.
35
35
  zold --public-key=id_rsa.pub create 0000000000000000
36
- zold pay --private-key=id_rsa 0000000000000000 NOPREFIX@aaaabbbbccccdddd 4.95 'To help you, dude!'
36
+ zold pay --private-key=id_rsa 0000000000000000 NOPREFIX@aaaabbbbccccdddd 4.95 'For the book'
37
+ zold remote clean
37
38
  zold remote add localhost ${first}
38
39
  zold push 0000000000000000
39
40
  zold remote clean
@@ -78,7 +79,7 @@ until zold fetch 0000000000000000 --ignore-score-weakness; do
78
79
  ((i++)) || sleep 1
79
80
  if ((i==5)); then
80
81
  cat ${first}/log.txt
81
- echo "The wallet has not been spread, after ${i} attempts"
82
+ echo "The wallet 0000000000000000 has not been spread, after ${i} attempts"
82
83
  exit 8
83
84
  fi
84
85
  sleep 1
data/lib/zold/amount.rb CHANGED
File without changes
File without changes
@@ -34,6 +34,7 @@ require_relative '../node/entrance'
34
34
  require_relative '../node/safe_entrance'
35
35
  require_relative '../node/spread_entrance'
36
36
  require_relative '../node/async_entrance'
37
+ require_relative '../node/nodup_entrance'
37
38
  require_relative '../node/front'
38
39
  require_relative '../node/farm'
39
40
  require_relative 'pull'
@@ -165,20 +166,25 @@ module Zold
165
166
  invoice = Invoice.new(wallets: @wallets, log: @log).run(['invoice', invoice])
166
167
  end
167
168
  SafeEntrance.new(
168
- AsyncEntrance.new(
169
- SpreadEntrance.new(
170
- Entrance.new(@wallets, @remotes, @copies, address, log: @log, network: opts['network']),
171
- @wallets, @remotes, address,
172
- log: @log,
173
- ignore_score_weakeness: opts['ignore-score-weakness']
174
- ), File.join(Dir.pwd, '.zoldata/entrance'), log: @log
175
- ), network: opts['network']
169
+ NoDupEntrance.new(
170
+ AsyncEntrance.new(
171
+ SpreadEntrance.new(
172
+ Entrance.new(@wallets, @remotes, @copies, address, log: @log, network: opts['network']),
173
+ @wallets, @remotes, address,
174
+ log: @log,
175
+ ignore_score_weakeness: opts['ignore-score-weakness']
176
+ ),
177
+ File.join(Dir.pwd, '.zoldata/entrance'), log: @log
178
+ ),
179
+ @wallets
180
+ ),
181
+ network: opts['network']
176
182
  ).start do |entrance|
177
183
  Front.set(:entrance, entrance)
178
184
  Farm.new(invoice, File.join(Dir.pwd, 'farm'), log: @log)
179
185
  .start(host, opts[:port], threads: opts[:threads], strength: opts[:strength]) do |farm|
180
186
  Front.set(:farm, farm)
181
- metronome(farm, entrance, opts).start do |metronome|
187
+ metronome(farm, opts).start do |metronome|
182
188
  Front.set(:metronome, metronome)
183
189
  @log.info("Starting up the web front at http://#{host}:#{opts[:port]}...")
184
190
  Front.run!
@@ -244,13 +250,13 @@ module Zold
244
250
  pid
245
251
  end
246
252
 
247
- def metronome(farm, entrance, opts)
253
+ def metronome(farm, opts)
248
254
  metronome = Metronome.new(@log)
249
255
  require_relative 'routines/spread'
250
- metronome.add(Routines::Spread.new(opts, @wallets, entrance, log: @log))
256
+ metronome.add(Routines::Spread.new(opts, @wallets, @remotes, log: @log))
251
257
  unless opts['standalone']
252
258
  require_relative 'routines/reconnect'
253
- metronome.add(Routines::Reconnect.new(opts, @remotes, farm, log: Log::Quiet.new))
259
+ metronome.add(Routines::Reconnect.new(opts, @remotes, farm, network: opts['network'], log: Log::Quiet.new))
254
260
  end
255
261
  @log.info('Metronome created')
256
262
  metronome
@@ -81,7 +81,7 @@ Available options:"
81
81
  if Tax.new(from).in_debt? && !opts['dont-pay-taxes']
82
82
  require_relative 'taxes'
83
83
  Taxes.new(wallets: @wallets, remotes: @remotes, log: @log).run(
84
- ['taxes', "--private-key=#{opts['private-key']}", id.to_s]
84
+ ['taxes', 'pay', "--private-key=#{opts['private-key']}", id.to_s]
85
85
  )
86
86
  end
87
87
  pay(from, invoice, amount, details, opts)
@@ -80,6 +80,9 @@ Available options:"
80
80
  o.bool '--ignore-score-value',
81
81
  'Don\'t complain when their score is too small',
82
82
  default: false
83
+ o.bool '--min-score',
84
+ "The minimum score required for winning the election (default: #{Tax::EXACT_SCORE})",
85
+ default: Tax::EXACT_SCORE
83
86
  o.bool '--force',
84
87
  'Add/remove if if this operation is not possible',
85
88
  default: false
@@ -192,7 +195,7 @@ Available options:"
192
195
  r.assert_valid_score(score)
193
196
  r.assert_score_ownership(score)
194
197
  r.assert_score_strength(score) unless opts['ignore-score-weakness']
195
- r.assert_score_value(score, Tax::EXACT_SCORE) unless opts['ignore-score-value']
198
+ r.assert_score_value(score, opts['min-score']) unless opts['ignore-score-value']
196
199
  scores << score
197
200
  end
198
201
  scores = scores.sample(1)
@@ -254,11 +257,9 @@ in #{(Time.now - start).round(2)}s")
254
257
  end
255
258
 
256
259
  def select(opts)
257
- max_nodes = opts['max-nodes']
258
- @log.info("Selecting #{max_nodes} strongest nodes.")
259
- selected_remotes = @remotes.all.sort_by { |remote| remote[:score] }.first(max_nodes)
260
- (@remotes.all - selected_remotes).each do |weak_remote|
261
- @remotes.remove(weak_remote[:host], weak_remote[:port])
260
+ selected = @remotes.all.sort_by { |r| r[:score] }.first(opts['max-nodes'])
261
+ (@remotes.all - selected).each do |r|
262
+ @remotes.remove(r[:host], r[:port])
262
263
  end
263
264
  end
264
265
 
@@ -32,22 +32,22 @@ module Zold
32
32
  module Routines
33
33
  # Reconnect to the network
34
34
  class Reconnect
35
- def initialize(opts, remotes, farm = Farm::Empty.new, log: Log::Quiet.new)
35
+ def initialize(opts, remotes, farm = Farm::Empty.new, network: 'test', log: Log::Quiet.new)
36
36
  @opts = opts
37
37
  @remotes = remotes
38
38
  @farm = farm
39
+ @network = network
39
40
  @log = log
40
41
  end
41
42
 
42
43
  def exec(_ = 0)
43
44
  sleep(60) unless @opts['routine-immediately']
44
- unless @opts['routine-immediately']
45
- Remote.new(remotes: @remotes, log: @log).run(%w[remote add b1.zold.io 80 --force])
46
- end
47
- Remote.new(remotes: @remotes, log: @log).run(%w[remote trim])
48
- Remote.new(remotes: @remotes, farm: @farm, log: @log).run(
49
- %w[remote update] + (@opts['never-reboot'] ? [] : ['--reboot'])
50
- )
45
+ cmd = Remote.new(remotes: @remotes, log: @log, farm: @farm)
46
+ args = ['remote', "--network=#{@opts['network']}"]
47
+ cmd.run(args + ['add', 'b1.zold.io', '80', '--force']) unless @opts['routine-immediately']
48
+ cmd.run(args + ['trim'])
49
+ cmd.run(args + ['select'])
50
+ cmd.run(args + ['update'] + (@opts['never-reboot'] ? [] : ['--reboot']))
51
51
  end
52
52
  end
53
53
  end
@@ -20,9 +20,8 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- require_relative '../remote'
24
- require_relative '../pull'
25
- require_relative '../pay'
23
+ require_relative '../../log'
24
+ require_relative '../../id'
26
25
  require_relative '../push'
27
26
 
28
27
  # Spread random wallets to the network.
@@ -34,22 +33,25 @@ module Zold
34
33
  module Routines
35
34
  # Spread them
36
35
  class Spread
37
- def initialize(opts, wallets, entrance, log: Log::Quiet.new)
36
+ def initialize(opts, wallets, remotes, log: Log::Quiet.new)
38
37
  @opts = opts
39
38
  @wallets = wallets
40
- @entrance = entrance
39
+ @remotes = remotes
41
40
  @log = log
42
41
  end
43
42
 
44
43
  def exec(_ = 0)
44
+ return if @remotes.all.empty?
45
45
  sleep(60) unless @opts['routine-immediately']
46
- pushed = []
47
- @wallets.all.sample(10).map do |w|
48
- id = Id.new(w)
49
- @entrance.push(id, File.read(@wallets.find(id).path))
50
- pushed << id
46
+ ids = @wallets.all.sample(10)
47
+ Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
48
+ ['push', "--network=#{@opts['network']}"] + ids
49
+ )
50
+ if ids.empty?
51
+ @log.info("Spread didn't push any wallets, we are empty")
52
+ else
53
+ @log.info("Spread #{ids.count} random wallets out of #{@wallets.all.count}: #{ids.join(', ')}")
51
54
  end
52
- @log.info("Spread #{pushed.count} random wallets out of #{@wallets.all.count}: #{pushed.join(', ')}")
53
55
  end
54
56
  end
55
57
  end
data/lib/zold/copies.rb CHANGED
File without changes
data/lib/zold/http.rb CHANGED
@@ -54,6 +54,12 @@ module Zold
54
54
  # protocol.
55
55
  PROTOCOL_HEADER = 'X-Zold-Protocol'
56
56
 
57
+ # Read timeout in seconds
58
+ READ_TIMEOUT = 32
59
+
60
+ # Connect timeout in seconds
61
+ CONNECT_TIMEOUT = 8
62
+
57
63
  # @todo #98:30m/DEV The following two statements are seen as issues by rubocop
58
64
  # raising a Lint/AmbiguousBlockAssociation offense. It is somthing
59
65
  # that could be solved by changing the TargetRubyVersion in .rubocop.yml
@@ -70,8 +76,8 @@ module Zold
70
76
 
71
77
  def get
72
78
  http = Net::HTTP.new(uri.host, uri.port)
73
- http.read_timeout = 8
74
- http.open_timeout = 4
79
+ http.read_timeout = Http::READ_TIMEOUT
80
+ http.open_timeout = Http::CONNECT_TIMEOUT
75
81
  path = uri.path
76
82
  path += '?' + uri.query if uri.query
77
83
  http.request_get(path, headers)
@@ -81,8 +87,8 @@ module Zold
81
87
 
82
88
  def put(body)
83
89
  http = Net::HTTP.new(uri.host, uri.port)
84
- http.read_timeout = 16
85
- http.open_timeout = 4
90
+ http.read_timeout = Http::READ_TIMEOUT
91
+ http.open_timeout = Http::CONNECT_TIMEOUT
86
92
  path = uri.path
87
93
  path += '?' + uri.query if uri.query
88
94
  http.request_put(
@@ -53,6 +53,7 @@ module Zold
53
53
  alive = true
54
54
  @routines.each do |r|
55
55
  @threads << Thread.start do
56
+ Thread.current.abort_on_exception = true
56
57
  Thread.current.name = r.class.name
57
58
  Thread.current.priority = -100
58
59
  step = 0
@@ -88,10 +88,12 @@ module Zold
88
88
  end
89
89
  end
90
90
 
91
+ # Always returns an array with a single ID of the pushed wallet
91
92
  def push(id, body)
92
93
  @mutex.synchronize do
93
94
  AtomicFile.new(File.join(@dir, id.to_s)).write(body)
94
95
  end
96
+ [id]
95
97
  end
96
98
 
97
99
  private
@@ -95,8 +95,8 @@ module Zold
95
95
  @log.info("Accepted #{id} in #{sec}s and modified #{modified.join(', ')}")
96
96
  end
97
97
  @mutex.synchronize do
98
- @history.shift if @history.length > 16
99
- @speed.shift if @speed.length > 64
98
+ @history.shift if @history.length >= 16
99
+ @speed.shift if @speed.length >= 64
100
100
  wallet = @wallets.find(id)
101
101
  @history << "#{id}/#{sec}/#{modified.count}/#{wallet.balance.to_zld}/#{wallet.txns.count}t"
102
102
  @speed << sec
@@ -76,6 +76,7 @@ module Zold
76
76
  @log.info("#{@pipeline.size} scores pre-loaded, the best is: #{best[0]}")
77
77
  @threads = (1..threads).map do |t|
78
78
  Thread.new do
79
+ Thread.current.abort_on_exception = true
79
80
  Thread.current.name = "f#{t}"
80
81
  Thread.current.priority = -100
81
82
  loop do
@@ -87,7 +88,9 @@ module Zold
87
88
  end
88
89
  alive = true
89
90
  @cleanup = Thread.new do
91
+ Thread.current.abort_on_exception = true
90
92
  Thread.current.name = 'cleanup'
93
+ Thread.current.priority = -100
91
94
  while alive
92
95
  sleep(60) unless strength == 1 # which will only happen in tests
93
96
  VerboseThread.new(@log).run do
@@ -71,7 +71,8 @@ module Zold
71
71
  before do
72
72
  check_header(Http::NETWORK_HEADER) do |header|
73
73
  if header != settings.network
74
- raise "Network name mismatch, you are in '#{header}', we are in '#{settings.network}'"
74
+ raise "Network name mismatch at #{request.url}, #{request.ip} is in '#{header}', \
75
+ while #{settings.address} is in '#{settings.network}'"
75
76
  end
76
77
  end
77
78
  check_header(Http::PROTOCOL_HEADER) do |header|
@@ -162,6 +163,8 @@ module Zold
162
163
  content_type 'application/json'
163
164
  {
164
165
  version: settings.version,
166
+ protocol: settings.protocol,
167
+ id: wallet.id.to_s,
165
168
  score: score.to_h,
166
169
  wallets: settings.wallets.all.count,
167
170
  mtime: wallet.mtime.utc.iso8601,
@@ -171,6 +174,25 @@ module Zold
171
174
  }.to_json
172
175
  end
173
176
 
177
+ get %r{/wallet/(?<id>[A-Fa-f0-9]{16}).json} do
178
+ id = Id.new(params[:id])
179
+ wallet = settings.wallets.find(id)
180
+ error 404 unless wallet.exists?
181
+ content_type 'application/json'
182
+ {
183
+ version: settings.version,
184
+ protocol: settings.protocol,
185
+ id: wallet.id.to_s,
186
+ score: score.to_h,
187
+ wallets: settings.wallets.all.count,
188
+ key: wallet.key.to_pub,
189
+ mtime: wallet.mtime.utc.iso8601,
190
+ digest: wallet.digest,
191
+ balance: wallet.balance.to_i,
192
+ txns: wallet.txns.count
193
+ }.to_json
194
+ end
195
+
174
196
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/balance} do
175
197
  id = Id.new(params[:id])
176
198
  wallet = settings.wallets.find(id)
@@ -184,7 +206,7 @@ module Zold
184
206
  wallet = settings.wallets.find(id)
185
207
  error 404 unless wallet.exists?
186
208
  content_type 'text/plain'
187
- wallet.key.to_s
209
+ wallet.key.to_pub
188
210
  end
189
211
 
190
212
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/mtime} do
@@ -225,16 +247,12 @@ module Zold
225
247
  end
226
248
 
227
249
  put %r{/wallet/(?<id>[A-Fa-f0-9]{16})/?} do
228
- id = Id.new(params[:id])
229
- wallet = settings.wallets.find(id)
230
250
  request.body.rewind
231
- after = request.body.read.to_s
232
- before = wallet.exists? ? AtomicFile.new(wallet.path).read.to_s : ''
233
- if before == after
251
+ modified = settings.entrance.push(Id.new(params[:id]), request.body.read.to_s)
252
+ if modified.empty?
234
253
  status 304
235
254
  return
236
255
  end
237
- settings.entrance.push(id, after)
238
256
  JSON.pretty_generate(
239
257
  version: settings.version,
240
258
  score: score.to_h,
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'tempfile'
24
+ require_relative '../log'
25
+ require_relative '../wallet'
26
+ require_relative '../atomic_file'
27
+
28
+ # The entrance that ignores duplicates.
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
31
+ # License:: MIT
32
+ module Zold
33
+ # The safe entrance
34
+ class NoDupEntrance
35
+ def initialize(entrance, wallets, log: Log::Quiet.new)
36
+ raise 'Entrance can\'t be nil' if entrance.nil?
37
+ @entrance = entrance
38
+ raise 'Wallets can\'t be nil' if wallets.nil?
39
+ @wallets = wallets
40
+ raise 'Log can\'t be nil' if log.nil?
41
+ @log = log
42
+ end
43
+
44
+ def start
45
+ @entrance.start { yield(self) }
46
+ end
47
+
48
+ def to_json
49
+ @entrance.to_json
50
+ end
51
+
52
+ # Returns a list of modifed wallets (as Zold::Id)
53
+ def push(id, body)
54
+ raise 'Id can\'t be nil' if id.nil?
55
+ raise 'Id must be of type Id' unless id.is_a?(Id)
56
+ raise 'Body can\'t be nil' if body.nil?
57
+ Tempfile.open(['', Wallet::EXTENSION]) do |f|
58
+ File.write(f, body)
59
+ wallet = Wallet.new(f.path)
60
+ wallet.refurbish
61
+ after = File.read(wallet.path)
62
+ wallet = @wallets.find(id)
63
+ before = wallet.exists? ? AtomicFile.new(wallet.path).read.to_s : ''
64
+ if before == after
65
+ @log.info("Duplicate of #{id}/#{wallet.digest[0, 6]}/#{after.length}b/#{wallet.txns.count}t ignored")
66
+ return []
67
+ end
68
+ @log.info("New content for #{id} arrived, #{before.length}b before and #{after.length}b after")
69
+ @entrance.push(id, body)
70
+ end
71
+ end
72
+ end
73
+ end
data/lib/zold/remotes.rb CHANGED
@@ -186,6 +186,7 @@ module Zold
186
186
  pool = Concurrent::FixedThreadPool.new([all.count, Concurrent.processor_count * 4].min, max_queue: 0)
187
187
  all.each do |r|
188
188
  pool.post do
189
+ Thread.current.abort_on_exception = true
189
190
  Thread.current.name = 'remotes'
190
191
  Thread.current.priority = -100
191
192
  start = Time.now
@@ -197,14 +198,14 @@ module Zold
197
198
  error(r[:host], r[:port])
198
199
  errors = errors(r[:host], r[:port])
199
200
  log.info("#{Rainbow("#{r[:host]}:#{r[:port]}").red}: #{e.message} \
200
- in #{(Time.now - start).round}s; errors=#{errors}")
201
+ in #{(Time.now - start).round}s; errors=#{errors}")
201
202
  log.debug(Backtrace.new(e).to_s)
202
203
  remove(r[:host], r[:port]) if errors > Remotes::TOLERANCE
203
204
  end
204
205
  end
205
206
  end
206
207
  pool.shutdown
207
- raise 'Failed to terminate the pool' unless pool.wait_for_termination(60)
208
+ pool.kill unless pool.wait_for_termination(5 * 60)
208
209
  end
209
210
 
210
211
  def errors(host, port = Remotes::PORT)
data/lib/zold/score.rb CHANGED
File without changes
data/lib/zold/tax.rb CHANGED
@@ -33,7 +33,7 @@ module Zold
33
33
  # A single tax payment
34
34
  class Tax
35
35
  # The exact score a wallet can buy in order to pay taxes.
36
- EXACT_SCORE = 16
36
+ EXACT_SCORE = 8
37
37
 
38
38
  # The maximum allowed amount in one transaction.
39
39
  MAX_PAYMENT = Amount.new(zld: 1.0)
data/lib/zold/txn.rb CHANGED
File without changes
data/lib/zold/type.rb CHANGED
File without changes
data/lib/zold/upgrades.rb CHANGED
File without changes
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.13'
28
+ VERSION = '0.14.14'
29
29
  PROTOCOL = 2
30
30
  end
File without changes
data/lib/zold/wallet.rb CHANGED
@@ -172,7 +172,9 @@ module Zold
172
172
  end
173
173
 
174
174
  def refurbish
175
- AtomicFile.new(@file).write("#{network}\n#{protocol}\n#{id}\n#{key.to_pub}\n\n#{txns.join("\n")}\n")
175
+ AtomicFile.new(@file).write(
176
+ "#{network}\n#{protocol}\n#{id}\n#{key.to_pub}\n\n#{txns.map { |t| t.to_s + "\n" }.join}"
177
+ )
176
178
  end
177
179
 
178
180
  private
@@ -39,10 +39,7 @@ class TestSpread < Minitest::Test
39
39
  opts = {
40
40
  'routine-immediately' => true
41
41
  }
42
- entrance = Zold::Entrance.new(
43
- home.wallets, home.remotes, home.copies(home.create_wallet).root, 'x', log: test_log
44
- )
45
- Zold::Routines::Spread.new(opts, home.wallets, entrance, log: test_log).exec
42
+ Zold::Routines::Spread.new(opts, home.wallets, home.remotes, log: test_log).exec
46
43
  end
47
44
  end
48
45
  end
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../fake_home'
25
+ require_relative '../test__helper'
26
+ require_relative '../../lib/zold/id'
27
+ require_relative '../../lib/zold/node/nodup_entrance'
28
+ require_relative 'fake_entrance'
29
+
30
+ # NoDupEntrance test.
31
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
32
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
33
+ # License:: MIT
34
+ class TestAsyncEntrance < Minitest::Test
35
+ def test_ignores_dup
36
+ FakeHome.new.run do |home|
37
+ wallet = home.create_wallet
38
+ Zold::NoDupEntrance.new(RealEntrance.new, home.wallets, log: test_log).start do |e|
39
+ assert(e.push(wallet.id, File.read(wallet.path)).empty?)
40
+ end
41
+ end
42
+ end
43
+
44
+ class RealEntrance < FakeEntrance
45
+ def push(id, _)
46
+ [id]
47
+ end
48
+ end
49
+ end
data/test/test_http.rb CHANGED
File without changes
data/test/test_score.rb CHANGED
File without changes
data/test/test_tax.rb CHANGED
File without changes
File without changes
data/test/test_version.rb CHANGED
File without changes
data/test/test_wallet.rb CHANGED
@@ -56,9 +56,21 @@ class TestWallet < Minitest::Test
56
56
  key = Zold::Key.new(file: 'fixtures/id_rsa')
57
57
  wallet.sub(amount, "NOPREFIX@#{Zold::Id.new}", key)
58
58
  wallet.sub(amount, "NOPREFIX@#{Zold::Id.new}", key)
59
+ before = File.read(wallet.path)
59
60
  File.write(wallet.path, File.read(wallet.path) + "\n\n\n")
60
61
  wallet.refurbish
61
62
  assert_equal(amount * -2, wallet.balance)
63
+ assert_equal(before, File.read(wallet.path))
64
+ end
65
+ end
66
+
67
+ def test_refurbishes_empty_wallet
68
+ FakeHome.new.run do |home|
69
+ wallet = home.create_wallet
70
+ before = File.read(wallet.path)
71
+ File.write(wallet.path, File.read(wallet.path) + "\n\n\n")
72
+ wallet.refurbish
73
+ assert_equal(before, File.read(wallet.path))
62
74
  end
63
75
  end
64
76
 
File without changes
data/upgrades/2.rb CHANGED
File without changes
File without changes
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.13
4
+ version: 0.14.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-16 00:00:00.000000000 Z
11
+ date: 2018-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -405,7 +405,6 @@ files:
405
405
  - lib/zold/commands/pull.rb
406
406
  - lib/zold/commands/push.rb
407
407
  - lib/zold/commands/remote.rb
408
- - lib/zold/commands/routines/bonuses.rb
409
408
  - lib/zold/commands/routines/reconnect.rb
410
409
  - lib/zold/commands/routines/spread.rb
411
410
  - lib/zold/commands/show.rb
@@ -424,6 +423,7 @@ files:
424
423
  - lib/zold/node/entrance.rb
425
424
  - lib/zold/node/farm.rb
426
425
  - lib/zold/node/front.rb
426
+ - lib/zold/node/nodup_entrance.rb
427
427
  - lib/zold/node/safe_entrance.rb
428
428
  - lib/zold/node/spread_entrance.rb
429
429
  - lib/zold/patch.rb
@@ -441,7 +441,6 @@ files:
441
441
  - lib/zold/wallet.rb
442
442
  - lib/zold/wallets.rb
443
443
  - resources/remotes
444
- - test/commands/routines/test_bonuses.rb
445
444
  - test/commands/routines/test_reconnect.rb
446
445
  - test/commands/routines/test_spread.rb
447
446
  - test/commands/test_alias.rb
@@ -468,6 +467,7 @@ files:
468
467
  - test/node/test_entrance.rb
469
468
  - test/node/test_farm.rb
470
469
  - test/node/test_front.rb
470
+ - test/node/test_nodup_entrance.rb
471
471
  - test/node/test_safe_entrance.rb
472
472
  - test/node/test_spread_entrance.rb
473
473
  - test/test__helper.rb
@@ -528,7 +528,6 @@ test_files:
528
528
  - features/gem_package.feature
529
529
  - features/step_definitions/steps.rb
530
530
  - features/support/env.rb
531
- - test/commands/routines/test_bonuses.rb
532
531
  - test/commands/routines/test_reconnect.rb
533
532
  - test/commands/routines/test_spread.rb
534
533
  - test/commands/test_alias.rb
@@ -555,6 +554,7 @@ test_files:
555
554
  - test/node/test_entrance.rb
556
555
  - test/node/test_farm.rb
557
556
  - test/node/test_front.rb
557
+ - test/node/test_nodup_entrance.rb
558
558
  - test/node/test_safe_entrance.rb
559
559
  - test/node/test_spread_entrance.rb
560
560
  - test/test__helper.rb
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright (c) 2018 Yegor Bugayenko
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the 'Software'), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in all
13
- # copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- # SOFTWARE.
22
-
23
- require_relative '../remote'
24
- require_relative '../pull'
25
- require_relative '../pay'
26
- require_relative '../push'
27
-
28
- # Pay bonuses routine.
29
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
- # Copyright:: Copyright (c) 2018 Yegor Bugayenko
31
- # License:: MIT
32
- module Zold
33
- # Routines module
34
- module Routines
35
- # Pay bonuses to random nodes
36
- class Bonuses
37
- def initialize(opts, wallets, remotes, copies, farm, log: Log::Quiet.new)
38
- @opts = opts
39
- @wallets = wallets
40
- @remotes = remotes
41
- @copies = copies
42
- @farm = farm
43
- @log = log
44
- end
45
-
46
- def exec(_ = 0)
47
- sleep(@opts['bonus-time'] * 60) unless @opts['routine-immediately']
48
- raise '--private-key is required to pay bonuses' unless @opts['private-key']
49
- raise '--bonus-wallet is required to pay bonuses' unless @opts['bonus-wallet']
50
- raise '--bonus-amount is required to pay bonuses' unless @opts['bonus-amount']
51
- winners = Remote.new(remotes: @remotes, log: @log, farm: @farm).run(
52
- ['remote', 'elect', @opts['bonus-wallet'], '--private-key', @opts['private-key']] +
53
- (@opts['ignore-score-weakness'] ? ['--ignore-score-weakness'] : [])
54
- )
55
- return if winners.empty?
56
- unless @wallets.find(Id.new(@opts['bonus-wallet'])).exists?
57
- Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
58
- ['pull', @opts['bonus-wallet']] +
59
- (@opts['ignore-score-weakness'] ? ['--ignore-score-weakness'] : [])
60
- )
61
- end
62
- winners.each do |score|
63
- Pay.new(wallets: @wallets, remotes: @remotes, log: @log).run(
64
- [
65
- 'pay', @opts['bonus-wallet'], score.invoice, @opts['bonus-amount'].to_s,
66
- "Hosting bonus for #{score.host} #{score.port} #{score.value}",
67
- '--private-key', @opts['private-key']
68
- ]
69
- )
70
- end
71
- Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
72
- ['push', @opts['bonus-wallet']] +
73
- (@opts['ignore-score-weakness'] ? ['--ignore-score-weakness'] : [])
74
- )
75
- end
76
- end
77
- end
78
- end
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright (c) 2018 Yegor Bugayenko
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the 'Software'), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in all
13
- # copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- # SOFTWARE.
22
-
23
- require 'minitest/autorun'
24
- require 'webmock/minitest'
25
- require_relative '../../test__helper'
26
- require_relative '../../fake_home'
27
- require_relative '../../node/fake_node'
28
- require_relative '../../../lib/zold/node/farm.rb'
29
- require_relative '../../../lib/zold/commands/push'
30
- require_relative '../../../lib/zold/commands/pay'
31
- require_relative '../../../lib/zold/commands/routines/bonuses.rb'
32
-
33
- # Bonuses test.
34
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
35
- # Copyright:: Copyright (c) 2018 Yegor Bugayenko
36
- # License:: MIT
37
- class TestBonuses < Minitest::Test
38
- def test_pays_bonuses
39
- FakeHome.new.run do |home|
40
- FakeNode.new(log: test_log).run(['--ignore-score-weakness']) do |port|
41
- bank = home.create_wallet
42
- Zold::Pay.new(wallets: home.wallets, remotes: home.remotes, log: test_log).run(
43
- ['pay', home.create_wallet.id.to_s, bank.id.to_s, '100', '--force', '--private-key=id_rsa']
44
- )
45
- assert_equal(Zold::Amount.new(zld: 100.0), bank.balance)
46
- opts = {
47
- 'ignore-score-weakness' => true,
48
- 'routine-immediately' => true,
49
- 'private-key' => 'id_rsa',
50
- 'bonus-wallet' => bank.id.to_s,
51
- 'bonus-amount' => 1,
52
- 'bonus-time' => 0
53
- }
54
- score = Zold::Score.new(
55
- time: Time.now, host: 'fake-node.local', port: 999, invoice: 'NOPREFIX@ffffffffffffffff', strength: 1
56
- )
57
- 16.times { score = score.next }
58
- remotes = home.remotes
59
- remotes.add('localhost', port)
60
- remotes.add(score.host, score.port)
61
- stub_request(:get, "http://#{score.host}:#{score.port}/").to_return(
62
- status: 200,
63
- body: {
64
- version: Zold::VERSION,
65
- score: score.to_h
66
- }.to_json
67
- )
68
- Zold::Routines::Bonuses.new(
69
- opts, home.wallets, remotes, home.copies(bank).root,
70
- Zold::Farm::Empty.new, log: test_log
71
- ).exec
72
- assert_equal(Zold::Amount.new(zld: 99.0), bank.balance)
73
- end
74
- end
75
- end
76
- end