zold 0.9.10 → 0.9.11

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: eb875c3b804c4d045fc34d2e00ad415d2d29a370
4
- data.tar.gz: 9562b642f01203ff7560dcd43d72c7edab08cb2d
3
+ metadata.gz: 76a4eedb7d10def207298c7f125a6c9fbe392bec
4
+ data.tar.gz: 04f8e073cea64e11e7989d1c943a629295e45425
5
5
  SHA512:
6
- metadata.gz: ac0fc271a65b8fd7284398dba3e67ab515f7d06942e12a10d27e654433907191ab1abeabb9767384296bb5259ff3387cb4ecf3a59b917d6f9c6b1a09a4ea698b
7
- data.tar.gz: 5be7344fecc4fc4d9376b16c45c43a5a25ca6a451386b4ecca734a73358c3257c91e8e5a1a98fd73323c683c4c81be854b8497548ea0529ae4c81e6e9ed24edc
6
+ metadata.gz: b70bdc247fa5230a8a6709578d603f1e7a87f8b3a758abc663dc5cf603136f49f8049a1df90fb709c728cf8a9d8b4ca9fcedbc9b0e15ee209ec2556cffe996be
7
+ data.tar.gz: 9dbfe9ece397a9ef7a6ee65e771ac560fe4aacc2ceb1ac77d291bb53b0d263ddc4349ae45e00ecc8c47564dab15f304a639e6440828325a7cded1a2e7de4db52
@@ -64,50 +64,28 @@ Available options:"
64
64
 
65
65
  def fetch(id, cps, opts)
66
66
  total = 0
67
- @remotes.all.each do |r|
68
- done = fetch_one(id, r, cps, opts)
69
- if done
70
- total += 1
71
- else
72
- @remotes.error(r[:host], r[:port])
73
- end
67
+ @remotes.iterate(@log) do |r|
68
+ fetch_one(id, r, cps, opts)
69
+ total += 1
74
70
  end
75
71
  @log.debug("#{total} copies of #{id} fetched, there are #{cps.all.count} available locally")
76
72
  end
77
73
 
78
74
  def fetch_one(id, r, cps, opts)
79
- address = "#{r[:host]}:#{r[:port]}".downcase
80
- if opts['ignore-node'].include?(address)
81
- @log.info("#{address} ignored because of --ignore-node")
82
- return false
83
- end
84
- uri = URI("#{r[:home]}wallet/#{id}")
85
- res = Http.new(uri).get
86
- if res.code == '404'
87
- @log.info("#{address} wallet #{id} #{Rainbow('not found').red}")
88
- return false
89
- end
90
- unless res.code == '200'
91
- @log.error("#{address} #{Rainbow(res.code).red}/#{res.message} at #{uri}")
75
+ if opts['ignore-node'].include?(r.to_s)
76
+ @log.info("#{r} ignored because of --ignore-node")
92
77
  return false
93
78
  end
79
+ res = r.http("/wallet/#{id}").get
80
+ raise "Wallet #{id} not found" if res.code == '404'
81
+ raise "#{res.code} \"#{res.message}\"" unless res.code == '200'
94
82
  json = JSON.parse(res.body)
95
83
  score = Score.parse_json(json['score'])
96
- unless score.valid?
97
- @log.error("#{address}: invalid score: #{score}")
98
- return false
99
- end
100
- if score.expired?
101
- @log.error("#{address}: score expired: #{score}")
102
- return false
103
- end
104
- if score.strength < Score::STRENGTH && !opts['ignore-score-weakness']
105
- @log.error("#{address} score is too weak (#{score.strength}<#{Score::STRENGTH}): #{score}")
106
- return false
107
- end
84
+ raise "Invalid score #{score}" unless score.valid?
85
+ raise "Score expired #{score}" if score.expired?
86
+ raise "Score is too weak #{score.strength}" if score.strength < Score::STRENGTH && !opts['ignore-score-weakness']
108
87
  cps.add(json['body'], score.host, score.port, score.value)
109
- @log.info("#{address} #{json['body'].length}b/#{Rainbow(score.value).green} (#{json['version']})")
110
- true
88
+ @log.info("#{r} #{json['body'].length}b/#{Rainbow(score.value).green} (#{json['version']})")
111
89
  end
112
90
  end
113
91
  end
@@ -112,6 +112,9 @@ module Zold
112
112
  threads: opts[:threads], strength: opts[:strength]
113
113
  )
