zold 0.22.3 → 0.22.4

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: 5be8d7043de82577c3705e6e9fd22bef60745a8f08e08080f6449592f9dcbac4
4
- data.tar.gz: 85390c7ab1d44af1a78c8bd51e08bf57b778e0f46eec912d03514eda2c6cc653
3
+ metadata.gz: ad91826d6e1a3e8d2b8b55ccdbf662ed3462098ad9ac97a638ca3a315ca16bf0
4
+ data.tar.gz: 139cd5314e6dd0dac0a10f20f0933e5e1d826820f4a3386eb3f7c643b49d8acc
5
5
  SHA512:
6
- metadata.gz: 1ce589ed9cb20513c176bd3ddc3146c0a8803fb7362b18fbd8cbe97539cd93367fa07e3afba617e0f87ae340dcfd4d7aea4a6ec9ee0c02264c021c09c4985f5c
7
- data.tar.gz: 8d9f92fb127a19192249748531fe19fccb782f7f7243950ae16348c43a3edbeb6f422c3c2556bc5fde7e783913b19c2046470a978c740d8b7ec88a5e6b37f5e8
6
+ metadata.gz: 3004779c370ec2ce966c89cbfa4e004c5b8644e164da6ab7a6dfb3be0eb64020853979eb6cf9ab2b8a980f0d6b0b8b610dfa44a47cdb351bd25c3d7b95341702
7
+ data.tar.gz: e9901486c5cb9f653723687091a25a4902e30818757a4cd56d71c7dced8a83c5dfbefd8de77b73b5053c3e91836e6edd33dbf1169edb914e10d2e6691f35a5b5
data/.rubocop.yml CHANGED
@@ -18,7 +18,7 @@ Metrics/AbcSize:
18
18
  Metrics/BlockNesting:
19
19
  Max: 4
20
20
  Metrics/BlockLength:
21
- Max: 120
21
+ Max: 130
22
22
  Metrics/ClassLength:
23
23
  Max: 450
24
24
  Layout/EndOfLine:
data/Dockerfile ADDED
@@ -0,0 +1,14 @@
1
+ FROM ruby:2.6
2
+
3
+ RUN gem install zold
4
+ EXPOSE 4096
5
+
6
+ RUN echo "#!/bin/bash" > node.sh
7
+ RUN echo "zold node --nohup \044\100" >> node.sh
8
+ RUN echo "tail -f zold.log" >> node.sh
9
+ RUN chmod +x /node.sh
10
+
11
+ RUN mkdir /zold
12
+ WORKDIR /zold
13
+
14
+ CMD ["/node.sh"]
data/README.md CHANGED
@@ -127,6 +127,19 @@ If you are lost, run this:
127
127
  $ zold node --help
