zold 0.22.3 → 0.22.4

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