114
114
  Zold::Front.set(:farm, farm)
115
+ Thread.start do
116
+ Zold::Remote.new(remotes: remotes, farm: farm, log: @log).run(%w[remote update]) unless best.nil?
117
+ end
115
118
  @log.debug('Starting up the web front...')
116
119
  begin
117
120
  Zold::Front.run!
@@ -49,44 +49,27 @@ Available options:"
49
49
  mine = Args.new(opts, @log).take || return
50
50
  mine = @wallets.all if mine.empty?
51
51
  mine.each do |id|
52
- push(@wallets.find(Id.new(id)), opts)
52
+ wallet = @wallets.find(Id.new(id))
53
+ raise "The wallet #{id} is absent" unless wallet.exists?
54
+ push(wallet, opts)
53
55
  end
54
56
  end
55
57
 
56
58
  def push(wallet, _)
57
- raise 'The wallet is absent' unless wallet.exists?
58
59
  total = 0
59
- @remotes.all.each do |r|
60
- uri = URI("#{r[:home]}wallet/#{wallet.id}")
61
- response = Http.new(uri).put(File.read(wallet.path))
60
+ @remotes.iterate(@log) do |r|
61
+ response = r.http("/wallet/#{wallet.id}").put(File.read(wallet.path))
62
62
  if response.code == '304'
63
- @log.info("#{uri}: same version there")
64
- next
65
- end
66
- unless response.code == '200'
67
- @remotes.error(r[:host], r[:port])
68
- @log.error("#{uri} failed as #{response.code}/#{response.message}")
69
- @log.debug(response.body) unless response.body.empty?
63
+ @log.info("#{r}: same version there")
70
64
  next
71
65
  end
66
+ raise "#{response.code} \"#{response.message}\" at #{response.body}" unless response.code == '200'
72
67
  json = JSON.parse(response.body)['score']
73
68
  score = Score.parse_json(json)
74
- unless score.valid?
75
- @remotes.error(r[:host], r[:port])
76
- @log.error("#{uri} invalid score: #{score}")
77
- next
78
- end
79
- if score.expired?
80
- @remotes.error(r[:host], r[:port])
81
- @log.error("#{uri} expired score: #{score}")
82
- next
83
- end
84
- if score.strength < Score::STRENGTH
85
- @remotes.error(r[:host], r[:port])
86
- @log.error("#{uri} score is too weak")
87
- next
88
- end
89
- @log.info("#{uri} accepted: #{Rainbow(score.value).green}")
69
+ raise "Invalid score #{score}" unless score.valid?
70
+ raise "Expired score #{score}" if score.expired?
71
+ raise "Score is too weak #{score}" if score.strength < Score::STRENGTH
72
+ @log.info("#{r} accepted: #{Rainbow(score.value).green}")
90
73
  total += score.value
91
74
  end
92
75
  @log.info("Total score is #{total}")
@@ -115,42 +115,15 @@ Available options:"
115
115
 
116
116
  def update(opts, deep = true)
117
117
  capacity = []
118
- @remotes.all.each do |r|
119
- uri = URI("#{r[:home]}remotes")
120
- res = Http.new(uri).get
121
- unless res.code == '200'
122
- @remotes.error(r[:host], r[:port])
123
- @log.info("#{Rainbow(r[:host]).red} #{res.code} \"#{res.message}\" #{uri}")
124
- next
125
- end
126
- begin
127
- json = JSON.parse(res.body)
128
- rescue JSON::ParserError => e
129
- error(r[:host], r[:port])
130
- @log.info("#{Rainbow(r[:host]).red} \"#{e.message}\": #{res.body}")
131
- next
132
- end
118
+ @remotes.iterate(@log) do |r|
119
+ res = r.http('/remotes').get
120
+ raise "#{res.code} \"#{res.message}\"" unless res.code == '200'
121
+ json = JSON.parse(res.body)
133
122
  score = Score.parse_json(json['score'])
