zold 0.16.17 → 0.16.18

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 294ae5c8edd1bfd6c8f8567f5e19161356d656d87a04050e29c82bcf5a5303cf
4
- data.tar.gz: 63646cc0ed79fcb7de5c1c9498db578248aa6ff3e22a840138ccede01eed468e
3
+ metadata.gz: de86dbcab88573c76088a862afd47563bebee0d62df73b695bdae79dc2e47778
4
+ data.tar.gz: 76f9b1a004ff1ef24acf833626b128bff9ac7663e16bc10c7a476e4667b349fc
5
5
  SHA512:
6
- metadata.gz: 31dc5ac7025b04c949c0ee1329bcdc773fa159322437c665b48722b3e21a2295f37ba465f5bc7df13cf5d7654a3e234bfaf999399b9c0fc9bbe42881995226d5
7
- data.tar.gz: 7e79554604ca13438cd05c467ce6f46274a8bbbb7bbedfee84e3d28ea8989f4707d7ad924e6c896108e6a282baee7c02cee76533f75ec78353eb070f9f823383
6
+ metadata.gz: 68cf2ad85d6eeaf93cb9d32d1e84f8c25c96f70826ca4dc9e68140e89c32bd9d9e584fcc94e9ccc7fbfcaec0e1e9b2c3c2a51313f717af97da577bf517ae7ade
7
+ data.tar.gz: 2c45d5fee9bf53f9e2e09e9e961c3004755574fd23a5840c93767b15beba9b3c236464fd299b70b39b846a3126b4ce66792bb29f608158f5af69fbc94ae3bf9c
data/lib/zold/http.rb CHANGED
@@ -55,10 +55,10 @@ module Zold
55
55
  PROTOCOL_HEADER = 'X-Zold-Protocol'
56
56
 
57
57
  # Read timeout in seconds
58
- READ_TIMEOUT = 0.4
58
+ READ_TIMEOUT = 1
59
59
 
60
60
  # Connect timeout in seconds
61
- CONNECT_TIMEOUT = 0.2
61
+ CONNECT_TIMEOUT = 0.4
62
62
 
63
63
  def initialize(uri:, score: Score::ZERO, network: 'test')
64
64
  @uri = uri.is_a?(URI) ? uri : URI(uri)
@@ -69,11 +69,11 @@ module Zold
69
69
  def get(timeout: Http::READ_TIMEOUT + Http::CONNECT_TIMEOUT)
70
70
  http = Net::HTTP.new(@uri.host, @uri.port)
71
71
  http.use_ssl = @uri.scheme == 'https'
72
- http.read_timeout = Http::READ_TIMEOUT
72
+ http.read_timeout = timeout
73
73
  http.open_timeout = Http::CONNECT_TIMEOUT
74
74
  path = @uri.path
75
75
  path += '?' + @uri.query if @uri.query
76
- Timeout.timeout(timeout) do
76
+ Timeout.timeout(timeout + Http::CONNECT_TIMEOUT) do
77
77
  http.request_get(path, headers)
78
78
  end
79
79
  rescue StandardError => e
@@ -83,11 +83,11 @@ module Zold
83
83
  def put(body, timeout: Http::READ_TIMEOUT + Http::CONNECT_TIMEOUT)
84
84
  http = Net::HTTP.new(@uri.host, @uri.port)
85
85
  http.use_ssl = @uri.scheme == 'https'
86
- http.read_timeout = Http::READ_TIMEOUT
86
+ http.read_timeout = timeout
87
87
  http.open_timeout = Http::CONNECT_TIMEOUT
88
88
  path = @uri.path
89
89
  path += '?' + @uri.query if @uri.query
