zold 0.14.8 → 0.14.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +0 -0
  4. data/Gemfile +2 -0
  5. data/Rakefile +2 -0
  6. data/appveyor.yml +8 -3
  7. data/features/step_definitions/steps.rb +2 -0
  8. data/features/support/env.rb +2 -0
  9. data/fixtures/merge/asserts.rb +2 -0
  10. data/fixtures/merge/into-no-wallet/assert.rb +2 -0
  11. data/fixtures/merge/random-expenses/assert.rb +2 -0
  12. data/fixtures/merge/simple-case/assert.rb +2 -0
  13. data/fixtures/scripts/_head.sh +13 -0
  14. data/fixtures/scripts/distribute-wallet.sh +86 -0
  15. data/fixtures/scripts/spread-wallets.sh +56 -0
  16. data/lib/zold.rb +2 -0
  17. data/lib/zold/amount.rb +3 -1
  18. data/lib/zold/atomic_file.rb +2 -0
  19. data/lib/zold/backtrace.rb +2 -0
  20. data/lib/zold/commands/alias.rb +2 -0
  21. data/lib/zold/commands/args.rb +2 -0
  22. data/lib/zold/commands/calculate.rb +5 -3
  23. data/lib/zold/commands/clean.rb +2 -0
  24. data/lib/zold/commands/create.rb +2 -0
  25. data/lib/zold/commands/diff.rb +2 -0
  26. data/lib/zold/commands/fetch.rb +7 -2
  27. data/lib/zold/commands/invoice.rb +2 -0
  28. data/lib/zold/commands/list.rb +2 -0
  29. data/lib/zold/commands/merge.rb +2 -0
  30. data/lib/zold/commands/node.rb +2 -0
  31. data/lib/zold/commands/pay.rb +2 -0
  32. data/lib/zold/commands/propagate.rb +2 -0
  33. data/lib/zold/commands/pull.rb +2 -0
  34. data/lib/zold/commands/push.rb +2 -0
  35. data/lib/zold/commands/remote.rb +11 -6
  36. data/lib/zold/commands/routines/bonuses.rb +78 -0
  37. data/lib/zold/commands/routines/reconnect.rb +2 -0
  38. data/lib/zold/commands/routines/spread.rb +2 -0
  39. data/lib/zold/commands/show.rb +2 -0
  40. data/lib/zold/commands/taxes.rb +2 -0
  41. data/lib/zold/copies.rb +4 -2
  42. data/lib/zold/hexnum.rb +2 -0
  43. data/lib/zold/http.rb +28 -20
  44. data/lib/zold/hungry_wallets.rb +2 -0
  45. data/lib/zold/id.rb +2 -0
  46. data/lib/zold/json_page.rb +2 -0
  47. data/lib/zold/key.rb +2 -0
  48. data/lib/zold/log.rb +2 -0
  49. data/lib/zold/metronome.rb +2 -0
  50. data/lib/zold/node/async_entrance.rb +3 -3
  51. data/lib/zold/node/emission.rb +2 -0
  52. data/lib/zold/node/entrance.rb +5 -2
  53. data/lib/zold/node/farm.rb +3 -1
  54. data/lib/zold/node/front.rb +4 -1
  55. data/lib/zold/node/safe_entrance.rb +2 -0
  56. data/lib/zold/node/spread_entrance.rb +2 -0
  57. data/lib/zold/patch.rb +2 -0
  58. data/lib/zold/prefixes.rb +2 -0
  59. data/lib/zold/remotes.rb +15 -13
  60. data/lib/zold/score.rb +62 -59
  61. data/lib/zold/signature.rb +2 -0
  62. data/lib/zold/tax.rb +3 -1
  63. data/lib/zold/txn.rb +4 -2
  64. data/lib/zold/type.rb +36 -0
  65. data/lib/zold/upgrades.rb +2 -4
  66. data/lib/zold/verbose_thread.rb +2 -0
  67. data/lib/zold/version.rb +4 -2
  68. data/lib/zold/version_file.rb +2 -0
  69. data/lib/zold/wallet.rb +4 -2
  70. data/lib/zold/wallets.rb +2 -0
  71. data/test/commands/routines/test_bonuses.rb +76 -0
  72. data/test/commands/routines/test_reconnect.rb +2 -0
  73. data/test/commands/routines/test_spread.rb +2 -0
  74. data/test/commands/test_alias.rb +2 -0
  75. data/test/commands/test_calculate.rb +2 -0
  76. data/test/commands/test_clean.rb +2 -0
  77. data/test/commands/test_create.rb +2 -0
  78. data/test/commands/test_diff.rb +2 -0
  79. data/test/commands/test_fetch.rb +2 -0
  80. data/test/commands/test_invoice.rb +2 -0
  81. data/test/commands/test_list.rb +2 -0
  82. data/test/commands/test_merge.rb +2 -0
  83. data/test/commands/test_node.rb +6 -0
  84. data/test/commands/test_pay.rb +2 -0
  85. data/test/commands/test_propagate.rb +2 -0
  86. data/test/commands/test_push.rb +2 -0
  87. data/test/commands/test_remote.rb +40 -0
  88. data/test/commands/test_show.rb +2 -0
  89. data/test/commands/test_taxes.rb +2 -0
  90. data/test/fake_home.rb +2 -0
  91. data/test/node/fake_entrance.rb +2 -0
  92. data/test/node/fake_node.rb +3 -1
  93. data/test/node/test_async_entrance.rb +2 -0
  94. data/test/node/test_emission.rb +2 -0
  95. data/test/node/test_entrance.rb +2 -0
  96. data/test/node/test_farm.rb +9 -7
  97. data/test/node/test_front.rb +15 -10
  98. data/test/node/test_safe_entrance.rb +2 -0
  99. data/test/node/test_spread_entrance.rb +2 -0
  100. data/test/test__helper.rb +2 -0
  101. data/test/test_amount.rb +2 -0
  102. data/test/test_atomic_file.rb +2 -0
  103. data/test/test_backtrace.rb +2 -0
  104. data/test/test_copies.rb +4 -1
  105. data/test/test_hexnum.rb +2 -0
  106. data/test/test_http.rb +5 -3
  107. data/test/test_id.rb +2 -0
  108. data/test/test_key.rb +2 -0
  109. data/test/test_metronome.rb +2 -0
  110. data/test/test_patch.rb +2 -0
  111. data/test/test_prefixes.rb +2 -0
  112. data/test/test_remotes.rb +11 -0
  113. data/test/test_score.rb +26 -24
  114. data/test/test_signature.rb +2 -0
  115. data/test/test_tax.rb +4 -2
  116. data/test/test_txn.rb +2 -0
  117. data/test/test_upgrades.rb +2 -0
  118. data/test/test_verbose_thread.rb +2 -0
  119. data/test/test_version.rb +2 -0
  120. data/test/test_wallet.rb +2 -0
  121. data/test/test_wallets.rb +2 -0
  122. data/test/test_zold.rb +2 -0
  123. data/test/upgrades/test_protocol_up.rb +2 -0
  124. data/upgrades/2.rb +2 -0
  125. data/upgrades/protocol_up.rb +2 -0
  126. data/upgrades/rename_foreign_wallets.rb +2 -0
  127. data/zold.gemspec +5 -1
  128. metadata +38 -4
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -138,7 +140,7 @@ Available options:"
138
140
 