134
- unless score.valid?
135
- error(r[:host], r[:port])
136
- @log.info("#{Rainbow(r[:host]).red} invalid score: #{score}")
137
- next
138
- end
139
- if score.expired?
140
- error(r[:host], r[:port])
141
- @log.info("#{Rainbow(r[:host]).red} expired score: #{score}")
142
- next
143
- end
144
- if score.strength < Score::STRENGTH && !opts['ignore-score-weakness']
145
- error(r[:host], r[:port])
146
- @log.info("#{Rainbow(r[:host]).red} score too weak: #{score.strength}")
147
- next
148
- end
149
- if r[:host] != score.host || r[:port] != score.port
150
- @remotes.error(r[:host], r[:port])
151
- @remotes.add(score.host, score.port)
152
- @log.info("#{r[:host]}:#{r[:port]} renamed to #{score.host}:#{score.port}")
153
- end
123
+ raise "Invalid score #{score}" unless score.valid?
124
+ raise "Expired score #{score}" if score.expired?
125
+ raise "Score too weak: #{score.strength}" if score.strength < Score::STRENGTH && !opts['ignore-score-weakness']
126
+ raise "Masqueraded as #{score.host}:#{score.port}" if r.host != score.host || r.port != score.port
154
127
  @remotes.rescore(score.host, score.port, score.value)
155
128
  if deep
156
129
  json['all'].each do |s|
@@ -158,7 +131,7 @@ Available options:"
158
131
  end
159
132
  end
160
133
  capacity << { host: score.host, port: score.port, count: json['all'].count }
161
- @log.info("#{r[:host]}:#{r[:port]}: #{Rainbow(score.value).green} (#{json['version']})")
134
+ @log.info("#{r}: #{Rainbow(score.value).green} (#{json['version']})")
162
135
  end
163
136
  max_capacity = capacity.map { |c| c[:count] }.max || 0
164
137
  capacity.each do |c|
@@ -117,37 +117,15 @@ Available options:"
117
117
 
118
118
  def top_scores
119
119
  best = []
120
- @remotes.all.each do |r|
121
- uri = URI(r[:home])
122
- name = "#{r[:host]}:#{r[:port]}"
123
- res = Http.new(uri).get
124
- unless res.code == '200'
125
- @log.info("#{name}: #{Rainbow(res.code).red} \"#{res.message}\" at #{uri}")
126
- next
127
- end
128
- begin
129
- json = JSON.parse(res.body)
130
- rescue JSON::ParserError => e
131
- @log.info("#{name}: #{Rainbow('broken').red} JSON \"#{e.message}\": #{res.body}")
132
- next
133
- end
120
+ @remotes.iterate(@log) do |r|
121
+ res = r.http.get
122
+ raise "#{res.code} \"#{res.message}\" at #{res.body}" unless res.code == '200'
123
+ json = JSON.parse(res.body)
134
124
  score = Score.parse_json(json['score'])
135
- unless score.valid?
136
- @log.info("#{name}: #{Rainbow('invalid').red} score")
137
- next
138
- end
139
- if score.expired?
140
- @log.info("#{name}: #{Rainbow('expired').red} score")
141
- next
142
- end
143
- if score.strength < Score::STRENGTH
144
- @log.info("#{name} score #{Rainbow(score.value).red} is too weak (<#{Score::STRENGTH})")
145
- next
146
- end
147
- if score.value < Tax::EXACT_SCORE
148
- @log.info("#{name} score #{Rainbow(score.value).red} is too small (<#{Tax::EXACT_SCORE})")
149
- next
150
- end
125
+ raise "Invalid score #{score}" unless score.valid?
126
+ raise "Expired score #{score}" if score.expired?
127
+ raise "Score is too weak (<#{Score::STRENGTH}) #{score}" if score.strength < Score::STRENGTH
128
+ raise "Score is too small (<#{Tax::EXACT_SCORE})" if score.value < Tax::EXACT_SCORE
151
129
  @log.info("#{score.host}:#{score.port}: #{Rainbow(score.value).green}")
152
130
  best << score
153
131
  end
data/lib/zold/http.rb CHANGED
@@ -75,7 +75,7 @@ module Zold
75
75
  'Connection': 'close'
76
76
  }
77
77
  headers[Http::VERSION_HEADER] = VERSION
78
- headers[Http::SCORE_HEADER] = score.reduced(4).to_s if @score.valid? && @score.value >= 3 && !score.expired?
78
+ headers[Http::SCORE_HEADER] = @score.reduced(4).to_s if @score.valid? && @score.value >= 3 && !score.expired?
79
79
  headers
80
80
  end
81
81
  end
@@ -29,6 +29,13 @@ require_relative '../score'
29
29
  module Zold
30
30
  # Farm
31
31
  class Farm
32
+ # Empty farm
33
+ class Empty
34
+ def best
35
+ []
36
+ end
37
+ end
38
+
32
39
  attr_reader :best
33
40
  def initialize(invoice, log: Log::Quiet.new)
