zold 0.13.34 → 0.13.35

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/README.md +6 -5
  4. data/bin/zold +6 -1
  5. data/fixtures/scripts/_head.sh +4 -2
  6. data/fixtures/scripts/push-and-pull.sh +1 -1
  7. data/lib/zold/amount.rb +5 -0
  8. data/lib/zold/atomic_file.rb +3 -3
  9. data/lib/zold/backtrace.rb +7 -1
  10. data/lib/zold/commands/clean.rb +1 -1
  11. data/lib/zold/commands/create.rb +2 -2
  12. data/lib/zold/commands/diff.rb +2 -2
  13. data/lib/zold/commands/fetch.rb +8 -4
  14. data/lib/zold/commands/merge.rb +3 -3
  15. data/lib/zold/commands/node.rb +32 -11
  16. data/lib/zold/commands/propagate.rb +1 -1
  17. data/lib/zold/commands/push.rb +14 -9
  18. data/lib/zold/commands/remote.rb +4 -2
  19. data/lib/zold/commands/routines/bonuses.rb +9 -5
  20. data/lib/zold/commands/routines/spread.rb +4 -1
  21. data/lib/zold/commands/show.rb +2 -2
  22. data/lib/zold/commands/taxes.rb +2 -1
  23. data/lib/zold/http.rb +9 -1
  24. data/lib/zold/id.rb +4 -0
  25. data/lib/zold/json_page.rb +43 -0
  26. data/lib/zold/metronome.rb +15 -15
  27. data/lib/zold/node/async_entrance.rb +81 -0
  28. data/lib/zold/node/entrance.rb +26 -91
  29. data/lib/zold/node/farm.rb +21 -15
  30. data/lib/zold/node/front.rb +30 -17
  31. data/lib/zold/node/safe_entrance.rb +78 -0
  32. data/lib/zold/node/spread_entrance.rb +105 -0
  33. data/lib/zold/patch.rb +7 -5
  34. data/lib/zold/remotes.rb +19 -7
  35. data/lib/zold/txn.rb +3 -1
  36. data/lib/zold/version.rb +1 -1
  37. data/lib/zold/wallet.rb +3 -3
  38. data/lib/zold/wallets.rb +2 -0
  39. data/test/commands/routines/test_bonuses.rb +2 -2
  40. data/test/commands/test_fetch.rb +9 -35
  41. data/test/commands/test_merge.rb +1 -1
  42. data/test/commands/test_node.rb +4 -2
  43. data/test/commands/test_push.rb +4 -2
  44. data/test/node/fake_entrance.rb +41 -0
  45. data/test/node/fake_node.rb +37 -29
  46. data/test/node/test_async_entrance.rb +88 -0
  47. data/test/node/test_entrance.rb +1 -19
  48. data/test/node/test_front.rb +3 -2
  49. data/test/node/test_safe_entrance.rb +56 -0
  50. data/test/node/test_spread_entrance.rb +60 -0
  51. data/test/test__helper.rb +0 -1
  52. data/test/test_atomic_file.rb +39 -0
  53. data/test/test_backtrace.rb +40 -0
  54. data/test/test_metronome.rb +0 -1
  55. data/test/test_patch.rb +17 -0
  56. data/test/test_remotes.rb +21 -0
  57. data/test/test_wallet.rb +14 -1
  58. data/test/test_zold.rb +1 -0
  59. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b72e9cbc1d475ad37e79d6f26dd0826702162b0a
4
- data.tar.gz: 1a423d084caf176481696a30e800c30910681775
3
+ metadata.gz: d72a17dead59c9ae473afc46355b7189dccde252
4
+ data.tar.gz: cd9e2182faaae9ef62448e099bbe836246832fef
5
5
  SHA512:
6
- metadata.gz: 017c6255d34f34f137acfb403a8e9123aba9442579b0824d50c63ab45f4aa739e120a629d1f39c72386e644c0b3861476a013acd851738e62f18a26c3c77f435
7
- data.tar.gz: 8dd19ab2329b2c78abbe39ffb9f06e8d2cc0d37b2b675887627970368082137f2cdd9438dbfa3707321ad593d54688796fbf86d95b6ab223484f825eb7ac40bd
6
+ metadata.gz: c2e3e6e3d3d34fc1f0442397efbf4fceb6b269d8d7b4099cf46a70c130beb4f35fe53d2397c63d4032576d1fd10d3e448835e808a7b79149c1d364de06fc8b24
7
+ data.tar.gz: 2b8283cb8e7d7661cf002f24b6430dae1c5c1ec1ab522c02f1ef650aef425dfe24383f9fc7c80132d676ba9510154698bd6b82040b8eac92d5104906524c255f
data/.rubocop.yml CHANGED
@@ -16,7 +16,7 @@ Metrics/AbcSize:
16
16
  Metrics/BlockLength:
17
17
  Max: 100
18
18
  Metrics/ClassLength:
19
- Max: 200
19
+ Max: 250
20
20
  Layout/EndOfLine:
21
21
  EnforcedStyle: lf
22
22
  Metrics/ParameterLists:
data/README.md CHANGED
@@ -87,6 +87,8 @@ $ zold push 5f96e731e48ae21f
87
87
 
88
88
  That's it.
89
89
 
90
+ ## How to Start a Node
91
+
90
92
  You also can contribute to Zold by running a node on your server.
91
93
  In order to do that just run (with your own wallet ID, of course):
92
94
 
@@ -145,6 +147,10 @@ with a lot of technical details. Here is the explanation of the majority of them
145
147
  The node is supposed to update update itself automatically (if you run it via `zold-nohup`)
146
148
  every time it discovers another node with a higher version.
147
149
 
150
+ `network` is the name of the network the node belongs to.
151
+ The production network's name is `zold`.
152
+ For testing purposes you can start a node in a test network, using `--network=test`.
153
+
148
154
  `score` is the current score your node is exposing to the network now.
149
155
  All other nodes are using this information in order to decide how much
150
156
  they can trust your node with the information it provides, about wallets.
@@ -207,11 +213,6 @@ network. You can see the full list of nodes at `/remotes` URL of your node.
207
213
  further. The health of this point is critical to the entire node. Some
208
214
  numbers it includes must be watched carefully.
209
215
 
210
- * `semaphores` is the amount of locks the server maintain, one per wallet.
211
- The number may be large (>100), if the node has processed a lot of wallets
212
- recently. If it's larger [than 1024](https://github.com/zold-io/zold/issues/199),
213
- it's a good reason to worry.
214
-
215
216
  To be continued...
216
217
 
217
218
  `date` is the current date and time on the server.
data/bin/zold CHANGED
@@ -97,6 +97,10 @@ Available options:"
97
97
  o.string '--home',
98
98
  "Home directory (default: #{Dir.pwd})",
99
99
  default: Dir.pwd
100
+ o.string '--network',
101
+ "The name of the network we work in (default: #{Zold::Wallet::MAIN_NETWORK}",
102
+ required: true,
103
+ default: Zold::Wallet::MAIN_NETWORK
100
104
  o.bool '-h', '--help', 'Show these instructions'
101
105
  o.bool '--trace', 'Show full stack trace in case of a problem'
102
106
  o.bool '--ignore-global-config', 'Don\'t read options from the ~/.zold file'
@@ -123,12 +127,13 @@ Available options:"
123
127
 
124
128
  args = opts.arguments
125
129
  args << '--help' if opts.help?
130
+ args << "--network=#{opts['network']}"
126
131
 
127
132
  FileUtils.mkdir_p(opts[:home])
128
133
  Dir.chdir(opts[:home])
129
134
 
130
135
  wallets = Zold::Wallets.new('.')
131
- remotes = Zold::Remotes.new('./.zoldata/remotes')
136
+ remotes = Zold::Remotes.new('./.zoldata/remotes', network: opts['network'])
132
137
  copies = './.zoldata/copies'
133
138
 
134
139
  case command