139
141
  def show
140
142
  @remotes.all.each do |r|
141
- score = Rainbow("/#{r[:score]}").color(r[:score] > 0 ? :green : :red)
143
+ score = Rainbow("/#{r[:score]}").color(r[:score].positive? ? :green : :red)
142
144
  @log.info(r[:host] + Rainbow(":#{r[:port]}").gray + score)
143
145
  end
144
146
  end
@@ -155,7 +157,7 @@ Available options:"
155
157
 
156
158
  def add(host, port, opts)
157
159
  unless opts['skip-ping']
158
- res = Http.new("http://#{host}:#{port}/version", network: opts['network']).get
160
+ res = Http.new(uri: "http://#{host}:#{port}/version", score: nil, network: opts['network']).get
159
161
  raise "The node #{host}:#{port} is not responding (code is #{res.code})" unless res.code == '200'
160
162
  end
161
163
  if @remotes.exists?(host, port)
@@ -251,10 +253,13 @@ in #{(Time.now - start).round(2)}s")
251
253
  end
252
254
  end
253
255
 
254
- # @todo #292:30min Implement the logic of selecting the nodes as per #292.
255
- # The strongest n nodes should be selected, where n = opts['max-nodes'].
256
- def select(_opts)
257
- raise NotImplementedError, 'This feature is not yet implemented.'
256
+ 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])
262
+ end
258
263
  end
