zold 0.18.8 → 0.18.9

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: 253fd71080a2529f04a374fa90311a1035ed907dbe6084a61f6d3c0f6f834d77
4
- data.tar.gz: '0857238f9834a99cc2ef85f5e6f1d547cb8013f4c5e5697ddd9ae7d4366fea34'
3
+ metadata.gz: 6ad955cd4ed1c34ff735ca6684945186c29c07bf970f4bdc130ffdc0b3164922
4
+ data.tar.gz: 9344af265b58567d3aa2afda28815c6efaaddf0235a379d85f1ae69433112cd0
5
5
  SHA512:
6
- metadata.gz: 1976dcc0a25d82d2722495ce617670fe6bae75e19890a087a8b1fbff209d64e312da3ddbf6b3c20e5d8148b4055e80c4ba705d9bc546fde5ee535e88b4437043
7
- data.tar.gz: 516f380178059ba3bc7d09319b7bf6451113008c09218d99fb7bf1129084507cc0a8bdba6d9368c50ced632fff4df716d4ae441fbdd08c9704ad219cf0b20d21
6
+ metadata.gz: fba208cc63dc399eb24dd6c015f6eada0e22c9a3ca2b22d6a5612576b6ea264f2e3bc6cb57c0a69618694544928faa5f919d779c6e29d7da5137d40325e9b62e
7
+ data.tar.gz: bbfd1eec62d4f29272c21273aef71d9d110bbe491237b264c18f571e687d724b606861141e2efacadc3551fafe7917b2d896ed88eccc39b80f86066e226d81ee
data/bin/zold CHANGED
@@ -36,6 +36,7 @@ require_relative '../lib/zold/wallets'
36
36
  require_relative '../lib/zold/tree_wallets'
37
37
  require_relative '../lib/zold/sync_wallets'
38
38
  require_relative '../lib/zold/cached_wallets'
39
+ require_relative '../lib/zold/hungry_wallets'
39
40
  require_relative '../lib/zold/log'
40
41
  require_relative '../lib/zold/key'
41
42
  require_relative '../lib/zold/age'
