zold 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -2
  3. data/bin/zold +34 -48
  4. data/features/cli.feature +1 -1
  5. data/features/step_definitions/steps.rb +1 -3
  6. data/features/support/env.rb +2 -0
  7. data/fixtures/scripts/push-and-pull.sh +6 -4
  8. data/lib/zold/amount.rb +17 -3
  9. data/lib/zold/commands/create.rb +9 -6
  10. data/lib/zold/commands/fetch.rb +11 -21
  11. data/lib/zold/commands/node.rb +7 -9
  12. data/lib/zold/commands/pay.rb +9 -6
  13. data/lib/zold/commands/propagate.rb +4 -5
  14. data/lib/zold/commands/push.rb +10 -5
  15. data/lib/zold/commands/remote.rb +22 -24
  16. data/lib/zold/commands/show.rb +1 -2
  17. data/lib/zold/commands/taxes.rb +154 -0
  18. data/lib/zold/http.rb +1 -3
  19. data/lib/zold/key.rb +1 -3
  20. data/lib/zold/node/entrance.rb +8 -3
  21. data/lib/zold/node/farm.rb +8 -6
  22. data/lib/zold/node/front.rb +0 -1
  23. data/lib/zold/patch.rb +1 -1
  24. data/lib/zold/remotes.rb +4 -0
  25. data/lib/zold/score.rb +85 -10
  26. data/lib/zold/signature.rb +7 -7
  27. data/lib/zold/tax.rb +79 -0
  28. data/lib/zold/txn.rb +12 -7
  29. data/lib/zold/version.rb +1 -1
  30. data/lib/zold/wallet.rb +2 -2
  31. data/test/commands/test_create.rb +3 -4
  32. data/test/commands/test_diff.rb +2 -3
  33. data/test/commands/test_merge.rb +4 -6
  34. data/test/commands/test_pay.rb +7 -5
  35. data/test/commands/test_remote.rb +5 -3
  36. data/test/commands/test_taxes.rb +66 -0
  37. data/test/node/fake_node.rb +1 -0
  38. data/test/node/test_farm.rb +2 -1
  39. data/test/node/test_front.rb +1 -0
  40. data/test/test__helper.rb +2 -0
  41. data/test/test_remotes.rb +0 -1
  42. data/test/test_score.rb +40 -21
  43. data/test/test_signature.rb +6 -3
  44. data/test/test_tax.rb +53 -0
  45. data/test/test_txn.rb +46 -0
  46. data/test/test_wallet.rb +2 -2
  47. data/test/test_zold.rb +1 -1
  48. data/wp/.gitignore +6 -0
  49. data/wp/wp.tex +38 -0
  50. data/zold.gemspec +1 -3
  51. metadata +12 -2
data/lib/zold/score.rb CHANGED
@@ -30,30 +30,93 @@ module Zold
30
30
  # Score
31
31
  class Score
32
32
  STRENGTH = 6
33
- attr_reader :time, :host, :port, :strength
33
+ attr_reader :time, :host, :port, :invoice, :strength
34
34
  # time: UTC ISO 8601 string
35
- def initialize(time, host, port, suffixes = [], strength: STRENGTH)
35
+ def initialize(time, host, port, invoice, suffixes = [], strength: STRENGTH)
36
+ raise "Invalid host name: #{host}" unless host =~ /^[a-z0-9\.-]+$/
36
37
  raise 'Time must be of type Time' unless time.is_a?(Time)
37
38
  raise 'Port must be of type Integer' unless port.is_a?(Integer)
39
+ raise "Invalid TCP port: #{port}" if port <= 0 || port > 65_535
40
+ raise "Invoice '#{invoice}' has wrong format" unless invoice =~ /^[a-zA-Z0-9]{8,32}@[a-f0-9]{16}$/
38
41
  @time = time
39
42
  @host = host
40
43
  @port = port
44
+ @invoice = invoice
41
45
  @suffixes = suffixes
42
46
  @strength = strength
43
47
  end
44
48
 
45
- ZERO = Score.new(Time.now, 'localhost', Remotes::PORT)
49
+ ZERO = Score.new(
50
+ Time.now, 'localhost',
51
+ Remotes::PORT, 'NOPREFIX@ffffffffffffffff'
52
+ )
53
+
54
+ def self.parse_json(json)
55
+ raise "Time in JSON is broken: #{json}" unless json['time'] =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
56
+ raise "Host is wrong: #{json}" unless json['host'] =~ /^[0-9a-z\.\-]+$/
57
+ raise "Port is wrong: #{json}" unless json['port'].is_a?(Integer)
58
+ raise "Invoice is wrong: #{json}" unless json['invoice'] =~ /^[a-zA-Z0-9]{8,32}@[a-f0-9]{16}$/
59
+ raise "Suffixes not array: #{json}" unless json['suffixes'].is_a?(Array)
60
+ Score.new(
61
+ Time.parse(json['time']), json['host'],
62
+ json['port'], json['invoice'], json['suffixes'],
63
+ strength: json['strength']
64
+ )
65
+ end
46
66
 