259
264
 
260
265
  def terminate
@@ -0,0 +1,78 @@
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -88,11 +90,11 @@ module Zold
88
90
  def add(content, host, port, score, time = Time.now)
89
91
  raise "Content can't be empty" if content.empty?
90
92
  raise 'TCP port must be of type Integer' unless port.is_a?(Integer)
91
- raise "TCP port can't be negative: #{port}" if port < 0
93
+ raise "TCP port can't be negative: #{port}" if port.negative?
92
94
  raise 'Time must be of type Time' unless time.is_a?(Time)
93
95
  raise "Time must be in the past: #{time}" if time > Time.now
94
96
  raise 'Score must be Integer' unless score.is_a?(Integer)
95
- raise "Score can't be negative: #{score}" if score < 0
97
+ raise "Score can't be negative: #{score}" if score.negative?
96
98
  @mutex.synchronize do
97
99
  FileUtils.mkdir_p(@dir)
98
100
  list = load
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -24,6 +26,7 @@ require 'net/http'
24
26
  require_relative 'backtrace'
25
27
  require_relative 'version'
26
28
  require_relative 'score'
29
+ require_relative 'type'
27
30
 
28
31
  # HTTP page.
29
32
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -31,48 +34,53 @@ require_relative 'score'
31
34
  # License:: MIT
32
35
  module Zold
33
36
  # Http page
34
- class Http
37
+ class Http < Dry::Struct
35
38
  # HTTP header we add to each HTTP request, in order to inform
36
39
  # the other node about the score. If the score is big enough,
37
40
  # the remote node will add us to its list of remote nodes.
38
- SCORE_HEADER = 'X-Zold-Score'.freeze
41
+ SCORE_HEADER = 'X-Zold-Score'
39
42
 
40
43
  # HTTP header we add, in order to inform the node about our
41
44
  # version. This is done mostly in order to let the other node
42
45
  # reboot itself, if the version is higher.
43
- VERSION_HEADER = 'X-Zold-Version'.freeze
46
+ VERSION_HEADER = 'X-Zold-Version'
44
47
 
45
48
  # HTTP header we add, in order to inform the node about our
46
49
  # network. This is done in order to isolate test networks from
47
50
  # production one.
48
- NETWORK_HEADER = 'X-Zold-Network'.freeze
51
+ NETWORK_HEADER = 'X-Zold-Network'
49
52
 
50
- def initialize(uri, score = Score::ZERO, network: 'test')
51
- raise 'URI can\'t be nil' if uri.nil?
52
- @uri = uri.is_a?(String) ? URI(uri) : uri
53
- raise 'Score can\'t be nil' if score.nil?
54
- @score = score
55
- raise 'Network can\'t be nil' if network.nil?
56
- @network = network
57
- end
53
+ # @todo #98:30m/DEV The following two statements are seen as issues by rubocop
54
+ # raising a Lint/AmbiguousBlockAssociation offense. It is somthing
55
+ # that could be solved by changing the TargetRubyVersion in .rubocop.yml
56
+ # that is already taken care of in another issue. I am leaving a todo
57
+ # to check that rubocop doesn't complain anymore, otherwise find another
58
+ # solution
59
+ attribute :uri, (Types::Class.constructor do |value|
60
+ value.is_a?(URI) ? value : URI(value)
61
+ end)
62
+ attribute :score, (Types::Class.constructor do |value|
63
+ value.nil? ? Score::ZERO : value
64
+ end)
65
+ attribute :network, Types::Strict::String.optional.default('test')
58
66
 