@@ -12,19 +12,21 @@ function reserve_port {
12
12
  function wait_for_port {
13
13
  while ! nc -z localhost $1; do
14
14
  ((p++)) || sleep 1
15
- if ((p==10)); then
15
+ if ((p==30)); then
16
16
  echo Port $1 is not available after $p seconds of waiting
17
17
  exit -1
18
18
  fi
19
+ sleep 1
19
20
  done
20
21
  }
21
22
 
22
23
  function wait_for_file {
23
24
  while [ ! -f $1 ]; do
24
25
  ((c++)) || sleep 1
25
- if ((c==10)); then
26
+ if ((c==30)); then
26
27
  echo File $1 not found, giving up after $c seconds of waiting
27
28
  exit -1
28
29
  fi
30
+ sleep 1
29
31
  done
30
32
  }
@@ -28,7 +28,7 @@ zold show 0000000000000000
28
28
  zold taxes debt 0000000000000000
29
29
 
30
30
  zold remote show
31
- zold push 0000000000000000 --sync
31
+ zold push 0000000000000000
32
32
  zold fetch 0000000000000000 --ignore-score-weakness
33
33
  zold diff 0000000000000000
34
34
  zold merge 0000000000000000
data/lib/zold/amount.rb CHANGED
@@ -86,6 +86,11 @@ module Zold
86
86
  @coins <= other.to_i
87
87
  end
88
88
 
89
+ def <=>(other)
90
+ raise '<= may only work with Amount' unless other.is_a?(Amount)
91
+ @coins <=> other.to_i
92
+ end
93
+
89
94
  def +(other)
90
95
  raise '+ may only work with Amount' unless other.is_a?(Amount)
91
96
  Amount.new(coins: @coins + other.to_i)
@@ -31,15 +31,15 @@ module Zold
31
31
  end
32
32
 
33
33
  def read
34
- File.open(@file, 'r') do |f|
35
- f.flock(File::LOCK_SH)
34
+ File.open(@file, 'rb') do |f|
35
+ f.flock(File::LOCK_EX)
36
36
  f.read
37
37
  end
38
38
  end
39
39
 
40
40
  def write(content)
41
41
  raise 'Content can\'t be nil' if content.nil?
42
- File.open(@file, 'w+') do |f|
42
+ File.open(@file, 'wb') do |f|
43
43
  f.flock(File::LOCK_EX)
44
44
  f.write(content)
45
45
  end
@@ -30,7 +30,13 @@ module Zold
30
30
  end
31
31
 
32
32
  def to_s
33
- "#{@error.class.name}: #{@error.message}\n#{@error.backtrace.join("\n\t")}"
33
+ [
34
+ @error.class.name,
35
+ ': ',
36
+ @error.message,
37
+ "\n\t",
38
+ @error.backtrace.select { |t| t.include?('zold/') }.join("\n\t")
39
+ ].join
34
40
  end
35
41
  end
36
42
  end
@@ -50,7 +50,7 @@ Available options:"
50
50
  end
51
51
  mine = Args.new(opts, @log).take || return
52
52
  mine = @wallets.all if mine.empty?
53
- mine.each do |id|
53
+ mine.map { |i| Id.new(i) }.each do |id|
54
54
  clean(Copies.new(File.join(@copies, id)), opts)
55
55
  end
56
56
  end
@@ -46,9 +46,9 @@ Available options:"
46
46
  require: true,
47
47
  default: '~/.ssh/id_rsa.pub'
48
48
  o.string '--network',
49
- 'The name of the network',
49
+ "The name of the network (default: #{Wallet::MAIN_NETWORK}",
50
50
  require: true,
51
- default: 'zold'
51
+ default: Wallet::MAIN_NETWORK
52
52
  o.bool '--help', 'Print instructions'
53
53
  end
54
54
  mine = Args.new(opts, @log).take || return
@@ -50,9 +50,9 @@ Available options:"
50
50
  mine = Args.new(opts, @log).take || return
51
51
  raise 'At least one wallet ID is required' if mine.empty?
52
52
  stdout = ''
53
- mine.each do |id|
53
+ mine.map { |i| Id.new(i) }.each do |id|
54
54
  stdout += diff(
55
- @wallets.find(Id.new(id)),
55
+ @wallets.find(id),
56
56
  Copies.new(File.join(@copies, id)),
57
57
  opts
58
58
  )
@@ -28,6 +28,7 @@ require_relative 'args'
28
28
  require_relative '../log'
29
29
  require_relative '../http'
30
30
  require_relative '../score'
31
+ require_relative '../json_page'
31
32
  require_relative '../copies'
32
33
 
33
34
  # FETCH command.
@@ -58,7 +59,7 @@ Available options:"
58
59
  end
59
60
  mine = Args.new(opts, @log).take || return
60
61
  mine = @wallets.all if mine.empty?
61
- mine.each do |id|
62
+ mine.map { |i| Id.new(i) }.each do |id|
62
63
  fetch(id, Copies.new(File.join(@copies, id)), opts)
63
64
  end
64
65
  end
@@ -68,10 +69,14 @@ Available options:"
68
69
  def fetch(id, cps, opts)
69
70
  total = 0
70
71
  nodes = 0
72
+ done = 0
71
73
  @remotes.iterate(@log) do |r|
72
- total += fetch_one(id, r, cps, opts)
73
74
  nodes += 1
75
+ total += fetch_one(id, r, cps, opts)
76
+ done += 1
74
77
  end
78
+ raise "There are no remote nodes, run 'zold remote reset'" if nodes.zero?
79
+ raise "No nodes out of #{nodes} have the wallet #{id}" if done.zero?
75
80
  @log.debug("#{nodes} copies of #{id} fetched for the total score of #{total}, #{cps.all.count} local copies")
76
81
  end
77
82
 
@@ -84,11 +89,10 @@ Available options:"
84
89
  res = r.http("/wallet/#{id}").get
85
90
  raise "Wallet #{id} not found" if res.code == '404'
86
91
  r.assert_code(200, res)
87
- json = JSON.parse(res.body)
92
+ json = JsonPage.new(res.body).to_hash
88
93
  score = Score.parse_json(json['score'])
89
94
  r.assert_valid_score(score)
90
95
  r.assert_score_ownership(score)
91
- r.assert_score_strength(score)
92
96
  r.assert_score_strength(score) unless opts['ignore-score-weakness']
93
97
  Tempfile.open do |f|
94
98
  body = json['body']
@@ -53,9 +53,9 @@ Available options:"
53
53
  mine = Args.new(opts, @log).take || return
54
54
  mine = @wallets.all if mine.empty?
55
55
  modified = []
56
- mine.each do |id|
57
- next unless merge(Id.new(id), Copies.new(File.join(@copies, id)), opts)
58
- modified << Id.new(id)
56
+ mine.map { |i| Id.new(i) }.each do |id|
57
+ next unless merge(id, Copies.new(File.join(@copies, id)), opts)
58
+ modified << id
59
59
  require_relative 'propagate'
60
60
  modified += Propagate.new(wallets: @wallets, log: @log).run(args)
61
61
  end
@@ -23,10 +23,14 @@ require_relative '../version'
23
23
  require_relative '../score'
24
24
  require_relative '../backtrace'
25
25
  require_relative '../metronome'
26
+ require_relative '../wallet'
26
27
  require_relative '../wallets'
27
28
  require_relative '../remotes'
28
29
  require_relative '../verbose_thread'
29
30
  require_relative '../node/entrance'
31
+ require_relative '../node/safe_entrance'
32
+ require_relative '../node/spread_entrance'
33
+ require_relative '../node/async_entrance'
30
34
  require_relative '../node/front'
31
35
  require_relative '../node/farm'
32
36
  require_relative 'pull'
@@ -104,6 +108,10 @@ module Zold
104
108
  o.string '--private-key',
105
109
  'The location of RSA private key (default: ~/.ssh/id_rsa)',
106
110
  default: '~/.ssh/id_rsa'
111
+ o.string '--network',
112
+ "The name of the network (default: #{Wallet::MAIN_NETWORK})",
113
+ require: true,
114
+ default: Wallet::MAIN_NETWORK
107
115
  o.bool '--help', 'Print instructions'
108
116
  end
109
117
  if opts.help?
@@ -125,6 +133,7 @@ module Zold
125
133
  @log.info("Home directory: #{Dir.pwd}")
126
134
  @log.info("Ruby version: #{RUBY_VERSION}")
127
135
  @log.info("Zold gem version: #{Zold::VERSION}")
136
+ @log.info("Network ID: #{opts['network']}")
128
137
  host = opts[:host] || ip
129
138
  address = "#{host}:#{opts[:port]}".downcase
130
139
  @log.info("Node location: #{address}")
@@ -138,12 +147,11 @@ module Zold
138
147
  @log.debug('Running in standalone mode! (will never talk to other remotes)')
139
148
  end
140
149
  Front.set(:ignore_score_weakness, opts['ignore-score-weakness'])
150
+ Front.set(:network, opts['network'])
141
151
  Front.set(:wallets, @wallets)
142
152
  Front.set(:remotes, @remotes)
143
153
  Front.set(:copies, @copies)
144
154
  Front.set(:address, address)
145
- entrance = Entrance.new(@wallets, @remotes, @copies, address, log: @log)
146
- Front.set(:entrance, entrance)
147
155
  Front.set(:root, Dir.pwd)
148
156
  Front.set(:port, opts['bind-port'])
149
157
  Front.set(:reboot, !opts['never-reboot'])
@@ -154,24 +162,37 @@ module Zold
154
162
  require_relative 'invoice'
155
163
  invoice = Invoice.new(wallets: @wallets, log: @log).run(['invoice', invoice])
156
164
  end
157
- farm = Farm.new(invoice, File.join(Dir.pwd, 'farm'), log: @log)
158
- farm.start(host, opts[:port], threads: opts[:threads], strength: opts[:strength]) do
159
- Front.set(:farm, farm)
160
- metronome(farm, entrance, opts).start do |metronome|
161
- Front.set(:metronome, metronome)
162
- @log.info("Starting up the web front at http://#{host}:#{opts[:port]}...")
163
- Front.run!
164
- @log.info("The web front stopped at http://#{host}:#{opts[:port]}")
165
+ SafeEntrance.new(
166
+ AsyncEntrance.new(
167
+ SpreadEntrance.new(
168
+ Entrance.new(@wallets, @remotes, @copies, address, log: @log),
169
+ @wallets, @remotes, address,
170
+ log: @log,
171
+ ignore_score_weakeness: opts['ignore-score-weakness']
172
+ ), log: @log
173
+ ), network: opts['network']
174
+ ).start do |entrance|
175
+ Front.set(:entrance, entrance)
176
+ Farm.new(invoice, File.join(Dir.pwd, 'farm'), log: @log)
177
+ .start(host, opts[:port], threads: opts[:threads], strength: opts[:strength]) do |farm|
178
+ Front.set(:farm, farm)
179
+ metronome(farm, entrance, opts).start do |metronome|
180
+ Front.set(:metronome, metronome)
181
+ @log.info("Starting up the web front at http://#{host}:#{opts[:port]}...")
182
+ Front.run!
183
+ @log.info("The web front stopped at http://#{host}:#{opts[:port]}")
184
+ end
165
185
  end
166
186
  end
187
+ @log.info("The node #{host}:#{opts[:port]} is shut down, thanks for helping Zold network!")
167
188
  end
168
189
 
169
190
  private
170
191
 
171
192
  # Returns exit code
172
193
  def exec(cmd, nohup_log)
194
+ start = Time.now
173
195
  Open3.popen2e(cmd) do |stdin, stdout, thr|
174
- start = Time.now
175
196
  nohup_log.print("Started process ##{thr.pid} from process ##{Process.pid}: #{cmd}\n")
176
197
  stdin.close
177
198
  until stdout.eof?
@@ -48,7 +48,7 @@ Available options:"
48
48
  mine = Args.new(opts, @log).take || return
49
49
  mine = @wallets.all if mine.empty?
50
50
  modified = []
51
- mine.each do |id|
51
+ mine.map { |i| Id.new(i) }.each do |id|
52
52
  modified += propagate(@wallets.find(id), opts)
53
53
  end
54
54
  modified
@@ -26,6 +26,7 @@ require_relative 'args'
26
26
  require_relative '../log'
27
27
  require_relative '../id'
28
28
  require_relative '../http'
29
+ require_relative '../json_page'
29
30
  require_relative '../atomic_file'
30
31
 
31
32
  # PUSH command.
@@ -45,18 +46,18 @@ module Zold
45
46
  opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
46
47
  o.banner = "Usage: zold push [ID...] [options]
47
48
  Available options:"
49
+ o.bool '--ignore-score-weakness',
50
+ 'Don\'t complain when their score is too weak',
51
+ default: false
48
52
  o.array '--ignore-node',
49
53
  'Ignore this node and don\'t push to it',
50
54
  default: []
51
- o.bool '--sync',
52
- 'Wait until the server confirms merge and pushes all wallets further (default: false)',
53
- default: false
54
55
  o.bool '--help', 'Print instructions'
55
56
  end
56
57
  mine = Args.new(opts, @log).take || return
57
58
  mine = @wallets.all if mine.empty?
58
- mine.each do |id|
59
- wallet = @wallets.find(Id.new(id))
59
+ mine.map { |i| Id.new(i) }.each do |id|
60
+ wallet = @wallets.find(id)
60
61
  raise "The wallet #{id} is absent" unless wallet.exists?
61
62
  push(wallet, opts)
62
63
  end
@@ -67,11 +68,15 @@ Available options:"
67
68
  def push(wallet, opts)
68
69
  total = 0
69
70
  nodes = 0
71
+ done = 0
70
72
  @remotes.iterate(@log) do |r|
71
- total += push_one(wallet, r, opts)
72
73
  nodes += 1
74
+ total += push_one(wallet, r, opts)
75
+ done += 1
73
76
  end
74
- @log.info("Push finished to #{nodes} nodes, total score for #{wallet.id} is #{total}")
77
+ raise "There are no remote nodes, run 'zold remote reset'" if nodes.zero?
78
+ raise "No nodes out of #{nodes} accepted the wallet #{wallet.id}" if done.zero?
79
+ @log.info("Push finished to #{done} nodes out of #{nodes}, total score for #{wallet.id} is #{total}")
75
80
  end
76
81
 
77
82
  def push_one(wallet, r, opts)
@@ -87,11 +92,11 @@ Available options:"
87
92
  return 0
88
93
  end
89
94
  r.assert_code(200, response)
90
- json = JSON.parse(response.body)
95
+ json = JsonPage.new(response.body).to_hash
91
96
  score = Score.parse_json(json['score'])
92
97
  r.assert_valid_score(score)
93
98
  r.assert_score_ownership(score)
94
- r.assert_score_strength(score)
99
+ r.assert_score_strength(score) unless opts['ignore-score-weakness']
95
100
  @log.info("#{r} accepted #{content.length}b/#{wallet.txns.count}t of #{wallet.id} \
96
101
  in #{(Time.now - start).round(2)}s: #{Rainbow(score.value).green} (#{json['version']})")
97
102
  score.value
@@ -27,6 +27,7 @@ require 'time'
27
27
  require_relative 'args'
28
28
  require_relative '../node/farm'
29
29
  require_relative '../log'
30
+ require_relative '../json_page'
30
31
  require_relative '../http'
31
32
  require_relative '../remotes'
32
33
  require_relative '../score'
@@ -152,7 +153,8 @@ Available options:"
152
153
  @remotes.iterate(@log, farm: @farm) do |r|
153
154
  res = r.http('/').get
154
155
  r.assert_code(200, res)
155
- score = Score.parse_json(JSON.parse(res.body)['score'])
156
+ json = JsonPage.new(res.body).to_hash
157
+ score = Score.parse_json(json['score'])
156
158
  r.assert_valid_score(score)
157
159
  r.assert_score_ownership(score)
158
160
  r.assert_score_strength(score) unless opts['ignore-score-weakness']
@@ -181,7 +183,7 @@ Available options:"
181
183
  start = Time.now
182
184
  res = r.http('/remotes').get
183
185
  r.assert_code(200, res)
184
- json = JSON.parse(res.body)
186
+ json = JsonPage.new(res.body).to_hash
185
187
  score = Score.parse_json(json['score'])
186
188
  r.assert_valid_score(score)
187
189
  r.assert_score_ownership(score)