47
67
  def self.parse(text, strength: STRENGTH)
48
- _, time, host, port, suffixes = text.split(' ', 5)
68
+ m = Regexp.new(
69
+ '^' + [
70
+ '([0-9]+:)',
71
+ '(?<time>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z)',
72
+ '(?<host>[0-9a-z\.\-]+)',
73
+ '(?<port>[0-9]+)',
74
+ '(?<invoice>[a-zA-Z0-9]{8,32}@[a-f0-9]{16})',
75
+ '(?<suffixes>[a-zA-Z0-9 ]+)'
76
+ ].join(' ') + '$'
77
+ ).match(text)
78
+ raise "Invalid score '#{text}'" if m.nil?
79
+ Score.new(
80
+ Time.parse(m[:time]), m[:host],
81
+ m[:port].to_i, m[:invoice],
82
+ m[:suffixes].split(' '),
83
+ strength: strength
84
+ )
85
+ end
86
+
87
+ def self.parse_text(text, strength: STRENGTH)
88
+ parts = text.split(' ', 6)
49
89
  Score.new(
50
- Time.parse(time), host, port.to_i,
51
- suffixes.split(' '), strength: strength
90
+ Time.at(parts[0].hex),
91
+ parts[1],
92
+ parts[2].hex,
93
+ "#{parts[3]}@#{parts[4]}",
94
+ parts[5].split(' '),
95
+ strength: strength
52
96
  )
53
97
  end
54
98
 
99
+ def to_text
100
+ prefix, bnf = @invoice.split('@')
101
+ [
102
+ @time.to_i.to_s(16),
103
+ @host,
104
+ @port.to_s(16),
105
+ prefix,
106
+ bnf,
107
+ @suffixes.join(' ')
108
+ ].join(' ')
109
+ end
110
+
55
111
  def to_s
56
- "#{value}: #{@time.utc.iso8601} #{@host} #{@port} #{@suffixes.join(' ')}"
112
+ [
113
+ "#{value}:",
114
+ @time.utc.iso8601,
115
+ @host,
116
+ @port,
117
+ @invoice,
118
+ @suffixes.join(' ')
119
+ ].join(' ')
57
120
  end
58
121
 
59
122
  def to_h
@@ -61,6 +124,7 @@ module Zold
61
124
  value: value,
62
125
  host: @host,
63
126
  port: @port,
127
+ invoice: @invoice,
64
128
  time: @time.utc.iso8601,
65
129
  suffixes: @suffixes,
66
130
  strength: @strength
@@ -68,7 +132,10 @@ module Zold
68
132
  end
69
133
 
70
134
  def reduced(max = 4)
71
- Score.new(@time, @host, @port, @suffixes[0..max - 1], strength: @strength)
135
+ Score.new(
136
+ @time, @host, @port, @invoice,
137
+ @suffixes[0..max - 1], strength: @strength
138
+ )
72
139
  end
73
140
 
74
141
  def next
@@ -77,7 +144,7 @@ module Zold
77
144
  loop do
78
145
  suffix = idx.to_s(16)
79
146
  score = Score.new(
80
- @time, @host, @port, @suffixes + [suffix],
147
+ @time, @host, @port, @invoice, @suffixes + [suffix],
81
148
  strength: @strength
82
149
  )
83
150
  return score if score.valid?
@@ -85,8 +152,16 @@ module Zold
85
152
  end
86
153
  end
87
154
 
155
+ def age_hours
156
+ (Time.now - @time) / 60
157
+ end
158
+
159
+ def expired?
160
+ @time < Time.now - 24 * 60
161
+ end
162
+
88
163
  def valid?
89
- start = "#{@time.utc.iso8601} #{@host} #{@port}"
164
+ start = "#{@time.utc.iso8601} #{@host} #{@port} #{@invoice}"
90
165
  @suffixes.reduce(start) do |prefix, suffix|
91
166
  hex = Digest::SHA256.hexdigest(prefix + ' ' + suffix)
92
167
  return false unless hex.end_with?('0' * @strength)
@@ -18,7 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
19
  # SOFTWARE.
20
20
 
21
- require 'time'
21
+ require 'digest'
22
22
  require_relative 'key'
