zold 0.14.42 → 0.14.43

Sign up to get free protection for your applications and to get access to all the features.
@@ -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