59
67
  def get
60
- http = Net::HTTP.new(@uri.host, @uri.port)
68
+ http = Net::HTTP.new(uri.host, uri.port)
61
69
  http.read_timeout = 8
62
70
  http.open_timeout = 4
63
- path = @uri.path
64
- path += '?' + @uri.query if @uri.query
71
+ path = uri.path
72
+ path += '?' + uri.query if uri.query
65
73
  http.request_get(path, headers)
66
74
  rescue StandardError => e
67
75
  Error.new(e)
68
76
  end
69
77
 
70
78
  def put(body)
71
- http = Net::HTTP.new(@uri.host, @uri.port)
79
+ http = Net::HTTP.new(uri.host, uri.port)
72
80
  http.read_timeout = 16
73
81
  http.open_timeout = 4
74
- path = @uri.path
75
- path += '?' + @uri.query if @uri.query
82
+ path = uri.path
83
+ path += '?' + uri.query if uri.query
76
84
  http.request_put(
77
85
  path, body,
78
86
  headers.merge(
@@ -112,8 +120,8 @@ module Zold
112
120
  'Accept-Encoding': 'gzip'
113
121
  }
114
122
  headers[Http::VERSION_HEADER] = VERSION
115
- headers[Http::NETWORK_HEADER] = @network
116
- headers[Http::SCORE_HEADER] = @score.reduced(4).to_text if @score.valid? && !@score.expired? && @score.value > 3
123
+ headers[Http::NETWORK_HEADER] = network
124
+ headers[Http::SCORE_HEADER] = score.reduced(4).to_text if score.valid? && !score.expired? && score.value > 3
117
125
  headers
118
126
  end
119
127
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delegate'
2
4
 
3
5
  module Zold
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -77,10 +79,8 @@ module Zold
77
79
 
78
80
  def to_json
79
81
  @entrance.to_json.merge(
80
- 'pool.completed_task_count': @pool.completed_task_count,
81
- 'pool.largest_length': @pool.largest_length,
82
+ 'queue': queue.count,
82
83
  'pool.length': @pool.length,
83
- 'pool.queue_length': @pool.queue_length,
84
84
  'pool.running': @pool.running?
85
85
  )
86
86
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -60,7 +62,8 @@ module Zold
60
62
 
61
63
  def to_json
62
64
  {
63
- history: @history.join(', ')
65
+ 'history': @history.join(', '),
66
+ 'history_size': @history.count
64
67
  }
65
68
  end
66
69
 
@@ -76,7 +79,7 @@ module Zold
76
79
  unless @remotes.all.empty?
77
80
  Fetch.new(
78
81
  wallets: @wallets, remotes: @remotes, copies: copies.root, log: @log
79
- ).run(['fetch', id.to_s, "--ignore-node=#{@address}", "--network=#{@network}"])
82
+ ).run(['fetch', id.to_s, "--ignore-node=#{@address}", "--network=#{@network}", '--quiet-if-absent'])
80
83
  end
