zold 0.14.42 → 0.14.43

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.
@@ -0,0 +1,62 @@
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
+ require 'pathname'
23
+ require_relative 'id'
24
+ require_relative 'wallet'
25
+
26
+ # Cached collection of wallets.
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Zold
31
+ # Collection of local wallets
32
+ class CachedWallets
33
+ def initialize(wallets)
34
+ @wallets = wallets
35
+ @cache = {}
36
+ @mutex = Mutex.new
37
+ end
38
+
39
+ def to_s
40
+ @wallets.to_s
41
+ end
42
+
43
+ def path
44
+ @wallets.path
45
+ end
46
+
47
+ def all
48
+ @wallets.all
49
+ end
50
+
51
+ def find(id)
52
+ @wallets.find(id) do |wallet|
53
+ yield(
54
+ @mutex.synchronize do
55
+ @cache[id] = wallet unless @cache[id]
56
+ @cache[id]
57
+ end
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,65 @@
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_relative 'txn'
24
+
25
+ # Head of a wallet.
26
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
27
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
28
+ # License:: MIT
29
+ module Zold
30
+ # Head of the wallet.
31
+ class Head
32
+ def initialize(file)
33
+ @file = file
34
+ end
35
+
36
+ def flush
37
+ # nothing
38
+ end
39
+
40
+ def fetch
41
+ raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
42
+ lines = AtomicFile.new(@file).read.split(/\n/)
43
+ raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
44
+ lines.take(4)
45
+ end
46
+
47
+ # Cached head.
48
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
49
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
50
+ # License:: MIT
51
+ class Cached
52
+ def initialize(txns)
53
+ @txns = txns
54
+ end
55
+
56
+ def flush
57
+ @fetch = nil
58
+ end
59
+
60
+ def fetch
61
+ @fetch ||= @txns.fetch
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,69 @@
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_relative 'txn'
24
+ require_relative 'atomic_file'
25
+
26
+ # Transactions in a wallet.
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Zold
31
+ # A collection of transactions
32
+ class Txns
33
+ def initialize(file)
34
+ @file = file
35
+ end
36
+
37
+ def flush
38
+ # nothing
39
+ end
40
+
41
+ def fetch
42
+ raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
43
+ lines = AtomicFile.new(@file).read.split(/\n/)
44
+ raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
45
+ lines.drop(5)
46
+ .each_with_index
47
+ .map { |line, i| Txn.parse(line, i + 6) }
48
+ .sort
49
+ end
50
+
51
+ # Cached transactions.
52
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
53
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
54
+ # License:: MIT
55
+ class Cached
56
+ def initialize(txns)
57
+ @txns = txns
58
+ end
59
+
60
+ def flush
61
+ @fetch = nil
62
+ end
63
+
64
+ def fetch
65
+ @fetch ||= @txns.fetch
66
+ end
67
+ end
68
+ end
69
+ end
@@ -25,6 +25,6 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.14.42'
28
+ VERSION = '0.14.43'
29
29
  PROTOCOL = 2
30
30
  end
@@ -31,6 +31,8 @@ require_relative 'amount'
31
31
  require_relative 'hexnum'
32
32
  require_relative 'signature'
33
33
  require_relative 'atomic_file'
34
+ require_relative 'txns'
35
+ require_relative 'head'
34
36
 
35
37
  # The wallet.
36
38
  #
@@ -52,8 +54,9 @@ module Zold
52
54
  EXTENSION = '.z'
53
55
 
54
56
  def initialize(file)
55
- @file = file
56
- @file = "#{file}#{EXTENSION}" if File.extname(file).empty?
57
+ @file = File.extname(file).empty? ? "#{file}#{EXTENSION}" : file
58
+ @txns = Txns::Cached.new(Txns.new(@file))
59
+ @head = Head::Cached.new(Head.new(@file))
57
60
  end
58
61
 
59
62
  def ==(other)
@@ -65,13 +68,13 @@ module Zold
65
68
  end
66
69
 
67
70
  def network
68
- n = lines[0].strip
71
+ n = @head.fetch[0].strip
69
72
  raise "Invalid network name '#{n}'" unless n =~ /^[a-z]{4,16}$/
70
73
  n
71
74
  end
72
75
 
73
76
  def protocol
74
- v = lines[1].strip
77
+ v = @head.fetch[1].strip
75
78
  raise "Invalid protocol version name '#{v}'" unless v =~ /^[0-9]+$/
76
79
  v.to_i
77
80
  end
@@ -88,6 +91,8 @@ module Zold
88
91
  raise "File '#{@file}' already exists" if File.exist?(@file) && !overwrite
89
92
  raise "Invalid network name '#{network}'" unless network =~ /^[a-z]{4,16}$/
90
93
  AtomicFile.new(@file).write("#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
94
+ @txns.flush
95
+ @head.flush
91
96
  end
92
97
 
93
98
  def root?
@@ -95,7 +100,7 @@ module Zold
95
100
  end
96
101
 
97
102
  def id
98
- Id.new(lines[2].strip)
103
+ Id.new(@head.fetch[2].strip)
99
104
  end
100
105
 
101
106
  def balance
@@ -130,6 +135,7 @@ module Zold
130
135
  raise "The transaction with the same ID and BNF already exists: #{dup}" unless dup.nil?
131
136
  raise "The tax payment already exists: #{txn}" if Tax.new(self).exists?(txn)
132
137
  File.open(@file, 'a') { |f| f.print "#{txn}\n" }
138
+ @txns.flush
133
139
  end
134
140
 
135
141
  def has?(id, bnf)
@@ -143,7 +149,7 @@ module Zold
143
149
  end
144
150
 
145
151
  def key
146
- Key.new(text: lines[3].strip)
152
+ Key.new(text: @head.fetch[3].strip)
147
153
  end
148
154
 
149
155
  def income
@@ -167,16 +173,14 @@ module Zold
167
173
  end
168
174
 
169
175
  def txns
170
- lines.drop(5)
171
- .each_with_index
172
- .map { |line, i| Txn.parse(line, i + 6) }
173
- .sort
176
+ @txns.fetch
174
177
  end
175
178
 
176
179
  def refurbish
177
180
  AtomicFile.new(@file).write(
178
181
  "#{network}\n#{protocol}\n#{id}\n#{key.to_pub}\n\n#{txns.map { |t| t.to_s + "\n" }.join}"
179
182
  )
183
+ @txns.flush
180
184
  end
181
185
 
182
186
  private
@@ -185,12 +189,5 @@ module Zold
185
189
  negative = txns.select { |t| t.amount.negative? }
186
190
  negative.empty? ? 0 : negative.max_by(&:id).id
187
191
  end
188
-
189
- def lines
190
- raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
191
- lines = AtomicFile.new(@file).read.split(/\n/)
192
- raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
193
- lines
194
- end
195
192
  end
196
193
  end
@@ -62,7 +62,7 @@ module Zold
62
62
 
63
63
  def find(id)
64
64
  raise 'Id can\'t be nil' if id.nil?
65
- raise 'Id must be of type Id' unless id.is_a?(Id)
65
+ raise "Id must be of type Id, #{id.class.name} instead" unless id.is_a?(Id)
66
66
  yield Zold::Wallet.new(File.join(path, id.to_s))
67
67
  end
68
68
  end
@@ -52,9 +52,12 @@ class FakeHome
52
52
  end
53
53
 
54
54
  def create_wallet(id = Zold::Id.new, dir = @dir)
55
- wallet = Zold::Wallet.new(File.join(dir, id.to_s))
56
- wallet.init(id, Zold::Key.new(file: File.join(__dir__, '../fixtures/id_rsa.pub')))
57
- wallet
55
+ target = Zold::Wallet.new(File.join(dir, id.to_s))
56
+ wallets.find(id) do |w|
57
+ w.init(id, Zold::Key.new(file: File.join(__dir__, '../fixtures/id_rsa.pub')))
58
+ File.write(target.path, File.read(w.path))
59
+ end
60
+ target
58
61
  end
59
62
 
60
63
  def create_wallet_json(id = Zold::Id.new)
@@ -56,6 +56,8 @@ class TestEntrance < Minitest::Test
56
56
  e = Zold::Entrance.new(home.wallets, home.remotes, home.copies(source).root, 'x', log: test_log)
57
57
  modified = e.push(source.id, body)
58
58
  assert_equal(2, modified.count)
59
+ assert(modified.include?(sid))
60
+ assert(modified.include?(tid))
59
61
  end
60
62
  end
61
63
 
@@ -65,7 +67,7 @@ class TestEntrance < Minitest::Test
65
67
  e = Zold::Entrance.new(home.wallets, home.remotes, home.copies.root, 'x', log: test_log)
66
68
  e.push(wallet.id, File.read(wallet.path))
67
69
  assert(e.to_json[:history].include?(wallet.id.to_s))
68
- assert(e.to_json[:speed].positive?)
70
+ assert(!e.to_json[:speed].negative?)
69
71
  end
70
72
  end
71
73
  end
@@ -0,0 +1,52 @@
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 'tmpdir'
25
+ require_relative 'fake_home'
26
+ require_relative '../lib/zold/key'
27
+ require_relative '../lib/zold/id'
28
+ require_relative '../lib/zold/wallets'
29
+ require_relative '../lib/zold/cached_wallets'
30
+ require_relative '../lib/zold/amount'
31
+
32
+ # CachedWallets test.
33
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
34
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
35
+ # License:: MIT
36
+ class TestCachedWallets < Minitest::Test
37
+ def test_adds_wallet
38
+ FakeHome.new.run do |home|
39
+ wallets = Zold::CachedWallets.new(home.wallets)
40
+ id = Zold::Id.new
41
+ first = nil
42
+ wallets.find(id) do |wallet|
43
+ wallet.init(id, Zold::Key.new(file: 'fixtures/id_rsa.pub'))
44
+ assert_equal(1, wallets.all.count)
45
+ first = wallet
46
+ end
47
+ wallets.find(id) do |wallet|
48
+ assert_equal(first, wallet)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -42,6 +42,19 @@ class TestWallet < Minitest::Test
42
42
  end
43
43
  end
44
44
 
45
+ def test_reads_large_wallet
46
+ key = Zold::Key.new(file: 'fixtures/id_rsa')
47
+ FakeHome.new.run do |home|
48
+ wallet = home.create_wallet(Zold::Id.new('448b451bc62e8e16'))
49
+ FileUtils.cp('fixtures/448b451bc62e8e16.z', wallet.path)
50
+ start = Time.now
51
+ wallet.txns
52
+ wallet.sub(Zold::Amount.new(zld: 39.99), "NOPREFIX@#{Zold::Id.new}", key)
53
+ time = Time.now - start
54
+ assert(time < 0.5, "Too slow: #{time.round(2)} seconds")
55
+ end
56
+ end
57
+
45
58
  def test_adds_transaction
46
59
  FakeHome.new.run do |home|
47
60
  wallet = home.create_wallet
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.14.42
4
+ version: 0.14.43
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-10-05 00:00:00.000000000 Z
11
+ date: 2018-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cachy
@@ -433,6 +433,7 @@ files:
433
433
  - features/gem_package.feature
434
434
  - features/step_definitions/steps.rb
435
435
  - features/support/env.rb
436
+ - fixtures/448b451bc62e8e16.z
436
437
  - fixtures/id_rsa
437
438
  - fixtures/id_rsa-2
438
439
  - fixtures/id_rsa-2.pub
@@ -475,6 +476,7 @@ files:
475
476
  - lib/zold/amount.rb
476
477
  - lib/zold/atomic_file.rb
477
478
  - lib/zold/backtrace.rb
479
+ - lib/zold/cached_wallets.rb
478
480
  - lib/zold/commands/alias.rb
479
481
  - lib/zold/commands/args.rb
480
482
  - lib/zold/commands/calculate.rb
@@ -499,6 +501,7 @@ files:
499
501
  - lib/zold/commands/taxes.rb
500
502
  - lib/zold/copies.rb
501
503
  - lib/zold/gem.rb
504
+ - lib/zold/head.rb
502
505
  - lib/zold/hexnum.rb
503
506
  - lib/zold/http.rb
504
507
  - lib/zold/hungry_wallets.rb
@@ -526,6 +529,7 @@ files:
526
529
  - lib/zold/tax.rb
527
530
  - lib/zold/tree_wallets.rb
528
531
  - lib/zold/txn.rb
532
+ - lib/zold/txns.rb
529
533
  - lib/zold/type.rb
530
534
  - lib/zold/upgrades.rb
531
535
  - lib/zold/verbose_thread.rb
@@ -570,6 +574,7 @@ files:
570
574
  - test/test_amount.rb
571
575
  - test/test_atomic_file.rb
572
576
  - test/test_backtrace.rb
577
+ - test/test_cached_wallets.rb
573
578
  - test/test_copies.rb
574
579
  - test/test_gem.rb
575
580
  - test/test_hexnum.rb
@@ -620,7 +625,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
620
625
  version: '0'
621
626
  requirements: []
622
627
  rubyforge_project:
623
- rubygems_version: 2.5.2
628
+ rubygems_version: 2.7.6
624
629
  signing_key:
625
630
  specification_version: 2
626
631
  summary: A fast cryptocurrency for micro payments
@@ -665,6 +670,7 @@ test_files:
665
670
  - test/test_amount.rb
666
671
  - test/test_atomic_file.rb
667
672
  - test/test_backtrace.rb
673
+ - test/test_cached_wallets.rb
668
674
  - test/test_copies.rb
669
675
  - test/test_gem.rb
670
676
  - test/test_hexnum.rb