90
- Timeout.timeout(timeout) do
90
+ Timeout.timeout(timeout + Http::CONNECT_TIMEOUT) do
91
91
  http.request_put(
92
92
  path, body,
93
93
  headers.merge(
data/lib/zold/id.rb CHANGED
@@ -31,6 +31,7 @@ module Zold
31
31
  if id.nil?
32
32
  @id = rand(2**32..2**64 - 1)
33
33
  else
34
+ raise "Invalid wallet ID type: #{id.class.name}" unless id.is_a?(String)
34
35
  raise "Invalid wallet ID: #{id}" unless id =~ /^[0-9a-fA-F]{16}$/
35
36
  @id = Integer("0x#{id}", 16)
36
37
  end
@@ -38,28 +38,33 @@ module Zold
38
38
  class AsyncEntrance
39
39
  def initialize(entrance, dir, log: Log::Quiet.new, threads: [Concurrent.processor_count, 4].max)
40
40
  @entrance = entrance
41
- @dir = dir
41
+ @dir = File.expand_path(dir)
42
42
  @log = log
43
43
  @total = threads
44
- @mutex = Mutex.new
44
+ @queue = Queue.new
45
45
  end
46
46
 
47
47
  def to_json
48
48
  @entrance.to_json.merge(
49
- 'queue': queue.count,
49
+ 'queue': @queue.size,
50
50
  'threads': @threads.count
51
51
  )
52
52
  end
53
53
 
54
54
  def start
55
55
  raise 'Block must be given to start()' unless block_given?
56
+ FileUtils.mkdir_p(@dir)
57
+ DirItems.new(@dir).fetch.select { |f| f =~ /^[0-9a-f]{16}-/ }.each do |f|
58
+ file = File.join(@dir, f)
59
+ id = f.split('-')[0]
60
+ @queue << { id: Id.new(id), file: file }
61
+ end
62
+ @log.info("#{@queue.size} wallets pre-loaded into async_entrace from #{@dir}") unless @queue.size.zero?
56
63
  @entrance.start do
57
- FileUtils.mkdir_p(@dir)
58
64
  @threads = (0..@total - 1).map do |i|
59
65
  Thread.start do
60
66
  Endless.new("async-e##{i}", log: @log).run do
61
67
  take
62
- sleep(1)
63
68
  end
64
69
  end
65
70
  end
@@ -73,14 +78,15 @@ module Zold
73
78
 
74
79
  # Always returns an array with a single ID of the pushed wallet
75
80
  def push(id, body)
76
- raise "Queue is too long (#{queue.count} wallets), try again later" if queue.count > 256
81
+ raise "Queue is too long (#{@queue.size} wallets), try again later" if @queue.size > 256
77
82
  start = Time.now
78
83
  loop do
79
84
  uuid = SecureRandom.uuid
80
85
  file = File.join(@dir, "#{id}-#{uuid}")
81
86
  next if File.exist?(file)
82
87
  IO.write(file, body)
83
- @log.debug("Added #{id}/#{Size.new(body.length)} to the queue at pos.#{queue.count} \
88
+ @queue << { id: id, file: file }
89
+ @log.debug("Added #{id}/#{Size.new(body.length)} to the queue at pos.#{@queue.size} \
84
90
  in #{Age.new(start, limit: 0.05)}: #{uuid}")
85
91
  break
86
92
  end
@@ -91,23 +97,13 @@ in #{Age.new(start, limit: 0.05)}: #{uuid}")
91
97
 
92
98
  def take
93
99
  start = Time.now
94
- id, body = @mutex.synchronize do
95
- opts = queue
96
- return if opts.empty?
97
- file = File.join(@dir, opts[0])
98
- id = opts[0].split('-')[0]
99
- Thread.current.thread_variable_set(:wallet, id)
100
- body = IO.read(file)
101
- FileUtils.rm_f(file)
102
- [id, body]
103
- end
104
- @entrance.push(Id.new(id), body)
105
- @log.debug("Pushed #{id}/#{Size.new(body.length)} to #{@entrance.class.name} \
106
- in #{Age.new(start, limit: 0.1)} (#{queue.count} still in the queue)")
107
- end
108
-
109
- def queue
110
- DirItems.new(@dir).fetch.select { |f| f =~ /^[0-9a-f]{16}-/ }
100
+ item = @queue.pop
101
+ Thread.current.thread_variable_set(:wallet, item[:id].to_s)
102
+ body = IO.read(item[:file])
103
+ FileUtils.rm_f(item[:file])
104
+ @entrance.push(item[:id], body)
105
+ @log.debug("Pushed #{item[:id]}/#{Size.new(body.length)} to #{@entrance.class.name} \
106
+ in #{Age.new(start, limit: 0.1)} (#{@queue.size} still in the queue)")
111
107
  end
112
108
  end
113
109
  end
@@ -92,7 +92,7 @@ module Zold
92
92
  @history.shift if @history.length >= 16
93
93
  @speed.shift if @speed.length >= 64
94
94
  @wallets.find(id) do |wallet|
95
- @history << "#{id}/#{sec}/#{modified.count}/#{wallet.mnemo}"
95
+ @history << "#{sec}/#{modified.count}/#{wallet.mnemo}"
96
96
  end
97
97
  @speed << sec
98
98
  end
@@ -71,7 +71,7 @@ for #{score.value}/#{score.strength} at #{score.host}:#{score.port}")
71
71
  buffer = +''
72
72
  loop do
73
73
  begin
74
- buffer << stdout.read_nonblock(1024)
74
+ buffer << stdout.read_nonblock(16 * 1024)
75
75
  # rubocop:disable Lint/HandleExceptions
76
76
  rescue IO::WaitReadable => _
77
77
  # rubocop:enable Lint/HandleExceptions
@@ -85,7 +85,7 @@ for #{score.value}/#{score.strength} at #{score.host}:#{score.port}")
85
85
  raise "Failed to calculate the score (##{thr.value}): #{buffer}" unless thr.value.to_i.zero?
86
86
  break
87
87
  end
88
- sleep(10)
88
+ sleep(1)
89
89
  Thread.current.thread_variable_set(:buffer, buffer.length.to_s)
90
90
  end
91
91
  after = Score.parse(buffer.strip)
@@ -230,7 +230,7 @@ in #{Age.new(@start, limit: 1)}")
230
230
  score: score.to_h,
231
231
  wallets: total_wallets,
232
232
  mtime: wallet.mtime.utc.iso8601,
233
- size: File.size(wallet.path),
233
+ size: wallet.size,
234
234
  digest: wallet.digest,
235
235
  copies: Copies.new(File.join(settings.copies, id)).all.count,
236
236
  balance: wallet.balance.to_i,
@@ -313,7 +313,7 @@ in #{Age.new(@start, limit: 1)}")
313
313
  "Balance: #{wallet.balance.to_zld(8)} ZLD (#{wallet.balance.to_i} zents)",
314
314
  "Transactions: #{wallet.txns.count}",
315
315
  "Taxes: #{Tax.new(wallet).paid} paid, the debt is #{Tax.new(wallet).debt}",
316
- "File size: #{File.size(wallet.path)} bytes (#{Copies.new(File.join(settings.copies, id)).all.count} copies)",
316
+ "File size: #{wallet.size} bytes (#{Copies.new(File.join(settings.copies, id)).all.count} copies)",
317
317
  "Modified: #{wallet.mtime.utc.iso8601} (#{Age.new(wallet.mtime.utc.iso8601)} ago)",
318
318
  "Digest: #{wallet.digest}"
319
319
  ].join("\n")
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'tempfile'
24
+ require 'openssl'
24
25
  require_relative '../log'
25
26
  require_relative '../size'
26
27
  require_relative '../wallet'
@@ -33,11 +34,8 @@ module Zold
33
34
  # The safe entrance
34
35
  class NoDupEntrance
35
36
  def initialize(entrance, wallets, log: Log::Quiet.new)
36
- raise 'Entrance can\'t be nil' if entrance.nil?
37
37
  @entrance = entrance
38
- raise 'Wallets can\'t be nil' if wallets.nil?
39
38
  @wallets = wallets
40
- raise 'Log can\'t be nil' if log.nil?
41
39
  @log = log
42
40
  end
43
41
 
@@ -52,25 +50,14 @@ module Zold
52
50
 
53
51
  # Returns a list of modifed wallets (as Zold::Id)
54
52
  def push(id, body)
55
- raise 'Id can\'t be nil' if id.nil?
56
- raise 'Id must be of type Id' unless id.is_a?(Id)
57
- raise 'Body can\'t be nil' if body.nil?
58
- Tempfile.open(['', Wallet::EXT]) do |f|
59
- IO.write(f, body)
60
- wallet = Wallet.new(f.path)
61
- wallet.refurbish
62
- after = IO.read(wallet.path)
63
- before = @wallets.find(id) do |w|
64
- w.exists? ? IO.read(w.path).to_s : ''
65
- end
66
- if before == after
67
- @log.info("Duplicate of #{Size.new(after.length)} #{wallet.mnemo} ignored")
68
- return []
69
- end
70
- @log.info("New content for #{wallet.mnemo} arrived, \
71
- #{Size.new(before.length)} before and #{Size.new(after.length)} after")
72
- @entrance.push(id, body)
53
+ before = @wallets.find(id) { |w| w.exists? ? w.digest : '' }
54
+ after = OpenSSL::Digest::SHA256.new(body).hexdigest
55
+ if before == after
56
+ @log.debug("Duplicate of #{id} ignored (#{Size.new(body.length)} bytes)")
57
+ return []
73
58
  end
59
+ @log.debug("New content for #{id} arrived (#{Size.new(body.length)} bytes)")
60
+ @entrance.push(id, body)
74
61
  end
75
62
  end
76
63
  end
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.16.17'
28
+ VERSION = '0.16.18'
29
29
  PROTOCOL = 2
30
30
  end
data/lib/zold/wallet.rb CHANGED
@@ -72,7 +72,7 @@ module Zold
72
72
  end
73
73
 
74
74
  def mnemo
75
- "#{id}/#{balance.to_zld(4)}/#{txns.count}t/#{digest[0, 6]}/#{Size.new(File.size(@file))}"
75
+ "#{id}/#{balance.to_zld(4)}/#{txns.count}t/#{digest[0, 6]}/#{Size.new(size)}"
76
76
  end
77
77
 
78
78
  def to_text
@@ -195,6 +195,11 @@ module Zold
195
195
  list.empty? ? 0 : (Time.now - list.min_by(&:date).date) / (60 * 60)
196
196
  end
197
197
 
198
+ # Size of the wallet file in bytes
199
+ def size
200
+ File.size(path)
201
+ end
202
+
198
203
  def txns
199
204
  @txns.fetch
200
205
  end
data/test/test_http.rb CHANGED
@@ -25,6 +25,7 @@ require 'tmpdir'
25
25
  require 'uri'
26
26
  require 'webmock/minitest'
27
27
  require 'zold/score'
28
+ require_relative 'test__helper'
28
29
  require_relative '../lib/zold/http'
29
30
 
30
31
  # Http test.
@@ -78,6 +79,26 @@ class TestHttp < Zold::Test
78
79
  assert_equal('599', res.code)
79
80
  end
80
81
 
82
+ def test_doesnt_terminate_on_long_call
83
+ require 'random-port'
84
+ WebMock.allow_net_connect!
85
+ RandomPort::Pool::SINGLETON.acquire do |port|
86
+ thread = Thread.start do
87
+ server = TCPServer.new(port)
88
+ loop do
89
+ client = server.accept
90
+ sleep 1
91
+ client.puts("HTTP/1.1 200 OK\nContent-Length: 4\n\nGood")
92
+ client.close
93
+ end
94
+ end
95
+ res = Zold::Http.new(uri: "http://localhost:#{port}/").get(timeout: 2)
96
+ assert_equal('200', res.code, res)
97
+ thread.kill
98
+ thread.join
99
+ end
100
+ end
101
+
81
102
  def test_sends_valid_version_header
82
103
  stub_request(:get, 'http://some-host-3/')
83
104
  .with(headers: { 'X-Zold-Version' => Zold::VERSION })
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.16.17
4
+ version: 0.16.18
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-11-08 00:00:00.000000000 Z
11
+ date: 2018-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace