zold 0.18.8 → 0.18.9

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: 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