23
23
  require_relative 'id'
24
24
  require_relative 'amount'
@@ -31,18 +31,18 @@ require_relative 'amount'
31
31
  module Zold
32
32
  # A signature
33
33
  class Signature
34
- def sign(pvt, t)
35
- pvt.sign(block(t))
34
+ def sign(pvt, id, t)
35
+ pvt.sign(body(id, t))
36
36
  end
37
37
 
38
- def valid?(pub, t)
39
- pub.verify(t.sign, block(t))
38
+ def valid?(pub, id, t)
39
+ pub.verify(t.sign, body(id, t))
40
40
  end
41
41
 
42
42
  private
43
43
 
44
- def block(t)
45
- [t.id, t.amount.to_i, t.prefix, t.bnf, t.details].join(';')
44
+ def body(id, t)
45
+ [id, t.id, t.amount.to_i, t.prefix, t.bnf, t.details].join(' ')
46
46
  end
47
47
  end
48
48
  end
data/lib/zold/tax.rb ADDED
@@ -0,0 +1,79 @@
1
+ # Copyright (c) 2018 Yegor Bugayenko
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the 'Software'), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+
21
+ require 'digest'
22
+ require_relative 'key'
23
+ require_relative 'id'
24
+ require_relative 'amount'
25
+
26
+ # Tax transaction.
27
+ #
28
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
30
+ # License:: MIT
31
+ module Zold
32
+ # A single tax payment
33
+ class Tax
34
+ # The minimum score a wallet can buy in order to pay taxes.
35
+ MIN_SCORE = 16
36
+
37
+ # This is how much we charge per one transaction per hour
38
+ # of storage. A wallet of 4096 transactions will pay
39
+ # approximately 16ZLD per year.
40
+ FEE_TXN_HOUR = Amount.new(zld: 16.0 / (365 * 24) / 4096)
41
+
42
+ # The maximum debt we can tolerate at the wallet. If the debt
43
+ # is bigger than this threshold, nodes must stop accepting PUSH.
44
+ TRIAL = Amount.new(zld: 1.0)
45
+
46
+ # For how many days to pay at once.
47
+ DAYS_INCREMENT = 32
48
+
49
+ def initialize(wallet)
50
+ @wallet = wallet
51
+ end
52
+
53
+ def pay(pvt, best)
54
+ fee = Tax::FEE * @wallet.txns.count * Tax::DAYS_INCREMENT * 24
55
+ @wallet.sub(fee, best.invoice, pvt, "TAXES #{best.to_text}")
56
+ end
57
+
58
+ def debt
59
+ txns = @wallet.txns
60
+ return Amount::ZERO if txns.empty?
61
+ paid = txns.map do |t|
62
+ pfx, body = t.details.split(' ')
63
+ if pfx != 'TAXES' || body.nil?
64
+ Amount::ZERO
65
+ else
66
+ score = Score.parse_text(body)
67
+ if score.valid? && score.value >= MIN_SCORE
68
+ t.amount
69
+ else
70
+ Amount::ZERO
71
+ end
72
+ end
73
+ end.inject(&:+) * -1
74
+ age_hours = (Time.now - txns.sort_by(&:date)[0].date) / 60
75
+ owned = Tax::FEE_TXN_HOUR * txns.count * age_hours
76
+ owned - paid
77
+ end
78
+ end
79
+ end
data/lib/zold/txn.rb CHANGED
@@ -49,6 +49,7 @@ module Zold
49
49
  @prefix = prefix
50
50
  raise 'Details can\'t be empty' if details.empty?
51
51
  raise "Details are too long: \"#{details}\"" if details.length > 128
52
+ raise "Details are wrong: \"#{details}\"" unless details =~ /^[a-zA-Z0-9 -\.,]{1,128}$/
52
53
  @details = details
53
54
  end
54
55
 
@@ -68,30 +69,34 @@ module Zold
68
69
  ].join(';')
69
70
  end
70
71
 
72
+ def to_text
73
+ "##{@id} #{@date.utc.iso8601} #{@amount} #{@bnf} #{@details}"
74
+ end
75
+
71
76
  def inverse(bnf)
72
77
  t = clone
73
- t.amount = amount.mul(-1)
78
+ t.amount = amount * -1
74
79
  t.bnf = bnf
75
80
  t
76
81
  end
77
82
 
78
- def signed(pvt)
83
+ def signed(pvt, id)
79
84
  t = clone
80
- t.sign = Signature.new.sign(pvt, self)
85
+ t.sign = Signature.new.sign(pvt, id, self)
81
86
  t
82
87
  end
83
88
 