81
84
  modified = Merge.new(
82
85
  wallets: @wallets, copies: copies.root, log: @log
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -121,7 +123,7 @@ module Zold
121
123
  def cleanup(host, port, strength, threads)
122
124
  scores = load
123
125
  before = scores.map(&:value).max.to_i
124
- save(threads, [Score.new(Time.now, host, port, @invoice, strength: strength)])
126
+ save(threads, [Score.new(time: Time.now, host: host, port: port, invoice: @invoice, strength: strength)])
125
127
  scores = load
126
128
  push(scores)
127
129
  after = scores.map(&:value).max.to_i
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -131,6 +133,7 @@ module Zold
131
133
  JSON.pretty_generate(
132
134
  version: settings.version,
133
135
  network: settings.network,
136
+ protocol: settings.protocol,
134
137
  score: score.to_h,
135
138
  pid: Process.pid,
136
139
  cpus: Concurrent.processor_count,
@@ -203,7 +206,7 @@ module Zold
203
206
  wallet.network,
204
207
  wallet.protocol,
205
208
  wallet.id.to_s,
206
- wallet.key.to_s,
209
+ wallet.key.to_pub,
207
210
  '',
208
211
  wallet.txns.map(&:to_text).join("\n"),
209
212
  '',
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2018 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -77,7 +79,7 @@ module Zold
77
79
  end
78
80
 
79
81
  def http(path = '/')
80
- Http.new("http://#{@host}:#{@port}#{path}", @score, network: @network)
82
+ Http.new(uri: "http://#{@host}:#{@port}#{path}", score: @score, network: @network)
81
83
  end
82
84
 
83
85
  def to_s
@@ -155,12 +157,11 @@ module Zold
155
157
  raise 'Port can\'t be nil' if port.nil?
156
158
  raise 'Port has to be of type Integer' unless port.is_a?(Integer)
157
159
  raise 'Port can\'t be zero' if port.zero?
158
- raise 'Port can\'t be negative' if port < 0
160
+ raise 'Port can\'t be negative' if port.negative?
159
161
  raise 'Port can\'t be over 65536' if port > 0xffff
160
162
  raise "#{host}:#{port} already exists" if exists?(host, port)
161
163
  list = load
162
164
  list << { host: host.downcase, port: port, score: 0 }
163
- list.uniq! { |r| "#{r[:host]}:#{r[:port]}" }
164
165
  save(list)
165
166
  end
166
167
 
@@ -168,7 +169,6 @@ module Zold
168
169
  raise 'Port has to be of type Integer' unless port.is_a?(Integer)
169
170
  raise 'Host can\'t be nil' if host.nil?
170
171
  raise 'Port can\'t be nil' if port.nil?
171
- raise "#{host}:#{port} is absent" unless exists?(host, port)
172
172
  list = load
173
173
  list.reject! { |r| r[:host] == host.downcase && r[:port] == port }
174
174
  save(list)
@@ -211,10 +211,7 @@ in #{(Time.now - start).round}s; errors=#{errors}")
211
211
  raise 'Host can\'t be nil' if host.nil?
212
212
  raise 'Port can\'t be nil' if port.nil?
213
213
  raise 'Port has to be of type Integer' unless port.is_a?(Integer)
214
- list = load
215
- raise "#{host}:#{port} is absent among #{list.count} remotes" unless exists?(host, port)
216
- list.find { |r| r[:host] == host.downcase && r[:port] == port }[:errors] += 1
217
- save(list)
214
+ if_present(host, port) { |r| r[:errors] += 1 }
218
215
  end
219
216
 
220
217
  def rescore(host, port, score)
@@ -222,14 +219,19 @@ in #{(Time.now - start).round}s; errors=#{errors}")
222
219
  raise 'Port can\'t be nil' if port.nil?
223
220
  raise 'Score can\'t be nil' if score.nil?
224
221
  raise 'Port has to be of type Integer' unless port.is_a?(Integer)
225
- raise "#{host}:#{port} is absent" unless exists?(host, port)
226
- list = load
227
- list.find { |r| r[:host] == host.downcase && r[:port] == port }[:score] = score
228
- save(list)
222
+ if_present(host, port) { |r| r[:score] = score }
229
223
  end
230
224
 
231
225
  private
232
226
 
227
+ def if_present(host, port)
228
+ list = load
229
+ remote = list.find { |r| r[:host] == host.downcase && r[:port] == port }
230
+ return unless remote
231
+ yield remote
232
+ save(list)
233
+ end
234
+
233
235
  def load
234
236
  @mutex.synchronize do
235
237
  raw = CSV.read(file).map do |r|
@@ -250,7 +252,7 @@ in #{(Time.now - start).round}s; errors=#{errors}")
250
252
  def save(list)
251
253
  @mutex.synchronize do
252
254
  AtomicFile.new(file).write(
253
- list.map do |r|
255
+ list.uniq { |r| "#{r[:host]}:#{r[:port]}" }.map do |r|
254
256
  [
255
257
  r[:host],
256
258
  r[:port],