zold 0.15.0 → 0.16.0
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 +4 -4
- data/.gitignore +1 -0
- data/bin/zold +9 -6
- data/features/gem_package.feature +1 -1
- data/features/step_definitions/steps.rb +2 -2
- data/fixtures/merge/into-no-wallet/assert.rb +1 -1
- data/fixtures/merge/random-expenses/assert.rb +1 -1
- data/fixtures/merge/simple-case/assert.rb +1 -1
- data/lib/zold/age.rb +16 -8
- data/lib/zold/amount.rb +4 -2
- data/lib/zold/cached_wallets.rb +5 -6
- data/lib/zold/commands/clean.rb +15 -8
- data/lib/zold/commands/diff.rb +3 -3
- data/lib/zold/commands/fetch.rb +4 -4
- data/lib/zold/commands/list.rb +5 -4
- data/lib/zold/commands/merge.rb +2 -1
- data/lib/zold/commands/node.rb +14 -12
- data/lib/zold/commands/propagate.rb +2 -2
- data/lib/zold/commands/push.rb +3 -3
- data/lib/zold/commands/remove.rb +4 -1
- data/lib/zold/commands/routines/reconnect.rb +1 -0
- data/lib/zold/copies.rb +13 -14
- data/lib/zold/dir_items.rb +44 -0
- data/lib/zold/head.rb +1 -1
- data/lib/zold/key.rb +2 -2
- data/lib/zold/log.rb +2 -0
- data/lib/zold/node/async_entrance.rb +38 -28
- data/lib/zold/node/entrance.rb +4 -11
- data/lib/zold/node/farm.rb +9 -9
- data/lib/zold/node/front.rb +40 -25
- data/lib/zold/node/nodup_entrance.rb +4 -4
- data/lib/zold/node/safe_entrance.rb +2 -2
- data/lib/zold/node/spread_entrance.rb +5 -9
- data/lib/zold/node/sync_entrance.rb +2 -23
- data/lib/zold/patch.rb +2 -2
- data/lib/zold/remotes.rb +10 -6
- data/lib/zold/sync_wallets.rb +3 -22
- data/lib/zold/tree_wallets.rb +11 -6
- data/lib/zold/txns.rb +1 -1
- data/lib/zold/version.rb +1 -1
- data/lib/zold/wallet.rb +14 -5
- data/lib/zold/wallets.rb +5 -4
- data/test/commands/routines/test_spread.rb +1 -1
- data/test/commands/test_alias.rb +4 -4
- data/test/commands/test_clean.rb +14 -1
- data/test/commands/test_create.rb +2 -2
- data/test/commands/test_diff.rb +5 -5
- data/test/commands/test_fetch.rb +2 -2
- data/test/commands/test_merge.rb +19 -19
- data/test/commands/test_node.rb +1 -1
- data/test/commands/test_pay.rb +7 -6
- data/test/commands/test_propagate.rb +2 -1
- data/test/commands/test_pull.rb +1 -1
- data/test/commands/test_push.rb +1 -1
- data/test/commands/test_remove.rb +57 -0
- data/test/commands/test_taxes.rb +1 -1
- data/test/fake_home.rb +11 -8
- data/test/node/fake_node.rb +2 -2
- data/test/node/test_async_entrance.rb +24 -8
- data/test/node/test_emission.rb +2 -2
- data/test/node/test_entrance.rb +8 -6
- data/test/node/test_farm.rb +1 -1
- data/test/node/test_front.rb +42 -33
- data/test/node/test_nodup_entrance.rb +2 -2
- data/test/node/test_safe_entrance.rb +5 -5
- data/test/node/test_spread_entrance.rb +3 -3
- data/test/node/test_sync_entrance.rb +1 -1
- data/test/test__helper.rb +3 -29
- data/test/test_cached_wallets.rb +1 -1
- data/test/test_copies.rb +4 -2
- data/test/test_dir_items.rb +88 -0
- data/test/test_key.rb +2 -2
- data/test/test_log.rb +38 -0
- data/test/test_patch.rb +11 -11
- data/test/test_prefixes.rb +1 -1
- data/test/test_remotes.rb +12 -6
- data/test/test_sync_wallets.rb +6 -6
- data/test/test_tax.rb +4 -4
- data/test/test_tree_wallets.rb +16 -2
- data/test/test_wallet.rb +26 -26
- data/test/test_wallets.rb +5 -2
- data/test/test_zold.rb +2 -2
- data/test/upgrades/test_protocol_up.rb +1 -1
- data/upgrades/move_wallets_into_tree.rb +1 -1
- data/upgrades/protocol_up.rb +3 -3
- data/upgrades/rename_foreign_wallets.rb +1 -1
- data/zold.gemspec +27 -25
- metadata +91 -56
@@ -47,6 +47,7 @@ module Zold
|
|
47
47
|
score = @farm.best[0]
|
48
48
|
args << "--ignore-node=#{score.host}:#{score.port}" if score
|
49
49
|
cmd.run(args + ['defaults']) unless @opts['routine-immediately']
|
50
|
+
return if @opts['routine-immediately'] && @remotes.all.empty?
|
50
51
|
cmd.run(args + ['trim'])
|
51
52
|
cmd.run(args + ['select'])
|
52
53
|
cmd.run(args + ['update'] + (@opts['never-reboot'] ? [] : ['--reboot']))
|
data/lib/zold/copies.rb
CHANGED
@@ -22,10 +22,12 @@
|
|
22
22
|
|
23
23
|
require 'time'
|
24
24
|
require 'csv'
|
25
|
+
require 'futex'
|
25
26
|
require 'backtrace'
|
26
27
|
require_relative 'log'
|
27
28
|
require_relative 'size'
|
28
29
|
require_relative 'wallet'
|
30
|
+
require_relative 'dir_items'
|
29
31
|
|
30
32
|
# The list of copies.
|
31
33
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -38,15 +40,12 @@ module Zold
|
|
38
40
|
EXT = '.zc'
|
39
41
|
|
40
42
|
def initialize(dir, log: Log::Quiet.new)
|
41
|
-
raise 'Dir can\'t be nil' if dir.nil?
|
42
43
|
@dir = dir
|
43
|
-
raise 'Log can\'t be nil' if log.nil?
|
44
44
|
@log = log
|
45
|
-
@mutex = Mutex.new
|
46
45
|
end
|
47
46
|
|
48
47
|
def root
|
49
|
-
File.join(@dir, '..')
|
48
|
+
File.expand_path(File.join(@dir, '..'))
|
50
49
|
end
|
51
50
|
|
52
51
|
def to_s
|
@@ -54,7 +53,7 @@ module Zold
|
|
54
53
|
end
|
55
54
|
|
56
55
|
def clean
|
57
|
-
@
|
56
|
+
Futex.new(file, log: @log).open do
|
58
57
|
list = load
|
59
58
|
list.reject! { |s| s[:time] < Time.now - 24 * 60 * 60 }
|
60
59
|
save(list)
|
@@ -74,7 +73,7 @@ module Zold
|
|
74
73
|
wallet.refurbish
|
75
74
|
raise "Invalid protocol #{wallet.protocol} in #{file}" unless wallet.protocol == Zold::PROTOCOL
|
76
75
|
rescue StandardError => e
|
77
|
-
|
76
|
+
FileUtils.rm_rf(file)
|
78
77
|
@log.debug("Copy at #{f} deleted: #{Backtrace.new(e)}")
|
79
78
|
deleted += 1
|
80
79
|
end
|
@@ -84,7 +83,7 @@ module Zold
|
|
84
83
|
end
|
85
84
|
|
86
85
|
def remove(host, port)
|
87
|
-
@
|
86
|
+
Futex.new(file, log: @log).open do
|
88
87
|
save(load.reject { |s| s[:host] == host && s[:port] == port })
|
89
88
|
end
|
90
89
|
end
|
@@ -98,21 +97,21 @@ module Zold
|
|
98
97
|
raise "Time must be in the past: #{time}" if time > Time.now
|
99
98
|
raise 'Score must be Integer' unless score.is_a?(Integer)
|
100
99
|
raise "Score can't be negative: #{score}" if score.negative?
|
101
|
-
@
|
100
|
+
Futex.new(file, log: @log).open do
|
102
101
|
FileUtils.mkdir_p(@dir)
|
103
102
|
list = load
|
104
103
|
target = list.find do |s|
|
105
104
|
f = File.join(@dir, "#{s[:name]}#{Copies::EXT}")
|
106
|
-
File.exist?(f) &&
|
105
|
+
File.exist?(f) && IO.read(f) == content
|
107
106
|
end
|
108
107
|
if target.nil?
|
109
|
-
max =
|
108
|
+
max = DirItems.new(@dir).fetch
|
110
109
|
.select { |f| File.basename(f, Copies::EXT) =~ /^[0-9]+$/ }
|
111
110
|
.map(&:to_i)
|
112
111
|
.max
|
113
112
|
max = 0 if max.nil?
|
114
113
|
name = (max + 1).to_s
|
115
|
-
|
114
|
+
IO.write(File.join(@dir, "#{name}#{Copies::EXT}"), content)
|
116
115
|
else
|
117
116
|
name = target[:name]
|
118
117
|
end
|
@@ -130,7 +129,7 @@ module Zold
|
|
130
129
|
end
|
131
130
|
|
132
131
|
def all
|
133
|
-
@
|
132
|
+
Futex.new(file, log: @log).open do
|
134
133
|
load.group_by { |s| s[:name] }.map do |name, scores|
|
135
134
|
{
|
136
135
|
name: name,
|
@@ -160,7 +159,7 @@ module Zold
|
|
160
159
|
private
|
161
160
|
|
162
161
|
def save(list)
|
163
|
-
|
162
|
+
IO.write(
|
164
163
|
file,
|
165
164
|
list.map do |r|
|
166
165
|
[
|
@@ -173,7 +172,7 @@ module Zold
|
|
173
172
|
end
|
174
173
|
|
175
174
|
def files
|
176
|
-
|
175
|
+
DirItems.new(@dir).fetch.select { |f| File.basename(f, Copies::EXT) =~ /^[0-9]+$/ }
|
177
176
|
end
|
178
177
|
|
179
178
|
def file
|
@@ -0,0 +1,44 @@
|
|
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
|
+
# Items in a directory.
|
24
|
+
#
|
25
|
+
# We need this class because Dir.new() from Ruby is blocking. It doesn't
|
26
|
+
# allow to write and read to any files in a directory, while listing it.
|
27
|
+
# More: https://stackoverflow.com/questions/52987672/
|
28
|
+
#
|
29
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
30
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
31
|
+
# License:: MIT
|
32
|
+
module Zold
|
33
|
+
# Items in a dir
|
34
|
+
class DirItems
|
35
|
+
def initialize(dir)
|
36
|
+
@dir = dir
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch(recursive: true)
|
40
|
+
txt = `find #{@dir} #{recursive ? '' : '-maxdepth 1'} -type f -print 2>/dev/null`
|
41
|
+
txt.nil? ? [] : txt.strip.split(' ').map { |f| f[(@dir.length + 1)..-1] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/zold/head.rb
CHANGED
@@ -39,7 +39,7 @@ module Zold
|
|
39
39
|
|
40
40
|
def fetch
|
41
41
|
raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
|
42
|
-
lines =
|
42
|
+
lines = IO.read(@file).split(/\n/)
|
43
43
|
raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
|
44
44
|
lines.take(4)
|
45
45
|
end
|
data/lib/zold/key.rb
CHANGED
@@ -37,7 +37,7 @@ module Zold
|
|
37
37
|
unless file.nil?
|
38
38
|
path = File.expand_path(file)
|
39
39
|
raise "Can't find RSA key at #{file} (#{path})" unless File.exist?(path)
|
40
|
-
return
|
40
|
+
return IO.read(path)
|
41
41
|
end
|
42
42
|
unless text.nil?
|
43
43
|
return text if text.start_with?('-----')
|
@@ -77,7 +77,7 @@ module Zold
|
|
77
77
|
text = @body.call.strip
|
78
78
|
unless text.start_with?('-----BEGIN')
|
79
79
|
Tempfile.open do |f|
|
80
|
-
|
80
|
+
IO.write(f.path, text)
|
81
81
|
text = `ssh-keygen -f #{f.path} -e -m pem`
|
82
82
|
end
|
83
83
|
end
|
data/lib/zold/log.rb
CHANGED
@@ -54,6 +54,7 @@ module Zold
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def debug(msg)
|
57
|
+
return unless debug?
|
57
58
|
@mutex.synchronize do
|
58
59
|
@log.debug(msg)
|
59
60
|
end
|
@@ -64,6 +65,7 @@ module Zold
|
|
64
65
|
end
|
65
66
|
|
66
67
|
def info(msg)
|
68
|
+
return unless info?
|
67
69
|
@mutex.synchronize do
|
68
70
|
@log.info(msg)
|
69
71
|
end
|
@@ -21,11 +21,13 @@
|
|
21
21
|
# SOFTWARE.
|
22
22
|
|
23
23
|
require 'concurrent'
|
24
|
+
require 'futex'
|
24
25
|
require_relative '../log'
|
25
26
|
require_relative '../age'
|
26
27
|
require_relative '../size'
|
27
28
|
require_relative '../id'
|
28
29
|
require_relative '../verbose_thread'
|
30
|
+
require_relative '../dir_items'
|
29
31
|
|
30
32
|
# The async entrance of the web front.
|
31
33
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -35,29 +37,25 @@ module Zold
|
|
35
37
|
# The entrance
|
36
38
|
class AsyncEntrance
|
37
39
|
# How many threads to use for processing
|
38
|
-
THREADS = Concurrent.processor_count
|
40
|
+
THREADS = [Concurrent.processor_count, 4].max
|
39
41
|
|
40
42
|
# Queue length
|
41
43
|
MAX_QUEUE = Concurrent.processor_count * 64
|
42
44
|
|
43
45
|
def initialize(entrance, dir, log: Log::Quiet.new)
|
44
|
-
raise 'Entrance can\'t be nil' if entrance.nil?
|
45
46
|
@entrance = entrance
|
46
|
-
raise 'Directory can\'t be nil' if dir.nil?
|
47
|
-
raise 'Directory must be of type String' unless dir.is_a?(String)
|
48
47
|
@dir = dir
|
49
|
-
raise 'Log can\'t be nil' if log.nil?
|
50
48
|
@log = log
|
51
49
|
@mutex = Mutex.new
|
52
50
|
end
|
53
51
|
|
54
52
|
def to_json
|
53
|
+
opts = queue
|
55
54
|
json = {
|
56
|
-
'queue':
|
55
|
+
'queue': opts.count,
|
57
56
|
'pool.length': @pool.length,
|
58
57
|
'pool.running': @pool.running?
|
59
58
|
}
|
60
|
-
opts = queue
|
61
59
|
json['queue_age'] = opts.empty? ? 0 : Time.now - File.mtime(File.join(@dir, opts[0]))
|
62
60
|
@entrance.to_json.merge(json)
|
63
61
|
end
|
@@ -66,7 +64,9 @@ module Zold
|
|
66
64
|
@entrance.start do
|
67
65
|
FileUtils.mkdir_p(@dir)
|
68
66
|
@pool = Concurrent::FixedThreadPool.new(
|
69
|
-
AsyncEntrance::THREADS,
|
67
|
+
AsyncEntrance::THREADS,
|
68
|
+
max_queue: AsyncEntrance::MAX_QUEUE,
|
69
|
+
fallback_policy: :abort
|
70
70
|
)
|
71
71
|
AsyncEntrance::THREADS.times do |t|
|
72
72
|
@pool.post do
|
@@ -80,14 +80,21 @@ module Zold
|
|
80
80
|
end
|
81
81
|
begin
|
82
82
|
yield(self)
|
83
|
+
cycle = 0
|
84
|
+
until queue.empty?
|
85
|
+
@log.info("Stopping async entrance, #{queue.count} still in the queue (cycle=#{cycle})...")
|
86
|
+
cycle += 1
|
87
|
+
raise "Can't wait for async entrance to stop for so long" if cycle > 10
|
88
|
+
sleep 1
|
89
|
+
end
|
83
90
|
ensure
|
84
91
|
@log.info("Stopping async entrance, pool length is #{@pool.length}, queue length is #{@pool.queue_length}")
|
85
92
|
@pool.shutdown
|
86
93
|
if @pool.wait_for_termination(10)
|
87
|
-
@log.info(
|
94
|
+
@log.info("Async entrance terminated peacefully with #{queue.count} wallets left in the queue")
|
88
95
|
else
|
89
96
|
@pool.kill
|
90
|
-
@log.info(
|
97
|
+
@log.info("Async entrance was killed, #{queue.count} wallets left in the queue")
|
91
98
|
end
|
92
99
|
end
|
93
100
|
end
|
@@ -96,36 +103,39 @@ module Zold
|
|
96
103
|
# Always returns an array with a single ID of the pushed wallet
|
97
104
|
def push(id, body)
|
98
105
|
raise "Queue is too long (#{queue.count} wallets), try again later" if queue.count > AsyncEntrance::MAX_QUEUE
|
99
|
-
|
100
|
-
|
106
|
+
start = Time.now
|
107
|
+
Futex.new(file(id), log: @log).open do |f|
|
108
|
+
IO.write(f, body)
|
101
109
|
end
|
110
|
+
@log.debug("Added #{id}/#{Size.new(body.length)} to the queue at pos.#{queue.count} \
|
111
|
+
in #{Age.new(start, limit: 0.05)}")
|
102
112
|
[id]
|
103
113
|
end
|
104
114
|
|
105
115
|
private
|
106
116
|
|
107
117
|
def take
|
108
|
-
id = ''
|
109
|
-
body = ''
|
110
|
-
@mutex.synchronize do
|
111
|
-
opts = queue
|
112
|
-
unless opts.empty?
|
113
|
-
file = File.join(@dir, opts[0])
|
114
|
-
id = opts[0]
|
115
|
-
body = File.read(file)
|
116
|
-
File.delete(file)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
return if id.empty? || body.empty?
|
120
118
|
start = Time.now
|
119
|
+
opts = queue
|
120
|
+
return if opts.empty?
|
121
|
+
id = opts[0]
|
122
|
+
body = Futex.new(file(id), log: @log).open do |f|
|
123
|
+
b = File.exist?(f) ? IO.read(f) : ''
|
124
|
+
FileUtils.rm_f(f)
|
125
|
+
b
|
126
|
+
end
|
127
|
+
return if body.empty?
|
121
128
|
@entrance.push(Id.new(id), body)
|
122
|
-
@log.debug("Pushed #{id}/#{Size.new(body.length)} to #{@entrance.class.name}
|
129
|
+
@log.debug("Pushed #{id}/#{Size.new(body.length)} to #{@entrance.class.name} \
|
130
|
+
in #{Age.new(start, limit: 0.1)} (#{queue.count} still in the queue)")
|
123
131
|
end
|
124
132
|
|
125
133
|
def queue
|
126
|
-
|
127
|
-
|
128
|
-
|
134
|
+
DirItems.new(@dir).fetch.select { |f| f =~ /^[0-9a-f]{16}$/ }
|
135
|
+
end
|
136
|
+
|
137
|
+
def file(id)
|
138
|
+
File.join(@dir, id.to_s)
|
129
139
|
end
|
130
140
|
end
|
131
141
|
end
|
data/lib/zold/node/entrance.rb
CHANGED
@@ -25,6 +25,7 @@ require_relative '../log'
|
|
25
25
|
require_relative '../remotes'
|
26
26
|
require_relative '../copies'
|
27
27
|
require_relative '../tax'
|
28
|
+
require_relative '../age'
|
28
29
|
require_relative '../commands/clean'
|
29
30
|
require_relative '../commands/merge'
|
30
31
|
require_relative '../commands/fetch'
|
@@ -38,19 +39,11 @@ module Zold
|
|
38
39
|
# The entrance
|
39
40
|
class Entrance
|
40
41
|
def initialize(wallets, remotes, copies, address, log: Log::Quiet.new, network: 'test')
|
41
|
-
raise 'Wallets can\'t be nil' if wallets.nil?
|
42
|
-
raise 'Wallets must implement the contract of Wallets: method #find is required' unless wallets.respond_to?(:find)
|
43
42
|
@wallets = wallets
|
44
|
-
raise 'Remotes can\'t be nil' if remotes.nil?
|
45
|
-
raise "Remotes must be of type Remotes: #{remotes.class.name}" unless remotes.is_a?(Remotes)
|
46
43
|
@remotes = remotes
|
47
|
-
raise 'Copies can\'t be nil' if copies.nil?
|
48
44
|
@copies = copies
|
49
|
-
raise 'Address can\'t be nil' if address.nil?
|
50
45
|
@address = address
|
51
|
-
raise 'Log can\'t be nil' if log.nil?
|
52
46
|
@log = log
|
53
|
-
raise 'Network can\'t be nil' if network.nil?
|
54
47
|
@network = network
|
55
48
|
@history = []
|
56
49
|
@speed = []
|
@@ -88,12 +81,12 @@ module Zold
|
|
88
81
|
).run(['merge', id.to_s])
|
89
82
|
Clean.new(wallets: @wallets, copies: copies.root, log: @log).run(['clean', id.to_s])
|
90
83
|
copies.remove(localhost, Remotes::PORT)
|
91
|
-
sec = (Time.now - start).round(2)
|
92
84
|
if modified.empty?
|
93
|
-
@log.info("Accepted #{id} in #{
|
85
|
+
@log.info("Accepted #{id} in #{Age.new(start, limit: 1)} and not modified anything")
|
94
86
|
else
|
95
|
-
@log.info("Accepted #{id} in #{
|
87
|
+
@log.info("Accepted #{id} in #{Age.new(start, limit: 1)} and modified #{modified.join(', ')}")
|
96
88
|
end
|
89
|
+
sec = (Time.now - start).round(2)
|
97
90
|
@mutex.synchronize do
|
98
91
|
@history.shift if @history.length >= 16
|
99
92
|
@speed.shift if @speed.length >= 64
|
data/lib/zold/node/farm.rb
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
require 'time'
|
24
24
|
require 'open3'
|
25
25
|
require 'backtrace'
|
26
|
+
require 'futex'
|
26
27
|
require_relative '../log'
|
27
28
|
require_relative '../score'
|
28
29
|
require_relative '../age'
|
@@ -48,7 +49,6 @@ module Zold
|
|
48
49
|
@invoice = invoice
|
49
50
|
@pipeline = Queue.new
|
50
51
|
@threads = []
|
51
|
-
@mutex = Mutex.new
|
52
52
|
end
|
53
53
|
|
54
54
|
def best
|
@@ -127,14 +127,14 @@ module Zold
|
|
127
127
|
def finish(thread)
|
128
128
|
start = Time.now
|
129
129
|
@alive = false
|
130
|
-
@log.info("Attempting to terminate the thread \"#{thread.name}\"...")
|
130
|
+
@log.info("Attempting to terminate the thread \"#{thread.name}\" of the farm...")
|
131
131
|
loop do
|
132
132
|
delay = Time.now - start
|
133
133
|
if thread.join(0.1)
|
134
134
|
@log.info("Thread \"#{thread.name}\" finished in #{Age.new(start)}")
|
135
135
|
break
|
136
136
|
end
|
137
|
-
if delay >
|
137
|
+
if delay > 1
|
138
138
|
thread.exit
|
139
139
|
@log.error("Thread \"#{thread.name}\" forcefully terminated after #{Age.new(start)}")
|
140
140
|
end
|
@@ -220,9 +220,9 @@ module Zold
|
|
220
220
|
def save(threads, list = [])
|
221
221
|
scores = load + list
|
222
222
|
period = 24 * 60 * 60 / [threads, 1].max
|
223
|
-
@
|
224
|
-
|
225
|
-
|
223
|
+
Futex.new(@cache, log: @log).open do |f|
|
224
|
+
IO.write(
|
225
|
+
f,
|
226
226
|
scores.select(&:valid?)
|
227
227
|
.reject(&:expired?)
|
228
228
|
.sort_by(&:value)
|
@@ -237,9 +237,9 @@ module Zold
|
|
237
237
|
end
|
238
238
|
|
239
239
|
def load
|
240
|
-
@
|
241
|
-
if File.exist?(
|
242
|
-
|
240
|
+
Futex.new(@cache, log: @log).open do |f|
|
241
|
+
if File.exist?(f)
|
242
|
+
IO.read(f).split(/\n/)
|
243
243
|
.map { |t| parse_score_line(t) }
|
244
244
|
.reject(&:zero?)
|
245
245
|
else
|
data/lib/zold/node/front.rb
CHANGED
@@ -55,7 +55,7 @@ module Zold
|
|
55
55
|
set :suppress_messages, true
|
56
56
|
set :start, Time.now
|
57
57
|
set :lock, false
|
58
|
-
set :show_exceptions,
|
58
|
+
set :show_exceptions, true
|
59
59
|
set :server, :puma
|
60
60
|
set :log, nil? # to be injected at node.rb
|
61
61
|
set :trace, nil? # to be injected at node.rb
|
@@ -68,6 +68,7 @@ module Zold
|
|
68
68
|
set :nohup_log, false # to be injected at node.rb
|
69
69
|
set :home, nil? # to be injected at node.rb
|
70
70
|
set :logging, true # to be injected at node.rb
|
71
|
+
set :logger, nil? # to be injected at node.rb
|
71
72
|
set :address, nil? # to be injected at node.rb
|
72
73
|
set :farm, nil? # to be injected at node.rb
|
73
74
|
set :metronome, nil? # to be injected at node.rb
|
@@ -121,7 +122,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
121
122
|
headers[Http::PROTOCOL_HEADER] = settings.protocol.to_s
|
122
123
|
headers['Access-Control-Allow-Origin'] = '*'
|
123
124
|
headers[Http::SCORE_HEADER] = score.reduced(16).to_s
|
124
|
-
headers['X-Zold-Thread'] = Thread.current.name
|
125
|
+
headers['X-Zold-Thread'] = Thread.current.name.to_s
|
125
126
|
headers['X-Zold-Milliseconds'] = ((Time.now - @start) * 1000).round.to_s
|
126
127
|
end
|
127
128
|
|
@@ -155,7 +156,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
155
156
|
raise "Log not found at #{settings.nohup_log}" unless File.exist?(settings.nohup_log)
|
156
157
|
response.headers['Content-Type'] = 'text/plain'
|
157
158
|
response.headers['Content-Disposition'] = "attachment; filename='#{File.basename(settings.nohup_log)}'"
|
158
|
-
|
159
|
+
IO.read(settings.nohup_log)
|
159
160
|
end
|
160
161
|
|
161
162
|
get '/favicon.ico' do
|
@@ -177,12 +178,12 @@ while #{settings.address} is in '#{settings.network}'"
|
|
177
178
|
protocol: settings.protocol,
|
178
179
|
score: score.to_h,
|
179
180
|
pid: Process.pid,
|
180
|
-
cpus: Concurrent.processor_count,
|
181
|
+
cpus: Cachy.cache(:a_cpus) { Concurrent.processor_count },
|
181
182
|
memory: GetProcessMem.new.bytes.to_i,
|
182
183
|
platform: RUBY_PLATFORM,
|
183
|
-
load: Usagewatch.uw_load.to_f,
|
184
|
+
load: Cachy.cache(:a_load, expires_in: 5 * 60) { Usagewatch.uw_load.to_f },
|
184
185
|
threads: "#{Thread.list.select { |t| t.status == 'run' }.count}/#{Thread.list.count}",
|
185
|
-
wallets: Cachy.cache(:
|
186
|
+
wallets: Cachy.cache(:a_wallets, expires_in: 5 * 60) { settings.wallets.all.count },
|
186
187
|
remotes: settings.remotes.all.count,
|
187
188
|
nscore: settings.remotes.all.map { |r| r[:score] }.inject(&:+) || 0,
|
188
189
|
farm: settings.farm.to_json,
|
@@ -196,7 +197,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
196
197
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})} do
|
197
198
|
error 404 if settings.disable_fetch
|
198
199
|
id = Id.new(params[:id])
|
199
|
-
|
200
|
+
copy_of(id) do |wallet|
|
200
201
|
error 404 unless wallet.exists?
|
201
202
|
content_type 'application/json'
|
202
203
|
JSON.pretty_generate(
|
@@ -219,7 +220,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
219
220
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16}).json} do
|
220
221
|
error 404 if settings.disable_fetch
|
221
222
|
id = Id.new(params[:id])
|
222
|
-
|
223
|
+
copy_of(id) do |wallet|
|
223
224
|
error 404 unless wallet.exists?
|
224
225
|
content_type 'application/json'
|
225
226
|
JSON.pretty_generate(
|
@@ -241,7 +242,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
241
242
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/balance} do
|
242
243
|
error 404 if settings.disable_fetch
|
243
244
|
id = Id.new(params[:id])
|
244
|
-
|
245
|
+
copy_of(id) do |wallet|
|
245
246
|
error 404 unless wallet.exists?
|
246
247
|
content_type 'text/plain'
|
247
248
|
wallet.balance.to_i.to_s
|
@@ -251,7 +252,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
251
252
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/key} do
|
252
253
|
error 404 if settings.disable_fetch
|
253
254
|
id = Id.new(params[:id])
|
254
|
-
|
255
|
+
copy_of(id) do |wallet|
|
255
256
|
error 404 unless wallet.exists?
|
256
257
|
content_type 'text/plain'
|
257
258
|
wallet.key.to_pub
|
@@ -261,7 +262,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
261
262
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/mtime} do
|
262
263
|
error 404 if settings.disable_fetch
|
263
264
|
id = Id.new(params[:id])
|
264
|
-
|
265
|
+
copy_of(id) do |wallet|
|
265
266
|
error 404 unless wallet.exists?
|
266
267
|
content_type 'text/plain'
|
267
268
|
wallet.mtime.utc.iso8601.to_s
|
@@ -271,7 +272,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
271
272
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/digest} do
|
272
273
|
error 404 if settings.disable_fetch
|
273
274
|
id = Id.new(params[:id])
|
274
|
-
|
275
|
+
copy_of(id) do |wallet|
|
275
276
|
error 404 unless wallet.exists?
|
276
277
|
content_type 'text/plain'
|
277
278
|
wallet.digest
|
@@ -281,7 +282,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
281
282
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})\.txt} do
|
282
283
|
error 404 if settings.disable_fetch
|
283
284
|
id = Id.new(params[:id])
|
284
|
-
|
285
|
+
copy_of(id) do |wallet|
|
285
286
|
error 404 unless wallet.exists?
|
286
287
|
content_type 'text/plain'
|
287
288
|
[
|
@@ -305,17 +306,17 @@ while #{settings.address} is in '#{settings.network}'"
|
|
305
306
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})\.bin} do
|
306
307
|
error 404 if settings.disable_fetch
|
307
308
|
id = Id.new(params[:id])
|
308
|
-
|
309
|
+
copy_of(id) do |wallet|
|
309
310
|
error 404 unless wallet.exists?
|
310
311
|
content_type 'text/plain'
|
311
|
-
|
312
|
+
IO.read(wallet.path)
|
312
313
|
end
|
313
314
|
end
|
314
315
|
|
315
316
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/copies} do
|
316
317
|
error 404 if settings.disable_fetch
|
317
318
|
id = Id.new(params[:id])
|
318
|
-
|
319
|
+
copy_of(id) do |wallet|
|
319
320
|
error 404 unless wallet.exists?
|
320
321
|
content_type 'text/plain'
|
321
322
|
copies = Copies.new(File.join(settings.copies, id))
|
@@ -335,12 +336,12 @@ while #{settings.address} is in '#{settings.network}'"
|
|
335
336
|
error 404 if settings.disable_fetch
|
336
337
|
id = Id.new(params[:id])
|
337
338
|
name = params[:name]
|
338
|
-
|
339
|
+
copy_of(id) do |wallet|
|
339
340
|
error 404 unless wallet.exists?
|
340
341
|
copy = Copies.new(File.join(settings.copies, id)).all.find { |c| c[:name] == name }
|
341
342
|
error 404 if copy.nil?
|
342
343
|
content_type 'text/plain'
|
343
|
-
|
344
|
+
IO.read(copy[:path])
|
344
345
|
end
|
345
346
|
end
|
346
347
|
|
@@ -383,12 +384,15 @@ while #{settings.address} is in '#{settings.network}'"
|
|
383
384
|
|
384
385
|
get '/threads' do
|
385
386
|
content_type 'text/plain'
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
387
|
+
[
|
388
|
+
"Total threads: #{Thread.list.count}",
|
389
|
+
Thread.list.map do |t|
|
390
|
+
[
|
391
|
+
"#{t.name}: status=#{t.status}; alive=#{t.alive?}",
|
392
|
+
t.backtrace.nil? ? 'NO BACKTRACE' : " #{t.backtrace.join("\n ")}"
|
393
|
+
].join("\n")
|
394
|
+
end
|
395
|
+
].flatten.join("\n\n")
|
392
396
|
end
|
393
397
|
|
394
398
|
not_found do
|
@@ -421,9 +425,20 @@ while #{settings.address} is in '#{settings.network}'"
|
|
421
425
|
end
|
422
426
|
|
423
427
|
def score
|
424
|
-
best = settings.farm.best
|
428
|
+
best = Cachy.cache(:a_score, expires_in: 60) { settings.farm.best }
|
425
429
|
raise 'Score is empty, there is something wrong with the Farm!' if best.empty?
|
426
430
|
best[0]
|
427
431
|
end
|
432
|
+
|
433
|
+
def copy_of(id)
|
434
|
+
Tempfile.open([id.to_s, Wallet::EXT]) do |f|
|
435
|
+
settings.wallets.find(id) do |wallet|
|
436
|
+
IO.write(f, IO.read(wallet.path)) if File.exist?(wallet.path)
|
437
|
+
end
|
438
|
+
path = f.path
|
439
|
+
f.delete if File.size(f.path).zero?
|
440
|
+
yield Wallet.new(path)
|
441
|
+
end
|
442
|
+
end
|
428
443
|
end
|
429
444
|
end
|