@@ -4,7 +4,7 @@ function start_node {
4
4
  port=$(reserve_port)
5
5
  mkdir ${port}
6
6
  cd ${port}
7
- zold node --trace --invoice=DISTRWALLET@ffffffffffffffff \
7
+ zold node --trace --invoice=DISTRWALLET@ffffffffffffffff --tolerate-edges \
8
8
  --host=127.0.0.1 --port=${port} --bind-port=${port} \
9
9
  --threads=0 --routine-immediately --never-reboot > log.txt 2>&1 &
10
10
  pid=$!
@@ -36,7 +36,7 @@ zold --public-key=id_rsa.pub create 0000000000000000
36
36
  zold pay --private-key=id_rsa 0000000000000000 NOPREFIX@aaaabbbbccccdddd 4.95 'For the book'
37
37
  zold remote clean
38
38
  zold remote add 127.0.0.1 ${first}
39
- zold push 0000000000000000
39
+ zold push 0000000000000000 --tolerate-edges
40
40
  zold remote clean
41
41
  zold remote add 127.0.0.1 ${second}
42
42
 
@@ -45,7 +45,7 @@ zold remote add 127.0.0.1 ${second}
45
45
  # delay between them, in order to give the first node a chance to distribute
46
46
  # the wallet.
47
47
  i=0
48
- until zold fetch 0000000000000000 --ignore-score-weakness; do
48
+ until zold fetch 0000000000000000 --ignore-score-weakness --tolerate-edges; do
49
49
  echo 'Failed to fetch, let us try again'
50
50
  ((i++)) || sleep 0
51
51
  if ((i==5)); then
@@ -73,7 +73,7 @@ fi
73
73
  # have the wallet very soon.
74
74
  rm -f ${second}/**/*.z
75
75
  i=0
76
- until zold fetch 0000000000000000 --ignore-score-weakness; do
76
+ until zold fetch 0000000000000000 --ignore-score-weakness --tolerate-edges; do
77
77
  echo 'Failed to fetch, let us try again'
78
78
  ((i++)) || sleep 0
79
79
  if ((i==5)); then
@@ -3,7 +3,8 @@
3
3
  port=$(reserve_port)
4
4
  mkdir server
5
5
  cd server
6
- zold node --trace --invoice=PULLONSTART@ffffffffffffffff --no-metronome \
6
+ zold remote clean
7
+ zold node --trace --invoice=PULLONSTART@ffffffffffffffff --no-metronome --tolerate-edges \
7
8
  --host=127.0.0.1 --port=${port} --bind-port=${port} \
8
9
  --threads=0 --standalone --pretty=full 2>&1 &
9
10
  cd ..
@@ -14,16 +15,16 @@ zold remote clean
14
15
  zold remote add 127.0.0.1 ${port}
15
16
 
16
17
  zold --public-key=id_rsa.pub create abcdabcdabcdabcd
17
- zold push abcdabcdabcdabcd
18
+ zold push abcdabcdabcdabcd --tolerate-edges
18
19
  zold remove abcdabcdabcdabcd
19
- zold invoice abcdabcdabcdabcd
20
+ zold invoice abcdabcdabcdabcd --tolerate-edges
20
21
 
21
22
  second_port=$(reserve_port)
22
23
  mkdir second
23
24
  cd second
24
25
  zold remote clean
25
26
  zold remote add 127.0.0.1 ${port}
26
- zold node --trace --invoice=abcdabcdabcdabcd --no-metronome \
27
+ zold node --trace --invoice=abcdabcdabcdabcd --no-metronome --tolerate-edges \
27
28
  --host=127.0.0.1 --port=${second_port} --bind-port=${second_port} \
28
29
  --threads=0 &
29
30
 
@@ -4,7 +4,7 @@ port=$(reserve_port)
4
4
 
5
5
  mkdir server
6
6
  cd server
7
- zold node --trace --invoice=PUSHNPULL@ffffffffffffffff \
7
+ zold node --trace --invoice=PUSHNPULL@ffffffffffffffff --tolerate-edges \
8
8
  --host=127.0.0.1 --port=${port} --bind-port=${port} \
9
9
  --threads=0 --standalone 2>&1 &
10
10
  pid=$!
@@ -29,13 +29,13 @@ zold show 0000000000000000
29
29
  zold taxes debt 0000000000000000
30
30
 
31
31
  zold remote show
32
- zold push
33
- zold push 0000000000000000
34
- until zold fetch 0000000000000000 --ignore-score-weakness; do
32
+ zold push --tolerate-edges
33
+ zold push 0000000000000000 --tolerate-edges
34
+ until zold fetch 0000000000000000 --ignore-score-weakness --tolerate-edges; do
35
35
  echo 'Failed to fetch, let us try again'
36
36
  sleep 1
37
37
  done
38
- zold fetch
38
+ zold fetch --tolerate-edges
39
39
  zold diff 0000000000000000
40
40
  zold merge
41
41
  zold merge 0000000000000000
@@ -5,7 +5,7 @@ function start_node {
5
5
  cd $1
6
6
  zold remote clean
7
7
  zold node $3 --nohup --nohup-command='touch restarted' --nohup-log=log --nohup-max-cycles=0 --nohup-log-truncate=10240 \
8
- --expose-version=$2 --save-pid=pid --routine-immediately \
8
+ --expose-version=$2 --save-pid=pid --routine-immediately --tolerate-edges \
9
9
  --verbose --trace --invoice=REDEPLOY@ffffffffffffffff \
10
10
  --host=127.0.0.1 --port=$1 --bind-port=$1 --threads=1 --strength=20 > /dev/null 2>&1
11
11
  wait_for_port $1
@@ -4,7 +4,8 @@ function start_node {
4
4
  port=$(reserve_port)
5
5
  mkdir ${port}
6
6
  cd ${port}
7
- zold node --trace --invoice=SPREADWALLETS@ffffffffffffffff \
7
+ zold remote clean
8
+ zold node --trace --invoice=SPREADWALLETS@ffffffffffffffff --tolerate-edges \
8
9
  --host=127.0.0.1 --port=${port} --bind-port=${port} \
9
10
  --threads=0 > log.txt 2>&1 &
10
11
  pid=$!
@@ -18,20 +19,19 @@ first=$(start_node)
18
19
  second=$(start_node)
19
20
  trap "halt_nodes ${first} ${second}" EXIT
20
21
 
21
- zold --home=${first} remote clean
22
22
  zold --home=${first} remote add 127.0.0.1 ${second}
23
- zold --home=${second} remote clean
24
23
  zold --home=${second} remote add 127.0.0.1 ${first}
25
24
 
26
25
  zold --public-key=id_rsa.pub create 0000000000000000
27
26
  zold pay --private-key=id_rsa 0000000000000000 NOPREFIX@aaaabbbbccccdddd 4.95 'To help you, dude!'
27
+ zold remote clean
28
28
  zold remote add 127.0.0.1 ${first}
29
- zold push 0000000000000000
29
+ zold push 0000000000000000 --tolerate-edges
30
30
  zold remote clean
31
31
  zold remote add 127.0.0.1 ${second}
32
32
 
33
33
  i=0
34
- until zold fetch 0000000000000000 --ignore-score-weakness; do
34
+ until zold fetch 0000000000000000 --ignore-score-weakness --tolerate-edges; do
35
35
  echo 'Failed to fetch, let us try again'
36
36
  ((i++)) || sleep 0
37
37
  if ((i==5)); then
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'zache'
24
+ require 'delegate'
24
25
  require_relative 'endless'
25
26
  require_relative 'thread_pool'
26
27
 
@@ -30,7 +31,7 @@ require_relative 'thread_pool'
30
31
  # License:: MIT
31
32
  module Zold
32
33
  # Collection of local wallets
33
- class CachedWallets
34
+ class CachedWallets < SimpleDelegator
34
35
  def initialize(wallets)
35
36
  @wallets = wallets
36
37
  @zache = Zache.new
@@ -41,22 +42,7 @@ module Zold
41
42
  @zache.clean
42
43
  end
43
44
  end
44
- end
45
-
46
- def to_s
47
- @wallets.to_s
48
- end
49
-
50
- def path
51
- @wallets.path
52
- end
53
-
54
- def all
55
- @wallets.all
56
- end
57
-
58
- def count
59
- @wallets.count
45
+ super(wallets)
60
46
  end
61
47
 
62
48
  def acq(id, exclusive: false)
@@ -66,6 +66,9 @@ Available options:"
66
66
  o.array '--ignore-node',
67
67
  'Ignore this node and don\'t fetch from it',
68
68
  default: []
69
+ o.bool '--tolerate-edges',
70
+ 'Don\'t fail if only "edge" (not default ones) nodes accepted the wallet',
71
+ default: false
69
72
  o.bool '--quiet-if-absent',
70
73
  'Don\'t fail if the wallet is absent in all remote nodes',
71
74
  default: false
@@ -91,15 +94,22 @@ Available options:"
91
94
  total = Concurrent::AtomicFixnum.new
92
95
  nodes = Concurrent::AtomicFixnum.new
93
96
  done = Concurrent::AtomicFixnum.new
97
+ defaults = Concurrent::AtomicFixnum.new
94
98
  @remotes.iterate(@log) do |r|
95
99
  nodes.increment
96
100
  total.increment(fetch_one(id, r, cps, opts))
101
+ defaults.increment if r.default?
97
102
  done.increment
98
103
  end
99
104
  raise "There are no remote nodes, run 'zold remote reset'" if nodes.value.zero?
100
- raise "No nodes out of #{nodes.value} have the wallet #{id}" if done.value.zero? && !opts['quiet-if-absent']
105
+ unless opts['quiet-if-absent']
106
+ raise "No nodes out of #{nodes.value} have the wallet #{id}" if done.value.zero?
107
+ if defaults.value.zero? && !opts['tolerate-edges']
108
+ raise "There are only edge nodes, run 'zold remote reset' or use --tolerate-edges"
109
+ end
110
+ end
101
111
  @log.info("#{done.value} copies of #{id} fetched in #{Age.new(start)} with the total score of \
102
- #{total.value} from #{nodes.value} nodes")
112
+ #{total.value} from #{nodes.value} nodes (#{defaults.value} defaults)")
103
113
  list = cps.all.map do |c|
104
114
  " ##{c[:name]}: #{c[:score]} #{Wallet.new(c[:path]).mnemo} \
105
115
  #{Size.new(File.size(c[:path]))}/#{Age.new(File.mtime(c[:path]))}"
@@ -51,6 +51,9 @@ Available options:"
51
51
  o.integer '--length',
52
52
  'The length of the invoice prefix (default: 8)',
53
53
  default: 8
54
+ o.bool '--tolerate-edges',
55
+ 'Don\'t fail if only "edge" (not default ones) nodes have the wallet',
56
+ default: false
54
57
  o.string '--network',
55
58
  'The name of the network we work in',
56
59
  default: 'test'
@@ -67,7 +70,8 @@ Available options:"
67
70
  unless @wallets.acq(id, &:exists?)
68
71
  require_relative 'pull'
69
72
  Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
70
- ['pull', id.to_s, "--network=#{opts['network']}"]
73
+ ['pull', id.to_s, "--network=#{opts['network']}"] +
74
+ (opts['tolerate-edges'] ? ['--tolerate-edges'] : [])
71
75
  )
72
76
  end
73
77
  inv = @wallets.acq(id) do |wallet|
@@ -78,6 +78,12 @@ Available options:"
78
78
  cps = cps.all.sort_by { |c| c[:score] }.reverse
79
79
  patch = Patch.new(@wallets, log: @log)
80
80
  score = 0
81
+ cps.each_with_index do |c, idx|
82
+ wallet = Wallet.new(c[:path])
83
+ name = "#{c[:name]}/#{idx}/#{c[:score]}"
84
+ merge_one(opts, patch, wallet, name)
85
+ score += c[:score]
86
+ end
81
87
  @wallets.acq(id) do |w|
82
88
  if w.exists?
83
89
  merge_one(opts, patch, w, 'localhost')
@@ -86,12 +92,6 @@ Available options:"
86
92
  @log.debug("Local copy of #{id} is absent, nothing to merge")
87
93
  end
88
94
  end
89
- cps.each_with_index do |c, idx|
90
- wallet = Wallet.new(c[:path])
91
- name = "#{c[:name]}/#{idx}/#{c[:score]}"
92
- merge_one(opts, patch, wallet, name)
93
- score += c[:score]
94
- end
95
95
  modified = @wallets.acq(id, exclusive: true) { |w| patch.save(w.path, overwrite: true) }
96
96
  if modified
97
97
  @log.info("#{cps.count} copies with the total score of #{score} successfully merged \
@@ -30,8 +30,10 @@ require_relative 'thread_badge'
30
30
  require_relative '../version'
31
31
  require_relative '../age'
32
32
  require_relative '../metronome'
33
+ require_relative '../thread_pool'
33
34
  require_relative '../wallet'
34
35
  require_relative '../wallets'
36
+ require_relative '../hungry_wallets'
35
37
  require_relative '../remotes'
36
38
  require_relative '../verbose_thread'
37
39
  require_relative '../node/farmers'
@@ -95,6 +97,9 @@ module Zold
95
97
  o.bool '--ignore-score-weakness',
96
98
  'Ignore score weakness of incoming requests and register those nodes anyway',
97
99
  default: false
100
+ o.bool '--tolerate-edges',
101
+ 'Don\'t fail if only "edge" (not default ones) nodes accepted/have the wallet',
102
+ default: false
98
103
  o.boolean '--nohup',
99
104
  'Run it in background, rebooting when a higher version is available in the network',
100
105
  default: false
@@ -134,8 +139,8 @@ module Zold
134
139
  'Maximum amount of memory we can consume, quit if we take more than that, in Mb (default: 512)',
135
140
  default: 512
136
141
  o.integer '--queue-limit',
137
- 'The maximum number of wallets to be accepted via PUSH and stored in the queue (default: 4096)',
138
- default: 4096
142
+ 'The maximum number of wallets to be accepted via PUSH and stored in the queue (default: 256)',
143
+ default: 256
139
144
  o.integer '--gc-age',
140
145
  'Maximum time in seconds to keep an empty and unused wallet on the disk',
141
146
  default: 60 * 60 * 24 * 10
@@ -213,8 +218,10 @@ module Zold
213
218
  Zold::Remote.new(remotes: @remotes).run(['remote', 'remove', host, port.to_s])
214
219
  @log.info("Removed current node (#{address}) from list of remotes")
215
220
  end
221
+ hungry = Zold::ThreadPool.new('hungry', log: @log)
222
+ wts = Zold::HungryWallets.new(@wallets, @remotes, @copies, hungry, log: @log, network: opts['network'])
216
223
  Front.set(:zache, Zache.new)
217
- Front.set(:wallets, @wallets)
224
+ Front.set(:wallets, wts)
218
225
  Front.set(:remotes, @remotes)
219
226
  Front.set(:copies, @copies)
220
227
  Front.set(:address, address)
@@ -222,18 +229,7 @@ module Zold
222
229
  Front.set(:opts, opts)
223
230
  Front.set(:dump_errors, opts['dump-errors'])
224
231
  Front.set(:port, opts['bind-port'])
225
- node_alias = opts[:alias] || address
226
- unless node_alias.eql?(address) || node_alias =~ /^[A-Za-z0-9]{4,16}$/
227
- raise "Alias should be a 4 to 16 char long alphanumeric string: #{node_alias}"
228
- end
229
- Front.set(:node_alias, node_alias)
230
- invoice = opts[:invoice]
231
- unless invoice.include?('@')
232
- require_relative 'invoice'
233
- invoice = Invoice.new(
234
- wallets: @wallets, remotes: @remotes, copies: @copies, log: @log
235
- ).run(['invoice', invoice, "--network=#{opts['network']}"])
236
- end
232
+ Front.set(:node_alias, node_alias(opts, address))
237
233
  entrance = SafeEntrance.new(
238
234
  NoSpamEntrance.new(
239
235
  NoDupEntrance.new(
@@ -241,22 +237,22 @@ module Zold
241
237
  SpreadEntrance.new(
242
238
  SyncEntrance.new(
243
239
  Entrance.new(
244
- @wallets,
245
- @remotes, @copies, address,
240
+ wts, @remotes, @copies, address,
246
241
  log: @log, network: opts['network']
247
242
  ),
248
243
  File.join(home, '.zoldata/sync-entrance'),
249
244
  log: @log
250
245
  ),
251
- @wallets, @remotes, address,
246
+ wts, @remotes, address,
252
247
  log: @log,
253
- ignore_score_weakeness: opts['ignore-score-weakness']
248
+ ignore_score_weakeness: opts['ignore-score-weakness'],
249
+ tolerate_edges: opts['tolerate-edges']
254
250
  ),
255
251
  File.join(home, '.zoldata/async-entrance'),
256
252
  log: @log,
257
253
  queue_limit: opts['queue-limit']
258
254
  ),
259
- @wallets,
255
+ wts,
260
256
  log: @log
261
257
  ),
262
258
  period: opts['allow-spam'] ? 0 : 60 * 60,
@@ -266,7 +262,10 @@ module Zold
266
262
  )
267
263
  entrance.start do |ent|
268
264
  Front.set(:entrance, ent)
269
- farm = Farm.new(invoice, File.join(home, 'farm'), log: @log, farmer: farmer(opts), strength: opts[:strength])
265
+ farm = Farm.new(
266
+ invoice(opts), File.join(home, 'farm'),
267
+ log: @log, farmer: farmer(opts), strength: opts[:strength]
268
+ )
270
269
  farm.start(host, opts[:port], threads: opts[:threads]) do |f|
271
270
  Front.set(:farm, f)
272
271
  metronome(f, opts).start do |metronome|
@@ -277,11 +276,32 @@ module Zold
277
276
  end
278
277
  end
279
278
  end
279
+ hungry.kill
280
280
  @log.info('Thanks for helping Zold network!')
281
281
  end
282
282
 
283
283
  private
284
284
 
285
+ def invoice(opts)
286
+ invoice = opts['invoice']
287
+ unless invoice.include?('@')
288
+ require_relative 'invoice'
289
+ invoice = Invoice.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
290
+ ['invoice', invoice, "--network=#{opts['network']}"] +
291
+ (opts['tolerate-edges'] ? ['--tolerate-edges'] : [])
292
+ )
293
+ end
294
+ invoice
295
+ end
296
+
297
+ def node_alias(opts, address)
298
+ a = opts[:alias] || address
299
+ unless a.eql?(address) || a =~ /^[A-Za-z0-9]{4,16}$/
300
+ raise "Alias should be a 4 to 16 char long alphanumeric string: #{a}"
301
+ end
302
+ a
303
+ end
304
+
285
305
  def farmer(opts)
286
306
  case opts['farmer'].downcase.strip
287
307
  when 'plain'
@@ -58,6 +58,12 @@ Available options:"
58
58
  o.bool '--ignore-score-weakness',
59
59
  'Don\'t complain when their score is too weak',
60
60
  default: false
61
+ o.bool '--tolerate-edges',
62
+ 'Don\'t fail if only "edge" (not default ones) nodes accepted the wallet',
63
+ default: false
64
+ o.bool '--quiet-if-missed',
65
+ 'Don\'t fail if the wallet wasn\'t delivered to any remotes',
66
+ default: false
61
67
  o.array '--ignore-node',
62
68
  'Ignore this node and don\'t push to it',
63
69
  default: []
@@ -76,19 +82,26 @@ Available options:"
76
82
  private
77
83
 
78
84
  def push(id, opts)
79
- total = 0
80
- nodes = 0
81
- done = 0
85
+ total = Concurrent::AtomicFixnum.new
86
+ nodes = Concurrent::AtomicFixnum.new
87
+ done = Concurrent::AtomicFixnum.new
88
+ defaults = Concurrent::AtomicFixnum.new
82
89
  start = Time.now
83
90
  @remotes.iterate(@log) do |r|
84
- nodes += 1
85
- total += push_one(id, r, opts)
86
- done += 1
91
+ nodes.increment
92
+ total.increment(push_one(id, r, opts))
93
+ defaults.increment if r.default?
94
+ done.increment
95
+ end
96
+ raise "There are no remote nodes, run 'zold remote reset'" if nodes.value.zero?
97
+ unless opts['quiet-if-missed']
98
+ raise "No nodes out of #{nodes} accepted the wallet #{id}" if done.value.zero?
99
+ if defaults.value.zero? && !opts['tolerate-edges']
100
+ raise "There are only edge nodes, run 'zold remote reset' or use --tolerate-edges"
101
+ end
87
102
  end
88
- raise "There are no remote nodes, run 'zold remote reset'" if nodes.zero?
89
- raise "No nodes out of #{nodes} accepted the wallet #{id}" if done.zero?
90
- @log.info("Push finished to #{done} nodes out of #{nodes} in #{Age.new(start)}, \
91
- total score for #{id} is #{total}")
103
+ @log.info("Push finished to #{done.value} nodes (#{defaults.value} defaults) \
104
+ out of #{nodes.value} in #{Age.new(start)}, total score for #{id} is #{total.value}")
92
105
  end
93
106
 
94
107
  def push_one(id, r, opts)
@@ -21,14 +21,43 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'delegate'
24
+ require_relative 'log'
25
+ require_relative 'thread_pool'
26
+ require_relative 'commands/pull'
24
27
 
28
+ # Wallets that PULL what's missing, in the background.
29
+ #
30
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
32
+ # License:: MIT
25
33
  module Zold
26
34
  # Wallets decorator that adds missing wallets to the queue to be pulled later.
27
35
  class HungryWallets < SimpleDelegator
28
- # @todo #280:30min Add to the queue. Once in there, try
29
- # to pull it as soon as possible as is described in #280.
36
+ def initialize(wallets, remotes, copies, pool,
37
+ log: Log::NULL, network: 'test')
38
+ @wallets = wallets
39
+ @remotes = remotes
40
+ @copies = copies
41
+ @log = log
42
+ @network = network
43
+ @pool = pool
44
+ @queue = Queue.new
45
+ @pool.add do
46
+ Endless.new('hungry', log: log).run do
47
+ id = @queue.pop
48
+ Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
49
+ ['pull', id.to_s, "--network=#{@network}"]
50
+ )
51
+ end
52
+ end
53
+ super(wallets)
54
+ end
55
+
30
56
  def acq(id, exclusive: false)
31
- yield super(id, exclusive: exclusive)
57
+ @wallets.acq(id, exclusive: exclusive) do |wallet|
58
+ @queue << id unless wallet.exists?
59
+ yield wallet
60
+ end
32
61
  end
33
62
  end
34
63
  end
@@ -79,7 +79,7 @@ module Zold
79
79
  end
80
80
  modified = Merge.new(
81
81
  wallets: @wallets, copies: copies.root, log: @log
82
- ).run(['merge', id.to_s])
82
+ ).run(['merge', id.to_s, '--no-baseline'])
83
83
  Clean.new(wallets: @wallets, copies: copies.root, log: @log).run(['clean', id.to_s])
84
84
  copies.remove(localhost, Remotes::PORT)
85
85
  if modified.empty?
@@ -41,13 +41,15 @@ require_relative '../commands/clean'
41
41
  module Zold
42
42
  # The entrance
43
43
  class SpreadEntrance
44
- def initialize(entrance, wallets, remotes, address, log: Log::NULL, ignore_score_weakeness: false)
44
+ def initialize(entrance, wallets, remotes, address, log: Log::NULL,
45
+ ignore_score_weakeness: false, tolerate_edges: false)
45
46
  @entrance = entrance
46
47
  @wallets = wallets
47
48
  @remotes = remotes
48
49
  @address = address
49
50
  @log = log
50
51
  @ignore_score_weakeness = ignore_score_weakeness
52
+ @tolerate_edges = tolerate_edges
51
53
  @mutex = Mutex.new
52
54
  @push = ThreadPool.new('spread-entrance')
53
55
  end
@@ -75,7 +77,8 @@ module Zold
75
77
  Thread.current.thread_variable_set(:wallet, id.to_s)
76
78
  Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
77
79
  ['push', "--ignore-node=#{@address}", id.to_s] +
78
- (@ignore_score_weakeness ? ['--ignore-score-weakness'] : [])
80
+ (@ignore_score_weakeness ? ['--ignore-score-weakness'] : []) +
81
+ (@tolerate_edges ? ['--tolerate-edges'] : [])
79
82
  )
80
83
  end
81
84
  @mutex.synchronize { @seen.delete(id) }
data/lib/zold/remotes.rb CHANGED
@@ -88,6 +88,10 @@ module Zold
88
88
  Http.new(uri: "http://#{@host}:#{@port}#{path}", score: @score, network: @network)
89
89
  end
90
90
 
91
+ def default?
92
+ !DEFS.find { |r| r[0] == @host && r[1].to_i == @port }.nil?
93
+ end
94
+
91
95
  def to_s
92
96
  "#{@host}:#{@port}/#{@idx}"
93
97
  end
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'futex'
24
+ require 'delegate'
24
25
  require_relative 'log'
25
26
 
26
27
  # Synchronized collection of wallets.
@@ -32,27 +33,12 @@ require_relative 'log'
32
33
  # License:: MIT
33
34
  module Zold
34
35
  # Synchronized collection of wallets
35
- class SyncWallets
36
+ class SyncWallets < SimpleDelegator
36
37
  def initialize(wallets, log: Log::NULL, dir: wallets.path)
37
38
  @wallets = wallets
38
39
  @log = log
39
40
  @dir = dir
40
- end
41
-
42
- def to_s
43
- @wallets.to_s
44
- end
45
-
46
- def path
47
- @wallets.path
48
- end
49
-
50
- def all
51
- @wallets.all
52
- end
53
-
54
- def count
55
- @wallets.count
41
+ super(wallets)
56
42
  end
57
43
 
58
44
  def acq(id, exclusive: false)
@@ -44,17 +44,22 @@ module Zold
44
44
  idx = Concurrent::AtomicFixnum.new
45
45
  mutex = Mutex.new
46
46
  list = set.dup
47
- [threads, set.count].min.times do
47
+ total = [threads, set.count].min
48
+ latch = Concurrent::CountDownLatch.new(total)
49
+ total.times do |i|
48
50
  add do
51
+ Thread.current.name = "#{@title}-#{i}"
49
52
  loop do
50
53
  r = mutex.synchronize { list.pop }
51
54
  break if r.nil?
52
55
  yield(r, idx.increment - 1)
53
56
  end
57
+ ensure
58
+ latch.count_down
54
59
  end
55
60
  end
56
- @threads.each(&:join)
57
- @threads.clear
61
+ latch.wait
62
+ kill
58
63
  end
59
64
 
60
65
  # Add a new thread
@@ -62,6 +67,7 @@ module Zold
62
67
  raise 'Block must be given to start()' unless block_given?
63
68
  latch = Concurrent::CountDownLatch.new(1)
64
69
  thread = Thread.start do
70
+ Thread.current.name = @title
65
71
  VerboseThread.new(@log).run do
66
72
  latch.count_down
67
73
  yield
@@ -75,22 +81,29 @@ module Zold
75
81
  @threads << thread
76
82
  end
77
83
 
84
+ def join(sec)
85
+ @threads.each { |t| t.join(sec) }
86
+ end
87
+
78
88
  # Kill them all immediately and close the pool
79
89
  def kill
80
90
  if @threads.empty?
81
91
  @log.debug("Thread pool \"#{@title}\" terminated with no threads")
82
92
  return
83
93
  end
84
- @log.info("Stopping \"#{@title}\" thread pool with #{@threads.count} threads: \
94
+ @log.debug("Stopping \"#{@title}\" thread pool with #{@threads.count} threads: \
85
95
  #{@threads.map { |t| "#{t.name}/#{t.status}" }.join(', ')}...")
86
96
  start = Time.new
87
97
  @threads.each do |t|
88
98
  (t.thread_variable_get(:kids) || []).each(&:kill)
89
99
  t.kill
90
100
  raise "Failed to join the thread \"#{t.name}\" in \"#{@title}\" pool" unless t.join(0.1)
91
- (Thread.current.thread_variable_get(:kids) || []).delete(t)
101
+ Thread.current.thread_variable_set(
102
+ :kids,
103
+ (Thread.current.thread_variable_get(:kids) || []) - [t]
104
+ )
92
105
  end
93
- @log.info("Thread pool \"#{@title}\" terminated all threads in #{Age.new(start)}, \
106
+ @log.debug("Thread pool \"#{@title}\" terminated all threads in #{Age.new(start)}, \
94
107
  it was alive for #{Age.new(@start)}: #{@threads.map { |t| "#{t.name}/#{t.status}" }.join(', ')}")
95
108
  @threads.clear
96
109
  end
@@ -35,6 +35,7 @@ module Zold
35
35
  end
36
36
 
37
37
  def run(safe = false)
38
+ Thread.current.report_on_exception = false
38
39
  yield
39
40
  rescue StandardError => e
40
41
  @log.error(Backtrace.new(e).to_s)
data/lib/zold/version.rb CHANGED
@@ -25,7 +25,7 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.18.8'
28
+ VERSION = '0.18.9'
29
29
  PROTOCOL = 2
30
30
  REPO = 'zold-io/zold'
31
31
  end
@@ -39,11 +39,7 @@ require_relative '../node/fake_node'
39
39
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
40
40
  # License:: MIT
41
41
  class TestNode < Zold::Test
42
- # @todo #306:30min This test is failing from time to time
43
- # We should find a way to check that tests involved in thread concurrency
44
- # are always working
45
42
  def test_push_and_fetch
46
- skip
47
43
  FakeHome.new(log: test_log).run do |home|
48
44
  FakeNode.new(log: test_log).run do |port|
49
45
  wallets = home.wallets
@@ -51,7 +47,7 @@ class TestNode < Zold::Test
51
47
  remotes = home.remotes
52
48
  remotes.add('localhost', port)
53
49
  Zold::Push.new(wallets: wallets, remotes: remotes, log: test_log).run(
54
- ['push', '--ignore-score-weakness']
50
+ ['push', '--ignore-score-weakness', '--tolerate-edges']
55
51
  )
56
52
  copies = home.copies(wallet)
57
53
  begin
@@ -59,7 +55,7 @@ class TestNode < Zold::Test
59
55
  Zold::Fetch.new(
60
56
  wallets: wallets, copies: copies.root,
61
57
  remotes: remotes, log: test_log
62
- ).run(['fetch', '--ignore-score-weakness'])
58
+ ).run(['fetch', '--ignore-score-weakness', '--tolerate-edges'])
63
59
  rescue StandardError => _
64
60
  sleep 1
65
61
  retry if (retries += 1) < 3
@@ -42,7 +42,7 @@ class TestPull < Zold::Test
42
42
  stub_request(:get, "http://localhost:4096/wallet/#{id}/size").to_return(status: 200, body: '10000')
43
43
  stub_request(:get, "http://localhost:4096/wallet/#{id}").to_return(status: 200, body: json)
44
44
  Zold::Pull.new(wallets: home.wallets, remotes: remotes, copies: home.copies.root.to_s, log: test_log).run(
45
- ['--ignore-this-stupid-option', 'pull', id.to_s]
45
+ ['--ignore-this-stupid-option', 'pull', id.to_s, '--tolerate-edges']
46
46
  )
47
47
  home.wallets.acq(Zold::Id.new(id)) do |wallet|
48
48
  assert(wallet.exists?)
@@ -43,7 +43,7 @@ class TestPush < Zold::Test
43
43
  remotes.add('localhost', 80)
44
44
  stub_request(:put, "http://localhost:80/wallet/#{wallet.id}").to_return(status: 304)
45
45
  Zold::Push.new(wallets: home.wallets, remotes: remotes, log: test_log).run(
46
- ['--ignore-this-stupid-option', 'push', wallet.id.to_s]
46
+ ['--ignore-this-stupid-option', 'push', wallet.id.to_s, '--tolerate-edges']
47
47
  )
48
48
  end
49
49
  end
@@ -58,7 +58,7 @@ class TestPush < Zold::Test
58
58
  stub_request(:put, "http://localhost:80/wallet/#{wallet_a.id}").to_return(status: 304)
59
59
  stub_request(:put, "http://localhost:80/wallet/#{wallet_b.id}").to_return(status: 304)
60
60
  Zold::Push.new(wallets: home.wallets, remotes: remotes, log: log).run(
61
- ['--ignore-this-stupid-option --threads 2', 'push', wallet_a.id.to_s, wallet_b.id.to_s]
61
+ ['--tolerate-edges', '--threads=2', 'push', wallet_a.id.to_s, wallet_b.id.to_s]
62
62
  )
63
63
  end
64
64
  end
@@ -44,8 +44,8 @@ class FakeNode
44
44
  RandomPort::Pool::SINGLETON.acquire do |port|
45
45
  node = Thread.new do
46
46
  Thread.current.name = 'fake_node'
47
+ Thread.current.abort_on_exception = true
47
48
  Zold::VerboseThread.new(@log).run do
48
- Thread.current.abort_on_exception = true
49
49
  require_relative '../../lib/zold/commands/node'
50
50
  Zold::Node.new(wallets: home.wallets, remotes: home.remotes, copies: home.copies.root, log: @log).run(
51
51
  [
data/test/test__helper.rb CHANGED
@@ -63,10 +63,14 @@ module Zold
63
63
  def assert_equal_wait(expected, max: 30)
64
64
  start = Time.now
65
65
  loop do
66
- actual = yield
67
- if expected == actual
68
- assert_equal(expected, actual)
69
- break
66
+ begin
67
+ actual = yield
68
+ if expected == actual
69
+ assert_equal(expected, actual)
70
+ break
71
+ end
72
+ rescue StandardError => e
73
+ test_log.debug(e.message)
70
74
  end
71
75
  sleep 1
72
76
  sec = Time.now - start
@@ -0,0 +1,53 @@
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 'webmock/minitest'
25
+ require_relative 'test__helper'
26
+ require_relative 'fake_home'
27
+ require_relative '../lib/zold/key'
28
+ require_relative '../lib/zold/thread_pool'
29
+ require_relative '../lib/zold/wallets'
30
+ require_relative '../lib/zold/hungry_wallets'
31
+
32
+ # HungryWallets test.
33
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
34
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
35
+ # License:: MIT
36
+ class TestHungryWallets < Zold::Test
37
+ def test_pulls_wallet
38
+ FakeHome.new(log: test_log).run do |home|
39
+ id = Zold::Id.new
40
+ get = stub_request(:get, "http://localhost:4096/wallet/#{id}/size").to_return(status: 404)
41
+ remotes = home.remotes
42
+ remotes.add('localhost', 4096)
43
+ pool = Zold::ThreadPool.new('test', log: test_log)
44
+ wallets = Zold::HungryWallets.new(
45
+ home.wallets, remotes, File.join(home.dir, 'copies'),
46
+ pool, log: test_log
47
+ )
48
+ wallets.acq(id) { |w| assert(!w.exists?) }
49
+ pool.join(1)
50
+ assert_requested(get)
51
+ end
52
+ end
53
+ end
@@ -69,6 +69,14 @@ class TestThreadPool < Zold::Test
69
69
  assert_equal('0 1 2', indexes.to_a.sort.join(' '))
70
70
  end
71
71
 
72
+ def test_runs_with_exceptions
73
+ assert_raises do
74
+ Zold::ThreadPool.new('test', log: test_log).run(1) do
75
+ raise 'intended'
76
+ end
77
+ end
78
+ end
79
+
72
80
  def test_adds_and_stops
73
81
  pool = Zold::ThreadPool.new('test', log: test_log)
74
82
  pool.add do
data/test/test_zold.rb CHANGED
@@ -34,9 +34,10 @@ require_relative '../lib/zold/age'
34
34
  # License:: MIT
35
35
  class TestZold < Zold::Test
36
36
  Dir.new('fixtures/scripts').select { |f| f =~ /\.sh$/ && !f.start_with?('_') }.each do |f|
37
- define_method("test_#{f.gsub(/\.sh$/, '').gsub(/[^a-z]/, '_')}") do
37
+ method = "test_#{f.gsub(/\.sh$/, '').gsub(/[^a-z]/, '_')}"
38
+ define_method(method) do
38
39
  start = Time.now
39
- test_log.info("\n\n#{f} running...")
40
+ test_log.info("\n\n#{method} running (script at #{f})...")
40
41
  Dir.mktmpdir do |dir|
41
42
  FileUtils.cp('fixtures/id_rsa.pub', dir)
42
43
  FileUtils.cp('fixtures/id_rsa', dir)
data/zold.gemspec CHANGED
@@ -61,12 +61,12 @@ and suggests a different architecture for digital wallet maintenance.'
61
61
  s.test_files = s.files.grep(%r{^(test|features)/})
62
62
  s.rdoc_options = ['--charset=UTF-8']
63
63
  s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
64
- s.add_runtime_dependency 'backtrace', '0.3.0'
64
+ s.add_runtime_dependency 'backtrace', '~>0'
65
65
  s.add_runtime_dependency 'concurrent-ruby', '1.1.3'
66
66
  s.add_runtime_dependency 'cucumber', '3.1.2' # has to stay here for Heroku
67
67
  s.add_runtime_dependency 'diffy', '3.2.1'
68
- s.add_runtime_dependency 'futex', '0.7.0'
69
- s.add_runtime_dependency 'get_process_mem', '0.2.3'
68
+ s.add_runtime_dependency 'futex', '~>0'
69
+ s.add_runtime_dependency 'get_process_mem', '~>0.2'
70
70
  s.add_runtime_dependency 'json', '2.1.0'
71
71
  s.add_runtime_dependency 'memory_profiler', '0.9.12'
72
72
  s.add_runtime_dependency 'openssl', '2.1.2'
@@ -79,12 +79,12 @@ and suggests a different architecture for digital wallet maintenance.'
79
79
  s.add_runtime_dependency 'slop', '4.6.2'
80
80
  s.add_runtime_dependency 'sys-proctable', '1.2.1'
81
81
  s.add_runtime_dependency 'thin', '1.7.2'
82
- s.add_runtime_dependency 'threads', '0.3.0'
82
+ s.add_runtime_dependency 'threads', '~>0'
83
83
  s.add_runtime_dependency 'typhoeus', '1.3.1'
84
84
  s.add_runtime_dependency 'usagewatch_ext', '0.2.1'
85
- s.add_runtime_dependency 'xcop', '0.6'
86
- s.add_runtime_dependency 'zache', '0.4.0'
87
- s.add_runtime_dependency 'zold-score', '0.4.3'
85
+ s.add_runtime_dependency 'xcop', '~>0'
86
+ s.add_runtime_dependency 'zache', '~>0'
87
+ s.add_runtime_dependency 'zold-score', '0.4.4'
88
88
  s.add_development_dependency 'codecov', '0.1.13'
89
89
  s.add_development_dependency 'minitest', '5.11.3'
90
90
  s.add_development_dependency 'minitest-fail-fast', '0.1.0'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.8
4
+ version: 0.18.9
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-12-14 00:00:00.000000000 Z
11
+ date: 2018-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.0
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '='
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.3.0
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: concurrent-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -70,30 +70,30 @@ dependencies:
70
70
  name: futex
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '='
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.7.0
75
+ version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '='
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.7.0
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: get_process_mem
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '='
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.2.3
89
+ version: '0.2'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '='
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.2.3
96
+ version: '0.2'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: json
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -266,16 +266,16 @@ dependencies:
266
266
  name: threads
267
267
  requirement: !ruby/object:Gem::Requirement
268
268
  requirements:
269
- - - '='
269
+ - - "~>"
270
270
  - !ruby/object:Gem::Version
271
- version: 0.3.0
271
+ version: '0'
272
272
  type: :runtime
273
273
  prerelease: false
274
274
  version_requirements: !ruby/object:Gem::Requirement
275
275
  requirements:
276
- - - '='
276
+ - - "~>"
277
277
  - !ruby/object:Gem::Version
278
- version: 0.3.0
278
+ version: '0'
279
279
  - !ruby/object:Gem::Dependency
280
280
  name: typhoeus
281
281
  requirement: !ruby/object:Gem::Requirement
@@ -308,44 +308,44 @@ dependencies:
308
308
  name: xcop
309
309
  requirement: !ruby/object:Gem::Requirement
310
310
  requirements:
311
- - - '='
311
+ - - "~>"
312
312
  - !ruby/object:Gem::Version
313
- version: '0.6'
313
+ version: '0'
314
314
  type: :runtime
315
315
  prerelease: false
316
316
  version_requirements: !ruby/object:Gem::Requirement
317
317
  requirements:
318
- - - '='
318
+ - - "~>"
319
319
  - !ruby/object:Gem::Version
320
- version: '0.6'
320
+ version: '0'
321
321
  - !ruby/object:Gem::Dependency
322
322
  name: zache
323
323
  requirement: !ruby/object:Gem::Requirement
324
324
  requirements:
325
- - - '='
325
+ - - "~>"
326
326
  - !ruby/object:Gem::Version
327
- version: 0.4.0
327
+ version: '0'
328
328
  type: :runtime
329
329
  prerelease: false
330
330
  version_requirements: !ruby/object:Gem::Requirement
331
331
  requirements:
332
- - - '='
332
+ - - "~>"
333
333
  - !ruby/object:Gem::Version
334
- version: 0.4.0
334
+ version: '0'
335
335
  - !ruby/object:Gem::Dependency
336
336
  name: zold-score
337
337
  requirement: !ruby/object:Gem::Requirement
338
338
  requirements:
339
339
  - - '='
340
340
  - !ruby/object:Gem::Version
341
- version: 0.4.3
341
+ version: 0.4.4
342
342
  type: :runtime
343
343
  prerelease: false
344
344
  version_requirements: !ruby/object:Gem::Requirement
345
345
  requirements:
346
346
  - - '='
347
347
  - !ruby/object:Gem::Version
348
- version: 0.4.3
348
+ version: 0.4.4
349
349
  - !ruby/object:Gem::Dependency
350
350
  name: codecov
351
351
  requirement: !ruby/object:Gem::Requirement
@@ -656,6 +656,7 @@ files:
656
656
  - test/test_gem.rb
657
657
  - test/test_hexnum.rb
658
658
  - test/test_http.rb
659
+ - test/test_hungry_wallets.rb
659
660
  - test/test_id.rb
660
661
  - test/test_json_page.rb
661
662
  - test/test_key.rb
@@ -689,7 +690,7 @@ licenses:
689
690
  - MIT
690
691
  metadata: {}
691
692
  post_install_message: |-
692
- Thanks for installing Zold 0.18.8!
693
+ Thanks for installing Zold 0.18.9!
693
694
  Study our White Paper: https://papers.zold.io/wp.pdf
694
695
  Read our blog posts: https://blog.zold.io
695
696
  Try online wallet at: https://wts.zold.io
@@ -765,6 +766,7 @@ test_files:
765
766
  - test/test_gem.rb
766
767
  - test/test_hexnum.rb
767
768
  - test/test_http.rb
769
+ - test/test_hungry_wallets.rb
768
770
  - test/test_id.rb
769
771
  - test/test_json_page.rb
770
772
  - test/test_key.rb