84
- def self.parse(line, idx)
89
+ def self.parse(line, idx = 0)
85
90
  regex = Regexp.new(
86
- [
91
+ '^' + [
87
92
  '([0-9]+)',
88
93
  '([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z)',
89
94
  '(-?[0-9]+)',
90
95
  '([A-Za-z0-9]{8,32})',
91
96
  '([a-f0-9]{16})',
92
- '([a-zA-Z0-9 -.]{1,128})',
97
+ '([a-zA-Z0-9 -\.,]{1,128})',
93
98
  '([A-Za-z0-9+/]+={0,3})?'
94
- ].join(';')
99
+ ].join(';') + '$'
95
100
  )
96
101
  clean = line.strip
97
102
  raise "Invalid line ##{idx}: #{line.inspect}" unless regex.match(clean)
data/lib/zold/version.rb CHANGED
@@ -23,5 +23,5 @@
23
23
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
24
24
  # License:: MIT
25
25
  module Zold
26
- VERSION = '0.5'.freeze
26
+ VERSION = '0.6'.freeze
27
27
  end
data/lib/zold/wallet.rb CHANGED
@@ -82,12 +82,12 @@ module Zold
82
82
  txn = Txn.new(
83
83
  max + 1,
84
84
  Time.now,
85
- amount.mul(-1),
85
+ amount * -1,
86
86
  prefix,
87
87
  Id.new(target),
88
88
  details
89
89
  )
90
- txn = txn.signed(pvt)
90
+ txn = txn.signed(pvt, self)
91
91
  add(txn)
92
92
  txn
93
93
  end
@@ -31,10 +31,9 @@ require_relative '../../lib/zold/commands/create'
31
31
  class TestCreate < Minitest::Test
32
32
  def test_creates_wallet
33
33
  Dir.mktmpdir 'test' do |dir|
34
- wallet = Zold::Create.new(
35
- wallets: Zold::Wallets.new(dir),
36
- pubkey: Zold::Key.new(file: 'fixtures/id_rsa.pub')
37
- ).run
34
+ wallet = Zold::Create.new(wallets: Zold::Wallets.new(dir)).run(
35
+ ['--public-key=fixtures/id_rsa.pub']
36
+ )
38
37
  assert wallet.balance.zero?