128
128
  ```
129
129
 
130
+ You can run a node in a docker container also.
131
+
132
+ ```bash
133
+ docker run -d -p 4096:4096 zold/zold:latest /node.sh --host=<your host IP> --invoice=5f96e731e48ae21f
134
+ ```
135
+
136
+ To store zold data between container restarts create a volume or bind a directory from host
137
+
138
+ ```bash
139
+ docker volume create zold
140
+ docker run -d -p 4096:4096 -v zold:/zold zold/zold:latest /node.sh --host=<your host IP> --invoice=5f96e731e48ae21f
141
+ ```
142
+
130
143
  ## Frequently Asked Questions
131
144
 
132
145
  > Where are my RSA private/public keys?
data/bin/zold CHANGED
@@ -276,14 +276,13 @@ else
276
276
  code = cmd.call
277
277
  end
278
278
 
279
+ Zold::Hands.stop
280
+
279
281
  log.debug("Memory footprint at the end is #{Zold::Size.new(GetProcessMem.new.bytes.to_i)}")
280
282
  if code.zero?
281
- log.debug("Failed in in #{Zold::Age.new(start)}")
282
- exit(code)
283
- else
284
283
  log.debug("Successfully finished in #{Zold::Age.new(start)}")
284
+ else
285
+ log.debug("Failed in #{Zold::Age.new(start)}")
286
+ exit(code)
285
287
  end
286
288
 
287
- Zold::Hands.stop
288
-
289
- abort # We need this in case some threads are still alive
@@ -6,7 +6,7 @@ function start_node {
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
8
  --expose-version=$2 --save-pid=pid --routine-immediately --tolerate-edges --tolerate-quorum=1 \
9
- --verbose --trace --invoice=REDEPLOY@ffffffffffffffff \
9
+ --verbose --trace --invoice=REDEPLOY@ffffffffffffffff --ignore-empty-remotes \
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
12
12
  cat pid
@@ -170,8 +170,8 @@ run 'zold remote update' or use --tolerate-quorum=1"
170
170
  end
171
171
  copy = cps.add(IO.read(f), score.host, score.port, score.value, master: r.master?)
172
172
  @log.info("#{r} returned #{wallet.mnemo} #{Age.new(json['mtime'])}/#{json['copies']}c \
173
- as copy ##{copy}/#{cps.all.count} in #{Age.new(start, limit: 4)}: \
174
- #{Rainbow(score.value).green} (#{json['version']})")
173
+ as copy ##{copy}/#{cps.all.count} in #{Age.new(start, limit: 4)}: \
174
+ #{Rainbow(score.value).green} (#{json['version']})")
175
175
  end
176
176
  end
177
177
  score.value
@@ -183,6 +183,7 @@ run 'zold remote update' or use --tolerate-quorum=1"
183
183
  begin
184
184
  uri = "/wallet/#{id}"
185
185
  head = r.http(uri).get
186
+ raise "The wallet #{id} doesn't exist at #{r}" if head.status == 404
186
187
  r.assert_code(200, head)
187
188
  json = JsonPage.new(head.body, uri).to_hash
188
189
  score = Score.parse_json(json['score'])
@@ -145,6 +145,9 @@ module Zold
145
145
  o.bool '--allow-spam',
146
146
  'Don\'t filter the incoming spam via PUT requests (duplicate wallets)',
147
147
  default: false
148
+ o.bool '--ignore-empty-remotes',
149
+ 'Don\'t fail if the list of remotes is empty (for testing mostly)',
150
+ default: false
148
151
  o.bool '--skip-oom',
149
152
  'Skip Out Of Memory check and never exit, no matter how much RAM is consumed',
150
153
  default: false
@@ -197,6 +200,10 @@ module Zold
197
200
  end
198
201
  raise '--invoice is mandatory' unless opts['invoice']
199
202
  if opts['nohup']
203
+ if @remotes.all.empty? && !opts['standalone'] && !opts['ignore-empty-remotes']
204
+ raise 'There are no remote nodes in the list and you are not running in --standalone mode;
205
+ the node won\'t connect to the network like that; try to do "zold remote reset" first'
206
+ end
200
207
  pid = nohup(opts)
201
208
  IO.write(opts['save-pid'], pid) if opts['save-pid']
202
209
  @log.debug("Process ID #{pid} saved into \"#{opts['save-pid']}\"")
@@ -323,7 +323,7 @@ Available options:"
323
323
  rescue JsonPage::CantParse, Score::CantParse, RemoteNode::CantAssert => e
324
324
  attempt += 1
325
325
  if attempt < opts['retry']
326
- @log.error("#{r} failed to read #{id}, trying again (attempt no.#{attempt}): #{e.message}")
326
+ @log.error("#{r} failed to read, trying again (attempt no.#{attempt}): #{e.message}")
327
327
  retry
328
328
  end
329
329
  raise e
data/lib/zold/head.rb CHANGED
@@ -44,7 +44,7 @@ module Zold
44
44
  raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
45
45
  lines = []
46
46
  File.open(@file) do |f|
47
- lines << f.readline while lines.count < 4 && !f.eof?
47
+ lines << f.readline.strip while lines.count < 4 && !f.eof?
48
48
  end
49
49
  raise CantParse, "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
50
50
  lines
data/lib/zold/key.rb CHANGED
@@ -54,6 +54,10 @@ module Zold
54
54
  # Public key of the root wallet
55
55
  ROOT = Key.new(file: File.expand_path(File.join(File.dirname(__FILE__), '../../resources/root.pub')))
56
56
 
57
+ def root?
58
+ to_s == ROOT.to_s
59
+ end
60
+
57
61
  def ==(other)
58
62
  to_s == other.to_s
59
63
  end
@@ -246,6 +246,7 @@ this is not a normal behavior, you may want to report a bug to our GitHub reposi
246
246
  id: wallet.id.to_s,
247
247
  score: score.to_h,
248
248
  mtime: wallet.mtime.utc.iso8601,
249
+ age: wallet.age.to_s,
249
250
  size: wallet.size,
250
251
  digest: wallet.digest,
251
252
  copies: Copies.new(File.join(settings.copies, wallet.id)).all.count,
@@ -454,7 +455,7 @@ time to stop; use --skip-oom to never quit")
454
455
  settings.zache.get(:total_mem, lifetime: settings.opts['no-cache'] ? 0 : 60) do
455
456
  Total::Mem.new.bytes
456
457
  rescue Total::CantDetect => e
457
- @log.error(e.message)
458
+ settings.log.error(e.message)
458
459
  0
459
460
  end
460
461
  end
@@ -56,7 +56,7 @@ module Zold
56
56
  raise 'pub must be of type Key' unless pub.is_a?(Key)
57
57
  raise 'id must be of type Id' unless id.is_a?(Id)
58
58
  raise 'txn must be of type Txn' unless txn.is_a?(Txn)
59
- pub.verify(txn.sign, body(id, txn)) && (@network != Wallet::MAINET || id != Id::ROOT || pub == Key::ROOT)
59
+ pub.verify(txn.sign, body(id, txn)) && (@network != Wallet::MAINET || !id.root? || pub.root?)
60
60
  end
61
61
 
62
62
  private
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.22.3'
28
+ VERSION = '0.22.4'
29
29
  PROTOCOL = 2
30
30
  REPO = 'zold-io/zold'
31
31
  end
data/lib/zold/wallet.rb CHANGED
@@ -54,6 +54,9 @@ module Zold
54
54
  # The extension of the wallet files
55
55
  EXT = '.z'
56
56
 
57
+ # The constructor of the wallet, from the file. The file may be
58
+ # absent at the time of creating the object. Later, don't forget to
59
+ # call init() in order to initialize the wallet, if it's absent.
57
60
  def initialize(file)
58
61
  unless file.end_with?(Wallet::EXT, Copies::EXT)
59
62
  raise "Wallet file must end with #{Wallet::EXT} or #{Copies::EXT}: #{file}"
@@ -71,55 +74,67 @@ module Zold
71
74
  id.to_s
72
75
  end
73
76
 
77
+ # Returns a convenient printable mnemo code of the wallet (mostly
78
+ # useful for logs).
74
79
  def mnemo
75
80
  "#{id}/#{balance.to_zld(4)}/#{txns.count}t/#{digest[0, 6]}/#{Size.new(size)}"
76
81
  end
77
82
 
83
+ # Convert the content of the wallet to the text.
78
84
  def to_text
79
85
  (@head.fetch + [''] + @txns.fetch.map(&:to_text)).join("\n")
80
86
  end
81
87
 
88
+ # Returns the network ID of the wallet.
82
89
  def network
83
- n = @head.fetch[0].strip
90
+ n = @head.fetch[0]
84
91
  raise "Invalid network name '#{n}'" unless n =~ /^[a-z]{4,16}$/
85
92
  n
86
93
  end
87
94
 
95
+ # Returns the protocol ID of the wallet file.
88
96
  def protocol
89
- v = @head.fetch[1].strip
97
+ v = @head.fetch[1]
90
98
  raise "Invalid protocol version name '#{v}'" unless v =~ /^[0-9]+$/
91
99
  v.to_i
92
100
  end
93
101
 
102
+ # Returns TRUE if the wallet file exists.
94
103
  def exists?
95
- File.exist?(@file)
104
+ File.exist?(path)
96
105
  end
97
106
 
107
+ # Returns the absolute path of the wallet file (it may be absent).
98
108
  def path
99
109
  @file
100
110
  end
101
111
 
112
+ # Creates an empty wallet with the specified ID and public key.
102
113
  def init(id, pubkey, overwrite: false, network: 'test')
103
- raise "File '#{@file}' already exists" if File.exist?(@file) && !overwrite
114
+ raise "File '#{path}' already exists" if File.exist?(path) && !overwrite
104
115
  raise "Invalid network name '#{network}'" unless network =~ /^[a-z]{4,16}$/
105
- FileUtils.mkdir_p(File.dirname(@file))
106
- IO.write(@file, "#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
116
+ FileUtils.mkdir_p(File.dirname(path))
117
+ IO.write(path, "#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
107
118
  @txns.flush
108
119
  @head.flush
109
120
  end
110
121
 
122
+ # Returns TRUE if it's a root wallet.
111
123
  def root?
112
124
  id == Id::ROOT
113
125
  end
114
126
 
127
+ # Returns the wallet ID.
115
128
  def id
116
- Id.new(@head.fetch[2].strip)
129
+ Id.new(@head.fetch[2])
117
130
  end
118
131
 
132
+ # Returns current wallet balance.
119
133
  def balance
120
134
  txns.inject(Amount::ZERO) { |sum, t| sum + t.amount }
121
135
  end
122
136
 
137
+ # Add a payment transaction to the wallet.
123
138
  def sub(amount, invoice, pvt, details = '-', time: Time.now)
124
139
  raise 'The amount has to be of type Amount' unless amount.is_a?(Amount)
125
140
  raise "The amount can't be negative: #{amount}" if amount.negative?
@@ -141,6 +156,7 @@ module Zold
141
156
  txn
142
157
  end
143
158
 
159
+ # Add a transaction to the wallet.
144
160
  def add(txn)
145
161
  raise 'The txn has to be of type Txn' unless txn.is_a?(Txn)
146
162
  raise "Wallet #{id} can't pay itself: #{txn}" if txn.bnf == id
@@ -152,66 +168,75 @@ module Zold
152
168
  raise "Positive transaction with the same ID #{txn.id} and BNF #{txn.bnf} already exists in #{id}"
153
169
  end
154
170
  raise "The tax payment already exists in #{id}: #{txn}" if Tax.new(self).exists?(txn.details)
155
- File.open(@file, 'a') { |f| f.print "#{txn}\n" }
171
+ File.open(path, 'a') { |f| f.print "#{txn}\n" }
156
172
  @txns.flush
157
173
  end
158
174
 
175
+ # Returns TRUE if the wallet contains a payment sent with the specified
176
+ # ID, which was sent to the specified beneficiary.
159
177
  def includes_negative?(id, bnf = nil)
160
178
  raise 'The txn ID has to be of type Integer' unless id.is_a?(Integer)
161
179
  !txns.find { |t| t.id == id && (bnf.nil? || t.bnf == bnf) && t.amount.negative? }.nil?
162
180
  end
163
181
 
182
+ # Returns TRUE if the wallet contains a payment received with the specified
183
+ # ID, which was sent by the specified beneficiary.
164
184
  def includes_positive?(id, bnf)
165
185
  raise 'The txn ID has to be of type Integer' unless id.is_a?(Integer)
166
186
  raise 'The bnf has to be of type Id' unless bnf.is_a?(Id)
167
187
  !txns.find { |t| t.id == id && t.bnf == bnf && !t.amount.negative? }.nil?
168
188
  end
169
189
 
190
+ # Returns TRUE if the public key of the wallet includes this payment
191
+ # prefix of the invoice.
170
192
  def prefix?(prefix)
171
193
  key.to_pub.include?(prefix)
172
194
  end
173
195
 
196
+ # Returns the public key of the wallet.
174
197
  def key
175
- Key.new(text: @head.fetch[3].strip)
176
- end
177
-
178
- def income
179
- txns.each do |t|
180
- yield t unless t.amount.negative?
181
- end
198
+ Key.new(text: @head.fetch[3])
182
199
  end
183
200
 
201
+ # Returns the time of when the wallet file was recently modified.
184
202
  def mtime
185
- File.mtime(@file)
203
+ File.mtime(path)
186
204
  end
187
205
 
206
+ # Returns a pseudo-unique hexadecimal digest of the wallet content.
188
207
  def digest
189
- OpenSSL::Digest::SHA256.file(@file).hexdigest
208
+ OpenSSL::Digest::SHA256.file(path).hexdigest
190
209
  end
191
210
 
192
- # Age of wallet in hours
211
+ # Age of wallet in hours.
193
212
  def age
194
213
  list = txns
195
214
  list.empty? ? 0 : (Time.now - list.min_by(&:date).date) / (60 * 60)
196
215
  end
197
216
 
198
- # Size of the wallet file in bytes
217
+ # Size of the wallet file in bytes. If the file doesn't exist
218
+ # an exception will be raised.
199
219
  def size
220
+ raise "The wallet file #{path} doesn't exist" unless File.exist?(path)
200
221
  File.size(path)
201
222
  end
202
223
 
224
+ # Retrieve the total list of all transactions.
203
225
  def txns
204
226
  @txns.fetch
205
227
  end
206
228
 
229
+ # Resaves the content of the wallet to the disc in the right format. All
230
+ # unnecessary space and EOL-s are removed. This operation is required
231
+ # in order to make sure two wallets with the same content are identical,
232
+ # no matter whether they were formatted differently.
207
233
  def refurbish
208
- IO.write(
209
- @file,
210
- "#{network}\n#{protocol}\n#{id}\n#{key.to_pub}\n\n#{txns.map { |t| t.to_s + "\n" }.join}"
211
- )
234
+ IO.write(path, (@head.fetch + [''] + @txns.fetch.map(&:to_s)).join("\n") + "\n")
212
235
  @txns.flush
213
236
  end
214
237
 
238
+ # Flush the in-memory cache and force the object to load all data from
239
+ # the disc again.
215
240
  def flush
216
241
  @head.flush
217
242
  @txns.flush
@@ -219,6 +244,9 @@ module Zold
219
244
 
220
245
  private
221
246
 
247
+ # Calculate the maximum transaction ID visible currently in the wallet.
248
+ # We go through them all and find the largest number. If there are
249
+ # no transactions, zero is returned.
222
250
  def max
223
251
  negative = txns.select { |t| t.amount.negative? }
224
252
  negative.empty? ? 0 : negative.max_by(&:id).id
data/test/fake_home.rb CHANGED
@@ -45,7 +45,9 @@ class FakeHome
45
45
  def run
46
46
  Dir.mktmpdir do |dir|
47
47
  FileUtils.copy(File.expand_path(File.join(__dir__, '../fixtures/id_rsa')), File.join(dir, 'id_rsa'))
48
- yield FakeHome.new(dir, log: @log)
48
+ result = yield FakeHome.new(dir, log: @log)
49
+ sleep 0.1 # It's a workaround against a bug (without it tests fail sporadically)
50
+ result
49
51
  end
50
52
  end
51
53
 
data/test/test__helper.rb CHANGED
@@ -52,7 +52,7 @@ module Zold
52
52
  # to wait can be increased, but try to make it as little as possible,
53
53
  # in order to catch problems ealier.
54
54
  def around
55
- Timeout.timeout(120) do
55
+ Timeout.timeout(180) do
56
56
  Thread.current.name = 'test'
57
57
  super
58
58
  end
@@ -47,7 +47,7 @@ class TestHungryWallets < Zold::Test
47
47
  )
48
48
  wallets.acq(id) { |w| assert(!w.exists?) }
49
49
  pool.join(2)
50
- assert_requested(get, times: 2)
50
+ assert_requested(get, times: 1)
51
51
  end
52
52
  end
53
53
  end
data/test/test_wallet.rb CHANGED
@@ -257,8 +257,8 @@ class TestWallet < Zold::Test
257
257
  )
258
258
  )
259
259
  sum = Zold::Amount::ZERO
260
- wallet.income do |t|
261
- sum += t.amount
260
+ wallet.txns.each do |t|
261
+ sum += t.amount unless t.amount.negative?
262
262
  end
263
263
  assert(
264
264
  sum == Zold::Amount.new(zents: 235_965_503_242),
data/test/test_zold.rb CHANGED
@@ -57,7 +57,7 @@ class TestZold < Zold::Test
57
57
  assert_equal(0, code, "#{f}\n#{out.join}")
58
58
  end
59
59
  end
60
- sleep 0.2
60
+ sleep 1 # It's a workaround, I can't fix the bug (tests crash sporadically)
61
61
  end
62
62
  test_log.info("\n\n#{f} done in #{Zold::Age.new(start)}")
63
63
  end
data/zold.gemspec CHANGED
@@ -77,7 +77,7 @@ and suggests a different architecture for digital wallet maintenance.'
77
77
  s.add_runtime_dependency 'sys-proctable', '1.2.1'
78
78
  s.add_runtime_dependency 'thin', '1.7.2'
79
79
  s.add_runtime_dependency 'threads', '>=0.3'
80
- s.add_runtime_dependency 'total', '>=0.2'
80
+ s.add_runtime_dependency 'total', '>=0.3'
81
81
  s.add_runtime_dependency 'typhoeus', '1.3.1'
82
82
  s.add_runtime_dependency 'usagewatch_ext', '0.2.1'
83
83
  s.add_runtime_dependency 'zache', '>=0.7'
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.22.3
4
+ version: 0.22.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-19 00:00:00.000000000 Z
11
+ date: 2019-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -240,14 +240,14 @@ dependencies:
240
240
  requirements:
241
241
  - - ">="
242
242
  - !ruby/object:Gem::Version
243
- version: '0.2'
243
+ version: '0.3'
244
244
  type: :runtime
245
245
  prerelease: false
246
246
  version_requirements: !ruby/object:Gem::Requirement
247
247
  requirements:
248
248
  - - ">="
249
249
  - !ruby/object:Gem::Version
250
- version: '0.2'
250
+ version: '0.3'
251
251
  - !ruby/object:Gem::Dependency
252
252
  name: typhoeus
253
253
  requirement: !ruby/object:Gem::Requirement
@@ -545,6 +545,7 @@ files:
545
545
  - ".rultor.yml"
546
546
  - ".simplecov"
547
547
  - ".travis.yml"
548
+ - Dockerfile
548
549
  - Gemfile
549
550
  - Guardfile
550
551
  - INSTALL.md
@@ -746,7 +747,7 @@ licenses:
746
747
  - MIT
747
748
  metadata: {}
748
749
  post_install_message: |-
749
- Thanks for installing Zold 0.22.3!
750
+ Thanks for installing Zold 0.22.4!
750
751
  Study our White Paper: https://papers.zold.io/wp.pdf
751
752
  Read our blog posts: https://blog.zold.io
752
753
  Try ZLD online wallet at: https://wts.zold.io