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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f2a2f29076ab430c597f9d07c7a72b7f10a378e3c69b9ff755b04f644accdb9
4
- data.tar.gz: 5bab77f75e6b20cf1e80cb1f8bea44353ec0060e523c0c2c08614334d728c22b
3
+ metadata.gz: 12bf3dea4ff2630f3530269b7c5db959ad30cec8ab6346ba3336570c4a61822d
4
+ data.tar.gz: 1cb07c0e0ce665ededffc57a221ead9a235d99ba9eb391a8a75b1af6434bf093
5
5
  SHA512:
6
- metadata.gz: 7aec846b0b6443e195e989a414254fe80bca5e810b19000e60e574ba4721b64cb3ac820f85811c96b2a23db45c1c64291d0ae63e1eb619a30d5f27406354a432
7
- data.tar.gz: 41d673a22f703fc01bdc857f61a7c82d3fa34059e1a05c2cd759532d65606df723e2810bff55ab44d6f2ef9f2c0914ec9c713ea557924b1244b1f6575c17ad3f
6
+ metadata.gz: 4788bfa23317ab20259ef9fae9266556d402caf07de529d8358d9541f61dca9f42b2921b6d15535e8b11583c4f79c130aae3fa82cda5cd8fd494aeb645f8fd8d
7
+ data.tar.gz: 2a929f263c3a0a2fa91e6cddc9436b273781c58f121753ad28191f4fa140b5df7b9f07bda75e84dcc448e6d30633695895906fac47089be453246b83f3665e83
@@ -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
- AtomicFile.new(wallet.path).read
70
+ File.read(wallet.path)
72
71
  end
73
72
  after = ''
74
73
  Tempfile.open(['', Wallet::EXTENSION]) do |f|
@@ -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 =~ /^[a-z0-9]{4,16}$/
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?
@@ -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
- AtomicFile.new(wallet.path).read
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) && AtomicFile.new(f).read == content
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
- AtomicFile.new(File.join(@dir, "#{name}#{Copies::EXT}")).write(content)
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
- AtomicFile.new(file).write(
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 = AtomicFile.new(@file).read.split(/\n/)
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 AtomicFile.new(path).read
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
- AtomicFile.new(File.join(@dir, id.to_s)).write(body)
100
+ File.write(File.join(@dir, id.to_s), body)
101
101
  end
102
102
  [id]
103
103
  end
@@ -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
- AtomicFile.new(@cache).write(
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
- AtomicFile.new(@cache).read.split(/\n/)
242
+ File.read(@cache).split(/\n/)
243
243
  .map { |t| parse_score_line(t) }
244
244
  .reject(&:zero?)
245
245
  else
@@ -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, 'webrick'
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: AtomicFile.new(wallet.path).read
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
- AtomicFile.new(wallet.path).read
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? ? AtomicFile.new(w.path).read.to_s : ''
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 = AtomicFile.new(file).read if File.exist?(file)
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 = AtomicFile.new(file).read
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
- Remotes::MUTEX.synchronize do
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
- AtomicFile.new(file).write(
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 = AtomicFile.new(@file).read.split(/\n/)
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
@@ -25,6 +25,6 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.14.52'
28
+ VERSION = '0.14.53'
29
29
  PROTOCOL = 2
30
30
  end
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
- AtomicFile.new(@file).write("#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
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
- AtomicFile.new(@file).write(
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: Zold::AtomicFile.new(wallet.path).read
76
+ body: File.read(wallet.path)
78
77
  }.to_json
79
78
  end
80
79
  end
@@ -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
- 'no-cache',
226
- response.header['Cache-Control']
227
- )
228
- assert_equal(
229
- 'close',
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 == 'redeploy-on-upgrade.sh'
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.52
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-17 00:00:00.000000000 Z
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
@@ -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
@@ -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