39
38
  assert(
40
39
  File.exist?(File.join(dir, wallet.id.to_s)),
@@ -46,9 +46,8 @@ class TestDiff < Minitest::Test
46
46
  second = Zold::Wallet.new(File.join(dir, 'copy-2'))
47
47
  File.write(second.path, File.read(wallet.path))
48
48
  Zold::Pay.new(
49
- wallets: Zold::Wallets.new(dir),
50
- pvtkey: Zold::Key.new(file: 'fixtures/id_rsa')
51
- ).run([id.to_s, second.id.to_s, '14.95', '--force'])
49
+ wallets: Zold::Wallets.new(dir)
50
+ ).run([id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
52
51
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
53
52
  copies.add(File.read(first.path), 'host-1', 80, 5)
54
53
  copies.add(File.read(second.path), 'host-2', 80, 5)
@@ -48,9 +48,8 @@ class TestMerge < Minitest::Test
48
48
  second = Zold::Wallet.new(File.join(dir, 'copy-2'))
49
49
  File.write(second.path, File.read(wallet.path))
50
50
  Zold::Pay.new(
51
- wallets: Zold::Wallets.new(dir),
52
- pvtkey: Zold::Key.new(file: 'fixtures/id_rsa')
53
- ).run([id.to_s, second.id.to_s, '14.95', '--force'])
51
+ wallets: Zold::Wallets.new(dir)
52
+ ).run([id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
54
53
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
55
54
  copies.add(File.read(first.path), 'host-1', 80, 5)
56
55
  copies.add(File.read(second.path), 'host-2', 80, 5)
@@ -74,9 +73,8 @@ class TestMerge < Minitest::Test
74
73
  second = Zold::Wallet.new(File.join(dir, 'copy-2'))
75
74
  File.write(second.path, File.read(wallet.path))
76
75
  Zold::Pay.new(
77
- wallets: Zold::Wallets.new(dir),
78
- pvtkey: Zold::Key.new(file: 'fixtures/id_rsa')
79
- ).run([id.to_s, second.id.to_s, '14.95', '--force'])
76
+ wallets: Zold::Wallets.new(dir)
77
+ ).run([id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
80
78
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
81
79
  copies.add(File.read(first.path), 'host-1', 80, 5)
82
80
  copies.add(File.read(second.path), 'host-2', 80, 5)
@@ -39,11 +39,13 @@ class TestPay < Minitest::Test
39
39
  source.init(id, Zold::Key.new(file: 'fixtures/id_rsa.pub'))
40
40
  target = Zold::Id.new
41
41
  amount = Zold::Amount.new(zld: 14.95)
42
- Zold::Pay.new(
43
- wallets: wallets,
44
- pvtkey: Zold::Key.new(file: 'fixtures/id_rsa')
45
- ).run(['--force', id.to_s, target.to_s, amount.to_zld, 'For the car'])
46
- assert_equal(amount.mul(-1), source.balance)
42
+ Zold::Pay.new(wallets: wallets).run(
43
+ [
44
+ '--force', '--private-key=fixtures/id_rsa',
45
+ id.to_s, target.to_s, amount.to_zld, 'For the car'
46
+ ]
47
+ )
48
+ assert_equal(amount * -1, source.balance)
47
49
  end
48
50
  end
49
51
  end
@@ -24,6 +24,7 @@ require 'webmock/minitest'
24
24
  require_relative '../../lib/zold/wallets'
25
25
  require_relative '../../lib/zold/remotes'
26
26
  require_relative '../../lib/zold/key'
27
+ require_relative '../../lib/zold/log'
27
28
  require_relative '../../lib/zold/score'
28
29
  require_relative '../../lib/zold/commands/remote'
29
30
 
@@ -35,10 +36,11 @@ class TestRemote < Minitest::Test
35
36
  def test_updates_remote
36
37
  Dir.mktmpdir 'test' do |dir|
37
38
  remotes = Zold::Remotes.new(File.join(dir, 'a/b/c/remotes'))
38
- stub_request(:get, 'http://localhost:1/remotes').to_return(
39
+ zero = Zold::Score::ZERO
40
+ stub_request(:get, "http://#{zero.host}:#{zero.port}/remotes").to_return(
39
41
  status: 200,
40
42
  body: {
41
- score: Zold::Score::ZERO.to_h,
43
+ score: zero.to_h,
42
44
  all: [
43
45
  { host: 'localhost', port: 888 },
44
46
  { host: 'localhost', port: 999 }
@@ -56,7 +58,7 @@ class TestRemote < Minitest::Test
56
58
  )
57
59
  cmd = Zold::Remote.new(remotes: remotes)
58
60
  cmd.run(['clean'])
59
- cmd.run(%w[add localhost 1])
61
+ cmd.run(['add', zero.host, zero.port.to_s])
60
62
  cmd.run(%w[add localhost 2])
61
63
  assert_equal(2, remotes.all.count)
62
64
  cmd.run(['update', '--ignore-score-weakness'])
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2018 Yegor Bugayenko
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the 'Software'), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+
21
+ require 'minitest/autorun'
22
+ require 'tmpdir'
23
+ require 'webmock/minitest'
24
+ require_relative '../../lib/zold/wallets'
25
+ require_relative '../../lib/zold/amount'
26
+ require_relative '../../lib/zold/key'
27
+ require_relative '../../lib/zold/log'
28
+ require_relative '../../lib/zold/id'
29
+ require_relative '../../lib/zold/commands/taxes'
30
+
31
+ # TAXES test.
32
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
33
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
34
+ # License:: MIT
35
+ class TestTaxes < Minitest::Test
36
+ def test_pays_taxes
37
+ Dir.mktmpdir 'test' do |dir|
38
+ id = Zold::Id.new
39
+ wallets = Zold::Wallets.new(dir)
40
+ wallet = wallets.find(id)
41
+ wallet.init(id, Zold::Key.new(file: 'fixtures/id_rsa.pub'))
42
+ wallet.add(
43
+ Zold::Txn.new(
44
+ 1,
45
+ Time.now - 24 * 60 * 365 * 20,
46
+ Zold::Amount.new(zld: 19.99),
47
+ 'NOPREFIX', Zold::Id.new, '-'
48
+ )
49
+ )
50
+ remotes = Zold::Remotes.new(File.join(dir, 'a/remotes'))
51
+ remotes.clean
52
+ zero = Zold::Score::ZERO
53
+ remotes.add(zero.host, zero.port)
54
+ stub_request(:get, "http://#{zero.host}:#{zero.port}/").to_return(
55
+ status: 200,
56
+ body: {
57
+ score: zero.to_h
58
+ }.to_json
59
+ )
60
+ Zold::Taxes.new(
61
+ wallets: wallets, remotes: remotes
62
+ ).run(['pay', '--private-key=fixtures/id_rsa', id.to_s])
63
+ assert_equal(Zold::Amount.new(coins: 335_376_547), wallet.balance)
64
+ end
65
+ end
66
+ end