zold 0.14.52 → 0.14.53
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/zold/commands/diff.rb +1 -2
- data/lib/zold/commands/node.rb +12 -4
- data/lib/zold/commands/push.rb +1 -2
- data/lib/zold/copies.rb +4 -4
- data/lib/zold/head.rb +1 -1
- data/lib/zold/key.rb +1 -2
- data/lib/zold/node/async_entrance.rb +1 -1
- data/lib/zold/node/farm.rb +3 -3
- data/lib/zold/node/front.rb +30 -7
- data/lib/zold/node/nodup_entrance.rb +1 -2
- data/lib/zold/patch.rb +2 -3
- data/lib/zold/remotes.rb +6 -8
- data/lib/zold/txns.rb +1 -2
- data/lib/zold/version.rb +1 -1
- data/lib/zold/wallet.rb +4 -3
- data/test/fake_home.rb +1 -2
- data/test/node/test_front.rb +29 -21
- data/test/test_zold.rb +1 -1
- data/zold.gemspec +1 -0
- metadata +16 -5
- data/lib/zold/atomic_file.rb +0 -52
- data/test/test_atomic_file.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12bf3dea4ff2630f3530269b7c5db959ad30cec8ab6346ba3336570c4a61822d
|
4
|
+
data.tar.gz: 1cb07c0e0ce665ededffc57a221ead9a235d99ba9eb391a8a75b1af6434bf093
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4788bfa23317ab20259ef9fae9266556d402caf07de529d8358d9541f61dca9f42b2921b6d15535e8b11583c4f79c130aae3fa82cda5cd8fd494aeb645f8fd8d
|
7
|
+
data.tar.gz: 2a929f263c3a0a2fa91e6cddc9436b273781c58f121753ad28191f4fa140b5df7b9f07bda75e84dcc448e6d30633695895906fac47089be453246b83f3665e83
|
data/lib/zold/commands/diff.rb
CHANGED
@@ -28,7 +28,6 @@ require_relative 'args'
|
|
28
28
|
require_relative '../log'
|
29
29
|
require_relative '../patch'
|
30
30
|
require_relative '../wallet'
|
31
|
-
require_relative '../atomic_file'
|
32
31
|
|
33
32
|
# DIFF command.
|
34
33
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -68,7 +67,7 @@ Available options:"
|
|
68
67
|
patch.join(Wallet.new(c[:path]))
|
69
68
|
end
|
70
69
|
before = @wallets.find(id) do |wallet|
|
71
|
-
|
70
|
+
File.read(wallet.path)
|
72
71
|
end
|
73
72
|
after = ''
|
74
73
|
Tempfile.open(['', Wallet::EXTENSION]) do |f|
|
data/lib/zold/commands/node.rb
CHANGED
@@ -135,6 +135,12 @@ module Zold
|
|
135
135
|
'Don\'t run the metronome',
|
136
136
|
required: true,
|
137
137
|
default: false
|
138
|
+
o.bool '--disable-push',
|
139
|
+
'Prohibit all PUSH requests',
|
140
|
+
default: false
|
141
|
+
o.bool '--disable-fetch',
|
142
|
+
'Prohibit all FETCH requests',
|
143
|
+
default: false
|
138
144
|
o.string '--alias',
|
139
145
|
'The alias of the node (default: host:port)',
|
140
146
|
require: false
|
@@ -160,6 +166,8 @@ module Zold
|
|
160
166
|
Front.set(:protocol, Zold::PROTOCOL)
|
161
167
|
Front.set(:logging, @log.debug?)
|
162
168
|
Front.set(:halt, opts['halt-code'])
|
169
|
+
Front.set(:disable_push, opts['disable-push'])
|
170
|
+
Front.set(:disable_fetch, opts['disable-fetch'])
|
163
171
|
Front.set(:home, opts['home'])
|
164
172
|
@log.info("Time: #{Time.now.utc.iso8601}")
|
165
173
|
@log.info("Home directory: #{opts['home']}")
|
@@ -198,7 +206,7 @@ module Zold
|
|
198
206
|
Front.set(:port, opts['bind-port'])
|
199
207
|
Front.set(:reboot, !opts['never-reboot'])
|
200
208
|
node_alias = opts[:alias] || address
|
201
|
-
unless node_alias.eql?(address) || node_alias =~ /^[
|
209
|
+
unless node_alias.eql?(address) || node_alias =~ /^[A-Za-z0-9]{4,16}$/
|
202
210
|
raise "Alias should be a 4 to 16 char long alphanumeric string: #{node_alias}"
|
203
211
|
end
|
204
212
|
Front.set(:node_alias, node_alias)
|
@@ -375,7 +383,7 @@ module Zold
|
|
375
383
|
end
|
376
384
|
|
377
385
|
def info(msg)
|
378
|
-
@log.debug(msg)
|
386
|
+
@log.debug('WEBRICK ' + msg)
|
379
387
|
end
|
380
388
|
|
381
389
|
def debug(msg)
|
@@ -383,11 +391,11 @@ module Zold
|
|
383
391
|
end
|
384
392
|
|
385
393
|
def error(msg)
|
386
|
-
@log.error(msg)
|
394
|
+
@log.error('WEBRICK ' + msg)
|
387
395
|
end
|
388
396
|
|
389
397
|
def fatal(msg)
|
390
|
-
@log.error(msg)
|
398
|
+
@log.error('WEBRICK ' + msg)
|
391
399
|
end
|
392
400
|
|
393
401
|
def debug?
|
data/lib/zold/commands/push.rb
CHANGED
@@ -31,7 +31,6 @@ require_relative '../log'
|
|
31
31
|
require_relative '../id'
|
32
32
|
require_relative '../http'
|
33
33
|
require_relative '../json_page'
|
34
|
-
require_relative '../atomic_file'
|
35
34
|
|
36
35
|
# PUSH command.
|
37
36
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -91,7 +90,7 @@ total score for #{id} is #{total}")
|
|
91
90
|
start = Time.now
|
92
91
|
content = @wallets.find(id) do |wallet|
|
93
92
|
raise "The wallet #{id} is absent" unless wallet.exists?
|
94
|
-
|
93
|
+
File.read(wallet.path)
|
95
94
|
end
|
96
95
|
uri = "/wallet/#{id}"
|
97
96
|
response = r.http(uri).put(content)
|
data/lib/zold/copies.rb
CHANGED
@@ -23,7 +23,6 @@
|
|
23
23
|
require 'time'
|
24
24
|
require 'csv'
|
25
25
|
require 'backtrace'
|
26
|
-
require_relative 'atomic_file'
|
27
26
|
require_relative 'log'
|
28
27
|
require_relative 'size'
|
29
28
|
require_relative 'wallet'
|
@@ -104,7 +103,7 @@ module Zold
|
|
104
103
|
list = load
|
105
104
|
target = list.find do |s|
|
106
105
|
f = File.join(@dir, "#{s[:name]}#{Copies::EXT}")
|
107
|
-
File.exist?(f) &&
|
106
|
+
File.exist?(f) && File.read(f) == content
|
108
107
|
end
|
109
108
|
if target.nil?
|
110
109
|
max = Dir.new(@dir)
|
@@ -113,7 +112,7 @@ module Zold
|
|
113
112
|
.max
|
114
113
|
max = 0 if max.nil?
|
115
114
|
name = (max + 1).to_s
|
116
|
-
|
115
|
+
File.write(File.join(@dir, "#{name}#{Copies::EXT}"), content)
|
117
116
|
else
|
118
117
|
name = target[:name]
|
119
118
|
end
|
@@ -161,7 +160,8 @@ module Zold
|
|
161
160
|
private
|
162
161
|
|
163
162
|
def save(list)
|
164
|
-
|
163
|
+
File.write(
|
164
|
+
file,
|
165
165
|
list.map do |r|
|
166
166
|
[
|
167
167
|
r[:name], r[:host],
|
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 = File.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
@@ -24,7 +24,6 @@ gem 'openssl'
|
|
24
24
|
require 'openssl'
|
25
25
|
require 'base64'
|
26
26
|
require 'tempfile'
|
27
|
-
require_relative 'atomic_file'
|
28
27
|
|
29
28
|
# The RSA key (either private or public).
|
30
29
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -38,7 +37,7 @@ module Zold
|
|
38
37
|
unless file.nil?
|
39
38
|
path = File.expand_path(file)
|
40
39
|
raise "Can't find RSA key at #{file} (#{path})" unless File.exist?(path)
|
41
|
-
return
|
40
|
+
return File.read(path)
|
42
41
|
end
|
43
42
|
unless text.nil?
|
44
43
|
return text if text.start_with?('-----')
|
@@ -97,7 +97,7 @@ module Zold
|
|
97
97
|
def push(id, body)
|
98
98
|
raise "Queue is too long (#{queue.count} wallets), try again later" if queue.count > AsyncEntrance::MAX_QUEUE
|
99
99
|
@mutex.synchronize do
|
100
|
-
|
100
|
+
File.write(File.join(@dir, id.to_s), body)
|
101
101
|
end
|
102
102
|
[id]
|
103
103
|
end
|
data/lib/zold/node/farm.rb
CHANGED
@@ -27,7 +27,6 @@ require_relative '../log'
|
|
27
27
|
require_relative '../score'
|
28
28
|
require_relative '../age'
|
29
29
|
require_relative '../verbose_thread'
|
30
|
-
require_relative '../atomic_file'
|
31
30
|
|
32
31
|
# The farm of scores.
|
33
32
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -222,7 +221,8 @@ module Zold
|
|
222
221
|
scores = load + list
|
223
222
|
period = 24 * 60 * 60 / [threads, 1].max
|
224
223
|
@mutex.synchronize do
|
225
|
-
|
224
|
+
File.write(
|
225
|
+
@cache,
|
226
226
|
scores.select(&:valid?)
|
227
227
|
.reject(&:expired?)
|
228
228
|
.sort_by(&:value)
|
@@ -239,7 +239,7 @@ module Zold
|
|
239
239
|
def load
|
240
240
|
@mutex.synchronize do
|
241
241
|
if File.exist?(@cache)
|
242
|
-
|
242
|
+
File.read(@cache).split(/\n/)
|
243
243
|
.map { |t| parse_score_line(t) }
|
244
244
|
.reject(&:zero?)
|
245
245
|
else
|
data/lib/zold/node/front.rb
CHANGED
@@ -35,11 +35,11 @@ require 'backtrace'
|
|
35
35
|
require_relative '../version'
|
36
36
|
require_relative '../size'
|
37
37
|
require_relative '../wallet'
|
38
|
+
require_relative '../age'
|
38
39
|
require_relative '../copies'
|
39
40
|
require_relative '../log'
|
40
41
|
require_relative '../id'
|
41
42
|
require_relative '../http'
|
42
|
-
require_relative '../atomic_file'
|
43
43
|
|
44
44
|
# The web front of the node.
|
45
45
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -56,7 +56,7 @@ module Zold
|
|
56
56
|
set :start, Time.now
|
57
57
|
set :lock, false
|
58
58
|
set :show_exceptions, false
|
59
|
-
set :server,
|
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
|
62
62
|
set :halt, '' # to be injected at node.rb
|
@@ -110,17 +110,19 @@ while #{settings.address} is in '#{settings.network}'"
|
|
110
110
|
cmd = Remote.new(remotes: settings.remotes, log: settings.log)
|
111
111
|
cmd.run(['remote', 'add', s.host, s.port.to_s, "--network=#{settings.network}"])
|
112
112
|
end
|
113
|
+
@start = Time.now
|
113
114
|
end
|
114
115
|
|
115
116
|
# @todo #357:30min Test that the headers are being set correctly.
|
116
117
|
# Currently there are no tests at all that would verify the headers.
|
117
118
|
after do
|
118
119
|
headers['Cache-Control'] = 'no-cache'
|
119
|
-
headers['Connection'] = 'close'
|
120
120
|
headers['X-Zold-Version'] = settings.version
|
121
121
|
headers[Http::PROTOCOL_HEADER] = settings.protocol.to_s
|
122
122
|
headers['Access-Control-Allow-Origin'] = '*'
|
123
123
|
headers[Http::SCORE_HEADER] = score.reduced(16).to_s
|
124
|
+
headers['X-Zold-Thread'] = Thread.current.name
|
125
|
+
headers['X-Zold-Milliseconds'] = ((Time.now - @start) * 1000).round.to_s
|
124
126
|
end
|
125
127
|
|
126
128
|
get '/robots.txt' do
|
@@ -192,6 +194,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
192
194
|
end
|
193
195
|
|
194
196
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})} do
|
197
|
+
error 404 if settings.disable_fetch
|
195
198
|
id = Id.new(params[:id])
|
196
199
|
settings.wallets.find(id) do |wallet|
|
197
200
|
error 404 unless wallet.exists?
|
@@ -208,12 +211,13 @@ while #{settings.address} is in '#{settings.network}'"
|
|
208
211
|
digest: wallet.digest,
|
209
212
|
copies: Copies.new(File.join(settings.copies, id)).all.count,
|
210
213
|
balance: wallet.balance.to_i,
|
211
|
-
body:
|
214
|
+
body: File.new(wallet.path).read
|
212
215
|
)
|
213
216
|
end
|
214
217
|
end
|
215
218
|
|
216
219
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16}).json} do
|
220
|
+
error 404 if settings.disable_fetch
|
217
221
|
id = Id.new(params[:id])
|
218
222
|
settings.wallets.find(id) do |wallet|
|
219
223
|
error 404 unless wallet.exists?
|
@@ -235,6 +239,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
235
239
|
end
|
236
240
|
|
237
241
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/balance} do
|
242
|
+
error 404 if settings.disable_fetch
|
238
243
|
id = Id.new(params[:id])
|
239
244
|
settings.wallets.find(id) do |wallet|
|
240
245
|
error 404 unless wallet.exists?
|
@@ -244,6 +249,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
244
249
|
end
|
245
250
|
|
246
251
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/key} do
|
252
|
+
error 404 if settings.disable_fetch
|
247
253
|
id = Id.new(params[:id])
|
248
254
|
settings.wallets.find(id) do |wallet|
|
249
255
|
error 404 unless wallet.exists?
|
@@ -253,6 +259,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
253
259
|
end
|
254
260
|
|
255
261
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/mtime} do
|
262
|
+
error 404 if settings.disable_fetch
|
256
263
|
id = Id.new(params[:id])
|
257
264
|
settings.wallets.find(id) do |wallet|
|
258
265
|
error 404 unless wallet.exists?
|
@@ -262,6 +269,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
262
269
|
end
|
263
270
|
|
264
271
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/digest} do
|
272
|
+
error 404 if settings.disable_fetch
|
265
273
|
id = Id.new(params[:id])
|
266
274
|
settings.wallets.find(id) do |wallet|
|
267
275
|
error 404 unless wallet.exists?
|
@@ -271,6 +279,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
271
279
|
end
|
272
280
|
|
273
281
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})\.txt} do
|
282
|
+
error 404 if settings.disable_fetch
|
274
283
|
id = Id.new(params[:id])
|
275
284
|
settings.wallets.find(id) do |wallet|
|
276
285
|
error 404 unless wallet.exists?
|
@@ -284,25 +293,27 @@ while #{settings.address} is in '#{settings.network}'"
|
|
284
293
|
wallet.txns.map(&:to_text).join("\n"),
|
285
294
|
'',
|
286
295
|
'--',
|
287
|
-
"Balance: #{wallet.balance.to_zld} ZLD (#{wallet.balance.to_i} zents)",
|
296
|
+
"Balance: #{wallet.balance.to_zld(8)} ZLD (#{wallet.balance.to_i} zents)",
|
288
297
|
"Transactions: #{wallet.txns.count}",
|
289
298
|
"File size: #{File.size(wallet.path)} bytes (#{Copies.new(File.join(settings.copies, id)).all.count} copies)",
|
290
|
-
"Modified: #{wallet.mtime.utc.iso8601}",
|
299
|
+
"Modified: #{wallet.mtime.utc.iso8601} (#{Age.new(wallet.mtime.utc.iso8601)} ago)",
|
291
300
|
"Digest: #{wallet.digest}"
|
292
301
|
].join("\n")
|
293
302
|
end
|
294
303
|
end
|
295
304
|
|
296
305
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})\.bin} do
|
306
|
+
error 404 if settings.disable_fetch
|
297
307
|
id = Id.new(params[:id])
|
298
308
|
settings.wallets.find(id) do |wallet|
|
299
309
|
error 404 unless wallet.exists?
|
300
310
|
content_type 'text/plain'
|
301
|
-
|
311
|
+
File.read(wallet.path)
|
302
312
|
end
|
303
313
|
end
|
304
314
|
|
305
315
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/copies} do
|
316
|
+
error 404 if settings.disable_fetch
|
306
317
|
id = Id.new(params[:id])
|
307
318
|
settings.wallets.find(id) do |wallet|
|
308
319
|
error 404 unless wallet.exists?
|
@@ -321,6 +332,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
321
332
|
end
|
322
333
|
|
323
334
|
get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/copy/(?<name>[0-9]+)} do
|
335
|
+
error 404 if settings.disable_fetch
|
324
336
|
id = Id.new(params[:id])
|
325
337
|
name = params[:name]
|
326
338
|
settings.wallets.find(id) do |wallet|
|
@@ -333,6 +345,7 @@ while #{settings.address} is in '#{settings.network}'"
|
|
333
345
|
end
|
334
346
|
|
335
347
|
put %r{/wallet/(?<id>[A-Fa-f0-9]{16})/?} do
|
348
|
+
error 404 if settings.disable_push
|
336
349
|
request.body.rewind
|
337
350
|
modified = settings.entrance.push(Id.new(params[:id]), request.body.read.to_s)
|
338
351
|
if modified.empty?
|
@@ -368,6 +381,16 @@ while #{settings.address} is in '#{settings.network}'"
|
|
368
381
|
settings.metronome.to_text
|
369
382
|
end
|
370
383
|
|
384
|
+
get '/threads' do
|
385
|
+
content_type 'text/plain'
|
386
|
+
Thread.list.map do |t|
|
387
|
+
[
|
388
|
+
"#{t.name}: status=#{t.status}; alive=#{t.alive?}",
|
389
|
+
t.backtrace.nil? ? 'NO BACKTRACE' : " #{t.backtrace.join("\n ")}"
|
390
|
+
].join("\n")
|
391
|
+
end.join("\n\n")
|
392
|
+
end
|
393
|
+
|
371
394
|
not_found do
|
372
395
|
status 404
|
373
396
|
content_type 'text/plain'
|
@@ -24,7 +24,6 @@ require 'tempfile'
|
|
24
24
|
require_relative '../log'
|
25
25
|
require_relative '../size'
|
26
26
|
require_relative '../wallet'
|
27
|
-
require_relative '../atomic_file'
|
28
27
|
|
29
28
|
# The entrance that ignores duplicates.
|
30
29
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -61,7 +60,7 @@ module Zold
|
|
61
60
|
wallet.refurbish
|
62
61
|
after = File.read(wallet.path)
|
63
62
|
before = @wallets.find(id) do |w|
|
64
|
-
w.exists? ?
|
63
|
+
w.exists? ? File.read(w.path).to_s : ''
|
65
64
|
end
|
66
65
|
if before == after
|
67
66
|
@log.info(
|
data/lib/zold/patch.rb
CHANGED
@@ -23,7 +23,6 @@
|
|
23
23
|
require_relative 'log'
|
24
24
|
require_relative 'wallet'
|
25
25
|
require_relative 'signature'
|
26
|
-
require_relative 'atomic_file'
|
27
26
|
|
28
27
|
# Patch.
|
29
28
|
#
|
@@ -120,7 +119,7 @@ among #{payer.txns.count} transactions: #{txn.to_text}")
|
|
120
119
|
def save(file, overwrite: false)
|
121
120
|
raise 'You have to join at least one wallet in' if @id.nil?
|
122
121
|
before = ''
|
123
|
-
before =
|
122
|
+
before = File.read(file) if File.exist?(file)
|
124
123
|
wallet = Wallet.new(file)
|
125
124
|
wallet.init(@id, @key, overwrite: overwrite, network: @network)
|
126
125
|
File.open(file, 'a') do |f|
|
@@ -129,7 +128,7 @@ among #{payer.txns.count} transactions: #{txn.to_text}")
|
|
129
128
|
end
|
130
129
|
end
|
131
130
|
wallet.refurbish
|
132
|
-
after =
|
131
|
+
after = File.read(file)
|
133
132
|
before != after
|
134
133
|
end
|
135
134
|
end
|
data/lib/zold/remotes.rb
CHANGED
@@ -31,7 +31,6 @@ require_relative 'age'
|
|
31
31
|
require_relative 'score'
|
32
32
|
require_relative 'http'
|
33
33
|
require_relative 'node/farm'
|
34
|
-
require_relative 'atomic_file'
|
35
34
|
require_relative 'type'
|
36
35
|
|
37
36
|
# The list of remotes.
|
@@ -50,12 +49,10 @@ module Zold
|
|
50
49
|
# Default number of nodes to fetch.
|
51
50
|
MAX_NODES = 16
|
52
51
|
|
53
|
-
# Mutex object
|
54
|
-
MUTEX = Mutex.new
|
55
|
-
|
56
52
|
attribute :file, Types::Strict::String
|
57
53
|
attribute :network, Types::Strict::String.optional.default('test')
|
58
54
|
attribute :timeout, Types::Strict::Integer.optional.default(16)
|
55
|
+
attribute :mutex, Types::Object.optional.default(Mutex.new)
|
59
56
|
|
60
57
|
# Empty, for standalone mode
|
61
58
|
class Empty < Remotes
|
@@ -183,7 +180,7 @@ module Zold
|
|
183
180
|
list.each do |r|
|
184
181
|
pool.post do
|
185
182
|
Thread.current.abort_on_exception = true
|
186
|
-
Thread.current.name = "remotes@#{r[:host]}:#{r[:port]}"
|
183
|
+
Thread.current.name = "remotes-#{idx}@#{r[:host]}:#{r[:port]}"
|
187
184
|
start = Time.now
|
188
185
|
begin
|
189
186
|
yield Remotes::Remote.new(
|
@@ -194,7 +191,6 @@ module Zold
|
|
194
191
|
log: log,
|
195
192
|
network: network
|
196
193
|
)
|
197
|
-
idx += 1
|
198
194
|
raise 'Took too long to execute' if (Time.now - start).round > timeout
|
199
195
|
rescue StandardError => e
|
200
196
|
error(r[:host], r[:port])
|
@@ -203,6 +199,7 @@ module Zold
|
|
203
199
|
remove(r[:host], r[:port]) if errors > Remotes::TOLERANCE
|
204
200
|
end
|
205
201
|
end
|
202
|
+
idx += 1
|
206
203
|
end
|
207
204
|
pool.shutdown
|
208
205
|
pool.kill unless pool.wait_for_termination(5 * 60)
|
@@ -230,7 +227,7 @@ module Zold
|
|
230
227
|
private
|
231
228
|
|
232
229
|
def modify
|
233
|
-
|
230
|
+
mutex.synchronize do
|
234
231
|
save(yield(load))
|
235
232
|
end
|
236
233
|
end
|
@@ -261,7 +258,8 @@ module Zold
|
|
261
258
|
end
|
262
259
|
|
263
260
|
def save(list)
|
264
|
-
|
261
|
+
File.write(
|
262
|
+
file,
|
265
263
|
list.uniq { |r| "#{r[:host]}:#{r[:port]}" }.map do |r|
|
266
264
|
[
|
267
265
|
r[:host],
|
data/lib/zold/txns.rb
CHANGED
@@ -21,7 +21,6 @@
|
|
21
21
|
# SOFTWARE.
|
22
22
|
|
23
23
|
require_relative 'txn'
|
24
|
-
require_relative 'atomic_file'
|
25
24
|
|
26
25
|
# Transactions in a wallet.
|
27
26
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -40,7 +39,7 @@ module Zold
|
|
40
39
|
|
41
40
|
def fetch
|
42
41
|
raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
|
43
|
-
lines =
|
42
|
+
lines = File.read(@file).split(/\n/)
|
44
43
|
raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
|
45
44
|
lines.drop(5)
|
46
45
|
.each_with_index
|
data/lib/zold/version.rb
CHANGED
data/lib/zold/wallet.rb
CHANGED
@@ -30,7 +30,6 @@ require_relative 'tax'
|
|
30
30
|
require_relative 'amount'
|
31
31
|
require_relative 'hexnum'
|
32
32
|
require_relative 'signature'
|
33
|
-
require_relative 'atomic_file'
|
34
33
|
require_relative 'txns'
|
35
34
|
require_relative 'head'
|
36
35
|
|
@@ -90,7 +89,8 @@ module Zold
|
|
90
89
|
def init(id, pubkey, overwrite: false, network: 'test')
|
91
90
|
raise "File '#{@file}' already exists" if File.exist?(@file) && !overwrite
|
92
91
|
raise "Invalid network name '#{network}'" unless network =~ /^[a-z]{4,16}$/
|
93
|
-
|
92
|
+
FileUtils.mkdir_p(File.dirname(@file))
|
93
|
+
File.write(@file, "#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
|
94
94
|
@txns.flush
|
95
95
|
@head.flush
|
96
96
|
end
|
@@ -187,7 +187,8 @@ module Zold
|
|
187
187
|
end
|
188
188
|
|
189
189
|
def refurbish
|
190
|
-
|
190
|
+
File.write(
|
191
|
+
@file,
|
191
192
|
"#{network}\n#{protocol}\n#{id}\n#{key.to_pub}\n\n#{txns.map { |t| t.to_s + "\n" }.join}"
|
192
193
|
)
|
193
194
|
@txns.flush
|
data/test/fake_home.rb
CHANGED
@@ -28,7 +28,6 @@ require_relative '../lib/zold/sync_wallets'
|
|
28
28
|
require_relative '../lib/zold/key'
|
29
29
|
require_relative '../lib/zold/version'
|
30
30
|
require_relative '../lib/zold/remotes'
|
31
|
-
require_relative '../lib/zold/atomic_file'
|
32
31
|
|
33
32
|
# Fake home dir.
|
34
33
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -74,7 +73,7 @@ class FakeHome
|
|
74
73
|
mtime: wallet.mtime.utc.iso8601,
|
75
74
|
digest: wallet.digest,
|
76
75
|
balance: wallet.balance.to_i,
|
77
|
-
body:
|
76
|
+
body: File.read(wallet.path)
|
78
77
|
}.to_json
|
79
78
|
end
|
80
79
|
end
|
data/test/node/test_front.rb
CHANGED
@@ -61,7 +61,8 @@ class FrontTest < Minitest::Test
|
|
61
61
|
'/farm',
|
62
62
|
'/metronome',
|
63
63
|
'/score',
|
64
|
-
'/trace'
|
64
|
+
'/trace',
|
65
|
+
'/threads'
|
65
66
|
],
|
66
67
|
'404' => [
|
67
68
|
'/this-is-absent',
|
@@ -121,6 +122,27 @@ class FrontTest < Minitest::Test
|
|
121
122
|
end
|
122
123
|
end
|
123
124
|
|
125
|
+
def test_fetch_in_multiple_threads
|
126
|
+
FakeNode.new(log: test_log).run(['--no-metronome']) do |port|
|
127
|
+
FakeHome.new.run do |home|
|
128
|
+
wallet = home.create_wallet
|
129
|
+
base = "http://localhost:#{port}"
|
130
|
+
Zold::Http.new(uri: "#{base}/wallet/#{wallet.id}", score: nil).put(File.read(wallet.path))
|
131
|
+
assert_equal_wait('200') { Zold::Http.new(uri: "#{base}/wallet/#{wallet.id}", score: nil).get.code }
|
132
|
+
threads = []
|
133
|
+
mutex = Mutex.new
|
134
|
+
assert_in_threads(loops: 100) do
|
135
|
+
assert_equal_wait('200') do
|
136
|
+
res = Zold::Http.new(uri: "#{base}/wallet/#{wallet.id}", score: nil).get
|
137
|
+
mutex.synchronize { threads << res.header['X-Zold-Thread'] }
|
138
|
+
res.code
|
139
|
+
end
|
140
|
+
end
|
141
|
+
assert(threads.uniq.count > 1)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
124
146
|
def test_pushes_twice
|
125
147
|
FakeNode.new(log: test_log).run do |port|
|
126
148
|
FakeHome.new.run do |home|
|
@@ -221,26 +243,12 @@ class FrontTest < Minitest::Test
|
|
221
243
|
Time.stub :now, Time.at(0) do
|
222
244
|
FakeNode.new(log: test_log).run(['--ignore-score-weakness']) do |port|
|
223
245
|
response = Zold::Http.new(uri: URI("http://localhost:#{port}/"), score: nil).get
|
224
|
-
assert_equal(
|
225
|
-
|
226
|
-
|
227
|
-
)
|
228
|
-
assert_equal(
|
229
|
-
|
230
|
-
response.header['Connection']
|
231
|
-
)
|
232
|
-
assert_equal(
|
233
|
-
app.settings.version,
|
234
|
-
response.header['X-Zold-Version']
|
235
|
-
)
|
236
|
-
assert_equal(
|
237
|
-
app.settings.protocol.to_s,
|
238
|
-
response.header[Zold::Http::PROTOCOL_HEADER]
|
239
|
-
)
|
240
|
-
assert_equal(
|
241
|
-
'*',
|
242
|
-
response.header['Access-Control-Allow-Origin']
|
243
|
-
)
|
246
|
+
assert_equal('no-cache', response.header['Cache-Control'])
|
247
|
+
assert_equal('close', response.header['Connection'])
|
248
|
+
assert_equal(app.settings.version, response.header['X-Zold-Version'])
|
249
|
+
assert_equal(app.settings.protocol.to_s, response.header[Zold::Http::PROTOCOL_HEADER])
|
250
|
+
assert_equal('*', response.header['Access-Control-Allow-Origin'])
|
251
|
+
assert(response.header['X-Zold-Milliseconds'])
|
244
252
|
assert(!response.header[Zold::Http::SCORE_HEADER].nil?)
|
245
253
|
end
|
246
254
|
end
|
data/test/test_zold.rb
CHANGED
@@ -34,7 +34,7 @@ require_relative '../lib/zold/version'
|
|
34
34
|
class TestZold < Minitest::Test
|
35
35
|
def test_all_scripts
|
36
36
|
Dir.new('fixtures/scripts').select { |f| f =~ /\.sh$/ && !f.start_with?('_') }.each do |f|
|
37
|
-
# next unless f == '
|
37
|
+
# next unless f == 'deadlocks.sh'
|
38
38
|
Dir.mktmpdir do |dir|
|
39
39
|
FileUtils.cp('fixtures/id_rsa.pub', dir)
|
40
40
|
FileUtils.cp('fixtures/id_rsa', dir)
|
data/zold.gemspec
CHANGED
@@ -65,6 +65,7 @@ and suggests a different architecture for digital wallet maintenance.'
|
|
65
65
|
s.add_runtime_dependency 'json', '~>1.8'
|
66
66
|
s.add_runtime_dependency 'moneta', '~>1.0'
|
67
67
|
s.add_runtime_dependency 'openssl', '~>2.1'
|
68
|
+
s.add_runtime_dependency 'puma'
|
68
69
|
s.add_runtime_dependency 'rainbow', '~>3.0'
|
69
70
|
s.add_runtime_dependency 'rake', '~>12.3' # has to stay here for Heroku
|
70
71
|
s.add_runtime_dependency 'rubocop', '0.58.1' # has to stay here for Heroku
|
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.
|
4
|
+
version: 0.14.53
|
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-10-
|
11
|
+
date: 2018-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: backtrace
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '2.1'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: puma
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: rainbow
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -488,7 +502,6 @@ files:
|
|
488
502
|
- lib/zold.rb
|
489
503
|
- lib/zold/age.rb
|
490
504
|
- lib/zold/amount.rb
|
491
|
-
- lib/zold/atomic_file.rb
|
492
505
|
- lib/zold/cached_wallets.rb
|
493
506
|
- lib/zold/commands/alias.rb
|
494
507
|
- lib/zold/commands/args.rb
|
@@ -587,7 +600,6 @@ files:
|
|
587
600
|
- test/test__helper.rb
|
588
601
|
- test/test_age.rb
|
589
602
|
- test/test_amount.rb
|
590
|
-
- test/test_atomic_file.rb
|
591
603
|
- test/test_cached_wallets.rb
|
592
604
|
- test/test_copies.rb
|
593
605
|
- test/test_gem.rb
|
@@ -684,7 +696,6 @@ test_files:
|
|
684
696
|
- test/test__helper.rb
|
685
697
|
- test/test_age.rb
|
686
698
|
- test/test_amount.rb
|
687
|
-
- test/test_atomic_file.rb
|
688
699
|
- test/test_cached_wallets.rb
|
689
700
|
- test/test_copies.rb
|
690
701
|
- test/test_gem.rb
|
data/lib/zold/atomic_file.rb
DELETED
@@ -1,52 +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
|
-
# Atomic file.
|
24
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
25
|
-
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
26
|
-
# License:: MIT
|
27
|
-
module Zold
|
28
|
-
# Atomic file
|
29
|
-
class AtomicFile
|
30
|
-
def initialize(file)
|
31
|
-
raise 'File can\'t be nil' if file.nil?
|
32
|
-
@file = file
|
33
|
-
@mutex = Mutex.new
|
34
|
-
end
|
35
|
-
|
36
|
-
def read
|
37
|
-
@mutex.synchronize do
|
38
|
-
File.open(@file, 'rb', &:read)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def write(content)
|
43
|
-
raise 'Content can\'t be nil' if content.nil?
|
44
|
-
FileUtils.mkdir_p(File.dirname(@file))
|
45
|
-
@mutex.synchronize do
|
46
|
-
File.open(@file, 'wb') do |f|
|
47
|
-
f.write(content)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
data/test/test_atomic_file.rb
DELETED
@@ -1,55 +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 'tmpdir'
|
25
|
-
require 'securerandom'
|
26
|
-
require_relative 'test__helper'
|
27
|
-
require_relative '../lib/zold/atomic_file'
|
28
|
-
require_relative '../lib/zold/verbose_thread'
|
29
|
-
|
30
|
-
# AtomicFile test.
|
31
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
32
|
-
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
33
|
-
# License:: MIT
|
34
|
-
class TestAtomicFile < Minitest::Test
|
35
|
-
def test_writes_and_reads
|
36
|
-
Dir.mktmpdir do |dir|
|
37
|
-
file = Zold::AtomicFile.new(File.join(dir, 'test.txt'))
|
38
|
-
['', 'hello, dude!'].each do |t|
|
39
|
-
file.write(t)
|
40
|
-
assert_equal(t, file.read)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_writes_from_many_threads
|
46
|
-
Dir.mktmpdir do |dir|
|
47
|
-
file = Zold::AtomicFile.new(File.join(dir, 'a.txt'))
|
48
|
-
content = SecureRandom.hex(1000)
|
49
|
-
assert_in_threads(loops: 1000) do
|
50
|
-
file.write(content)
|
51
|
-
assert_equal(content, file.read, 'Invalid content')
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|