34
41
  @log = log
data/lib/zold/remotes.rb CHANGED
@@ -21,6 +21,7 @@
21
21
  require 'csv'
22
22
  require 'uri'
23
23
  require 'fileutils'
24
+ require_relative 'node/farm'
24
25
 
25
26
  # The list of remotes.
26
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -29,6 +30,7 @@ require 'fileutils'
29
30
  module Zold
30
31
  # All remotes
31
32
  class Remotes
33
+ # The default TCP port all nodes are supposed to use.
32
34
  PORT = 4096
33
35
 
34
36
  # Empty, for standalone mode
@@ -36,10 +38,34 @@ module Zold
36
38
  def all
37
39
  []
38
40
  end
41
+
42
+ def iterate(_)
43
+ # Nothing to do here
44
+ end
45
+ end
46
+
47
+ # One remote.
48
+ class Remote
49
+ attr_reader :host, :port
50
+ def initialize(host, port, score)
51
+ @host = host
52
+ @port = port
53
+ @score = score
54
+ end
55
+
56
+ def http(path = '/')
57
+ uri = URI("http://#{@host}:#{@port}#{path}")
58
+ Http.new(uri, @score)
59
+ end
60
+
61
+ def to_s
62
+ "#{@host}:#{@port}"
63
+ end
39
64
  end
40
65
 
41
- def initialize(file)
66
+ def initialize(file, farm = Farm::Empty.new)
42
67
  @file = file
68
+ @farm = farm
43
69
  end
44
70
 
45
71
  def all
@@ -89,16 +115,19 @@ module Zold
89
115
  save(list)
90
116
  end
91
117
 
92
- def score(host, port = Remotes::PORT)
93
- raise 'Port has to be of type Integer' unless port.is_a?(Integer)
94
- raise "#{host}:#{port} is absent" unless exists?(host, port)
95
- load.find { |r| r[:host] == host.downcase && r[:port] == port }[:score]
96
- end
97
-
98
- def errors(host, port = Remotes::PORT)
99
- raise 'Port has to be of type Integer' unless port.is_a?(Integer)
100
- raise "#{host}:#{port} is absent" unless exists?(host, port)
101
- load.find { |r| r[:host] == host.downcase && r[:port] == port }[:errors]
118
+ def iterate(log)
119
+ best = @farm.best[0]
120
+ require_relative 'score'
121
+ score = best.nil? ? Score::ZERO : best
122
+ all.each do |r|
123
+ begin
124
+ yield Remotes::Remote.new(r[:host], r[:port], score)
125
+ rescue StandardError => e
126
+ error(r[:host], r[:port])
127
+ log.info("#{Rainbow("#{r[:host]}:#{r[:port]}").red}: #{e.message}")
128
+ log.debug(e.backtrace[0..5].join("\n\t"))
129
+ end
130
+ end
102
131
  end
103
132
 
104
133
  def error(host, port = Remotes::PORT)
data/lib/zold/score.rb CHANGED
@@ -48,10 +48,8 @@ module Zold
48
48
  @strength = strength
49
49
  end
50
50
 
51
- ZERO = Score.new(
52
- Time.now, 'localhost',
53
- Remotes::PORT, 'NOPREFIX@ffffffffffffffff'
54
- )
51
+ # The default no-value score.
52
+ ZERO = Score.new(Time.now, 'localhost', 80, 'NOPREFIX@ffffffffffffffff')
55
53
 
56
54
  def self.parse_json(json)
57
55
  raise "Time in JSON is broken: #{json}" unless json['time'] =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
data/lib/zold/version.rb CHANGED
@@ -23,5 +23,5 @@
23
23
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
24
24
  # License:: MIT
25
25
  module Zold
26
- VERSION = '0.9.10'.freeze
26
+ VERSION = '0.9.11'.freeze
27
27
  end
data/test/test_remotes.rb CHANGED
@@ -65,7 +65,9 @@ class TestRemotes < Minitest::Test
65
65
  remotes = Zold::Remotes.new(file)
66
66
  remotes.add('127.0.0.1', 80)
67
67
  remotes.rescore('127.0.0.1', 80, 15)
68
- assert_equal(remotes.score('127.0.0.1', 80), 15)
68
+ remotes.all.each do |r|
69
+ assert_equal(15, r[:score])
70
+ end
69
71
  end
70
72
  end
71
73
  end
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.9.10
4
+ version: 0.9.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko