zold 0.13.32 → 0.13.33
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.
- checksums.yaml +4 -4
- data/heroku-run.sh +1 -1
- data/lib/zold/commands/diff.rb +1 -1
- data/lib/zold/commands/merge.rb +9 -6
- data/lib/zold/commands/node.rb +1 -0
- data/lib/zold/metronome.rb +6 -0
- data/lib/zold/node/entrance.rb +1 -1
- data/lib/zold/node/front.rb +6 -0
- data/lib/zold/patch.rb +30 -12
- data/lib/zold/txn.rb +11 -5
- data/lib/zold/version.rb +1 -1
- data/test/commands/test_merge.rb +15 -0
- data/test/node/test_front.rb +1 -0
- data/test/test_patch.rb +17 -2
- data/test/test_txn.rb +12 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7fc6c26e23e3a3d056b786a847f4ea01b4f8763
|
4
|
+
data.tar.gz: f43869756f2b8eb4a7d63e22c6392ee297483246
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 896be48716e75a94ff60de18c026ca36eeba4a558d2fbad47a7aa934dab4543724d23df676e778a20f28c19a2aa93a8504a1a099832f04013626adc4f3568534
|
7
|
+
data.tar.gz: 34aed103cece3a518ea2d9d17a8f5c03e2ee7ba495341a0e8a50ae85c32115a3717811eba5118661d6e35424032736f7a0f26f37e7fdd2f25a80fe93a28332a3
|
data/heroku-run.sh
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
|
3
|
-
./bin/zold node --no-colors --trace
|
3
|
+
./bin/zold node --no-colors --trace \
|
4
4
|
--bind-port=$PORT --port=80 --host=b1.zold.io --threads=0 \
|
5
5
|
--invoice=ML5Ern7m@912ecc24b32dbe74 --never-reboot \
|
6
6
|
--bonus-wallet=81c9c25789b03876 --private-key=bonus.key --bonus-amount=1 --bonus-time=60
|
data/lib/zold/commands/diff.rb
CHANGED
@@ -65,7 +65,7 @@ Available options:"
|
|
65
65
|
def diff(wallet, cps, _)
|
66
66
|
raise "There are no remote copies, try 'zold fetch' first" if cps.all.empty?
|
67
67
|
cps = cps.all.sort_by { |c| c[:score] }.reverse
|
68
|
-
patch = Patch.new(log: @log)
|
68
|
+
patch = Patch.new(@wallets, log: @log)
|
69
69
|
cps.each do |c|
|
70
70
|
patch.join(Wallet.new(c[:path]))
|
71
71
|
end
|
data/lib/zold/commands/merge.rb
CHANGED
@@ -45,6 +45,9 @@ module Zold
|
|
45
45
|
opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
|
46
46
|
o.banner = "Usage: zold merge [ID...] [options]
|
47
47
|
Available options:"
|
48
|
+
o.bool '--no-baseline',
|
49
|
+
'Don\'t trust any remote copies and re-validate all incoming payments against their wallets',
|
50
|
+
default: false
|
48
51
|
o.bool '--help', 'Print instructions'
|
49
52
|
end
|
50
53
|
mine = Args.new(opts, @log).take || return
|
@@ -61,20 +64,20 @@ Available options:"
|
|
61
64
|
|
62
65
|
private
|
63
66
|
|
64
|
-
def merge(id, cps,
|
67
|
+
def merge(id, cps, opts)
|
65
68
|
if cps.all.empty?
|
66
69
|
@log.error("There are no remote copies of #{id}, try 'zold fetch' first")
|
67
70
|
return
|
68
71
|
end
|
69
72
|
cps = cps.all.sort_by { |c| c[:score] }.reverse
|
70
|
-
patch = Patch.new(log: @log)
|
73
|
+
patch = Patch.new(@wallets, log: @log)
|
71
74
|
cps.each do |c|
|
72
|
-
merge_one(patch, Wallet.new(c[:path]), "#{c[:host]}:#{c[:port]}")
|
75
|
+
merge_one(opts, patch, Wallet.new(c[:path]), "#{c[:host]}:#{c[:port]}")
|
73
76
|
@log.debug("#{c[:host]}:#{c[:port]} merged: #{patch}")
|
74
77
|
end
|
75
78
|
wallet = @wallets.find(id)
|
76
79
|
if wallet.exists?
|
77
|
-
merge_one(patch, wallet, 'localhost')
|
80
|
+
merge_one(opts, patch, wallet, 'localhost')
|
78
81
|
@log.debug("Local copy merged: #{patch}")
|
79
82
|
else
|
80
83
|
@log.debug("Local copy is absent, won't merge")
|
@@ -88,8 +91,8 @@ Available options:"
|
|
88
91
|
modified
|
89
92
|
end
|
90
93
|
|
91
|
-
def merge_one(patch, wallet, name)
|
92
|
-
patch.join(wallet)
|
94
|
+
def merge_one(opts, patch, wallet, name)
|
95
|
+
patch.join(wallet, !opts['no-baseline'])
|
93
96
|
rescue StandardError => e
|
94
97
|
@log.error("Can't merge a copy coming from #{name}: #{e.message}")
|
95
98
|
@log.debug(Backtrace.new(e).to_s)
|
data/lib/zold/commands/node.rb
CHANGED
@@ -158,6 +158,7 @@ module Zold
|
|
158
158
|
farm.start(host, opts[:port], threads: opts[:threads], strength: opts[:strength]) do
|
159
159
|
Front.set(:farm, farm)
|
160
160
|
metronome = metronome(farm, entrance, opts)
|
161
|
+
Front.set(:metronome, metronome)
|
161
162
|
begin
|
162
163
|
@log.info("Starting up the web front at http://#{host}:#{opts[:port]}...")
|
163
164
|
Front.run!
|
data/lib/zold/metronome.rb
CHANGED
@@ -33,6 +33,12 @@ module Zold
|
|
33
33
|
@threads = []
|
34
34
|
end
|
35
35
|
|
36
|
+
def to_text
|
37
|
+
@threads.map do |t|
|
38
|
+
"#{t.name}: status=#{t.status}; alive=#{t.alive?};\n #{t.backtrace.join("\n ")}"
|
39
|
+
end.join("\n")
|
40
|
+
end
|
41
|
+
|
36
42
|
def add(routine)
|
37
43
|
@threads << Thread.start do
|
38
44
|
Thread.current.name = routine.class.name
|
data/lib/zold/node/entrance.rb
CHANGED
@@ -131,7 +131,7 @@ and modified nothing (this is most likely a bug!)")
|
|
131
131
|
).run(['fetch', id.to_s, "--ignore-node=#{@address}"])
|
132
132
|
modified = Merge.new(
|
133
133
|
wallets: @wallets, copies: copies.root, log: @log
|
134
|
-
).run(['merge', id.to_s])
|
134
|
+
).run(['merge', id.to_s, '--no-baseline'])
|
135
135
|
Clean.new(wallets: @wallets, copies: copies.root, log: @log).run(['clean', id.to_s])
|
136
136
|
copies.remove(localhost, Remotes::PORT)
|
137
137
|
spread(modified)
|
data/lib/zold/node/front.rb
CHANGED
@@ -56,6 +56,7 @@ module Zold
|
|
56
56
|
set :log, nil? # to be injected at node.rb
|
57
57
|
set :address, nil? # to be injected at node.rb
|
58
58
|
set :farm, nil? # to be injected at node.rb
|
59
|
+
set :metronome, nil? # to be injected at node.rb
|
59
60
|
set :entrance, nil? # to be injected at node.rb
|
60
61
|
set :wallets, nil? # to be injected at node.rb
|
61
62
|
set :remotes, nil? # to be injected at node.rb
|
@@ -201,6 +202,11 @@ module Zold
|
|
201
202
|
settings.farm.to_text
|
202
203
|
end
|
203
204
|
|
205
|
+
get '/metronome' do
|
206
|
+
content_type 'text/plain'
|
207
|
+
settings.metronome.to_text
|
208
|
+
end
|
209
|
+
|
204
210
|
not_found do
|
205
211
|
status 404
|
206
212
|
content_type 'text/plain'
|
data/lib/zold/patch.rb
CHANGED
@@ -31,21 +31,29 @@ require_relative 'atomic_file'
|
|
31
31
|
module Zold
|
32
32
|
# A patch
|
33
33
|
class Patch
|
34
|
-
def initialize(log: Log::Quiet.new)
|
34
|
+
def initialize(wallets, log: Log::Quiet.new)
|
35
|
+
raise 'Wallets can\'t be nil' if wallets.nil?
|
36
|
+
raise 'Wallets must be of type Wallets' unless wallets.is_a?(Wallets)
|
37
|
+
@wallets = wallets
|
38
|
+
@txns = []
|
35
39
|
@log = log
|
36
40
|
end
|
37
41
|
|
38
42
|
def to_s
|
39
|
-
return 'empty' if @
|
43
|
+
return 'empty' if @txns.empty?
|
40
44
|
"#{@txns.count} txns"
|
41
45
|
end
|
42
46
|
|
43
|
-
def join(wallet)
|
47
|
+
def join(wallet, baseline = true)
|
44
48
|
if @id.nil?
|
45
49
|
@id = wallet.id
|
46
50
|
@key = wallet.key
|
47
|
-
|
48
|
-
|
51
|
+
if baseline
|
52
|
+
@txns = wallet.txns
|
53
|
+
@log.debug("The baseline: #{@txns.count} transactions, the balance is #{wallet.balance}")
|
54
|
+
else
|
55
|
+
@log.debug("The baseline of #{@txns.count} transactions ignored")
|
56
|
+
end
|
49
57
|
@network = wallet.network
|
50
58
|
end
|
51
59
|
if wallet.network != @network
|
@@ -53,17 +61,16 @@ module Zold
|
|
53
61
|
end
|
54
62
|
raise 'Public key mismatch' if wallet.key != @key
|
55
63
|
raise "Wallet ID mismatch: #{@id} != #{wallet.id}" if wallet.id != @id
|
56
|
-
|
57
|
-
max = negative.empty? ? 0 : negative.max_by(&:id).id
|
64
|
+
max = @txns.select { |t| t.amount.negative? }.map(&:id).max.to_i
|
58
65
|
wallet.txns.each do |txn|
|
59
66
|
next if @txns.find { |t| t == txn }
|
60
67
|
if txn.amount.negative?
|
61
68
|
if txn.id <= max
|
62
|
-
@log.
|
69
|
+
@log.error("Transaction ID is less than max #{max}: #{txn.to_text}")
|
63
70
|
next
|
64
71
|
end
|
65
72
|
if @txns.find { |t| t.id == txn.id }
|
66
|
-
@log.
|
73
|
+
@log.error("Transaction ##{txn.id} already exists: #{txn.to_text}")
|
67
74
|
next
|
68
75
|
end
|
69
76
|
if !@txns.empty? && @txns.map(&:amount).inject(&:+) < txn.amount
|
@@ -74,9 +81,20 @@ module Zold
|
|
74
81
|
@log.error("Invalid RSA signature at transaction ##{txn.id} of #{wallet.id}: #{txn.to_text}")
|
75
82
|
next
|
76
83
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
84
|
+
else
|
85
|
+
if !txn.sign.nil? && !txn.sign.empty?
|
86
|
+
@log.error("RSA signature is redundant at ##{txn.id} of #{wallet.id}: #{txn.to_text}")
|
87
|
+
next
|
88
|
+
end
|
89
|
+
payer = @wallets.find(txn.bnf)
|
90
|
+
unless payer.exists?
|
91
|
+
@log.error("Paying wallet #{wallet.id} is absent at ##{txn.id}: #{txn.to_text}")
|
92
|
+
next
|
93
|
+
end
|
94
|
+
unless payer.has?(txn.id, wallet.id)
|
95
|
+
@log.error("Paying wallet #{wallet.id} doesn't have transaction ##{txn.id}: #{txn.to_text}")
|
96
|
+
next
|
97
|
+
end
|
80
98
|
end
|
81
99
|
@log.debug("Merged on top: #{txn.to_text}")
|
82
100
|
@txns << txn
|
data/lib/zold/txn.rb
CHANGED
@@ -32,6 +32,12 @@ require_relative 'signature'
|
|
32
32
|
module Zold
|
33
33
|
# A single transaction
|
34
34
|
class Txn
|
35
|
+
# Regular expression for details
|
36
|
+
RE_DETAILS = '[a-zA-Z0-9 @\!\?\*_\-\.:,\']+'.freeze
|
37
|
+
|
38
|
+
# Regular expression for prefix
|
39
|
+
RE_PREFIX = '[a-zA-Z0-9]+'.freeze
|
40
|
+
|
35
41
|
attr_reader :id, :date, :amount, :prefix, :bnf, :details, :sign
|
36
42
|
attr_writer :sign, :amount, :bnf
|
37
43
|
def initialize(id, date, amount, prefix, bnf, details)
|
@@ -52,12 +58,12 @@ module Zold
|
|
52
58
|
raise 'Prefix can\'t be NIL' if prefix.nil?
|
53
59
|
raise "Prefix is too short: \"#{prefix}\"" if prefix.length < 8
|
54
60
|
raise "Prefix is too long: \"#{prefix}\"" if prefix.length > 32
|
55
|
-
raise "Prefix is wrong: \"#{prefix}\"" unless prefix =~
|
61
|
+
raise "Prefix is wrong: \"#{prefix}\" (#{Txn::RE_PREFIX})" unless prefix =~ Regexp.new("^#{Txn::RE_PREFIX}$")
|
56
62
|
@prefix = prefix
|
57
63
|
raise 'Details can\'t be NIL' if details.nil?
|
58
64
|
raise 'Details can\'t be empty' if details.empty?
|
59
65
|
raise "Details are too long: \"#{details}\"" if details.length > 512
|
60
|
-
raise "
|
66
|
+
raise "Wrong details \"#{details}\" (#{Txn::RE_DETAILS})" unless details =~ Regexp.new("^#{Txn::RE_DETAILS}$")
|
61
67
|
@details = details
|
62
68
|
end
|
63
69
|
|
@@ -103,14 +109,14 @@ module Zold
|
|
103
109
|
'([0-9a-f]{4})',
|
104
110
|
'([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z)',
|
105
111
|
'([0-9a-f]{16})',
|
106
|
-
|
112
|
+
"(#{Txn::RE_PREFIX})",
|
107
113
|
'([0-9a-f]{16})',
|
108
|
-
|
114
|
+
"(#{Txn::RE_DETAILS})",
|
109
115
|
'([A-Za-z0-9+/]+={0,3})?'
|
110
116
|
].join(';') + '$'
|
111
117
|
)
|
112
118
|
clean = line.strip
|
113
|
-
raise "Invalid line ##{idx}: #{line.inspect}" unless regex.match(clean)
|
119
|
+
raise "Invalid line ##{idx}: #{line.inspect} #{regex}" unless regex.match(clean)
|
114
120
|
parts = clean.split(';')
|
115
121
|
txn = Txn.new(
|
116
122
|
Hexnum.parse(parts[0]).to_i,
|
data/lib/zold/version.rb
CHANGED
data/test/commands/test_merge.rb
CHANGED
@@ -106,4 +106,19 @@ class TestMerge < Minitest::Test
|
|
106
106
|
assert(!wallet.balance.zero?)
|
107
107
|
end
|
108
108
|
end
|
109
|
+
|
110
|
+
def test_rejects_fake_positives_in_new_wallet
|
111
|
+
FakeHome.new.run do |home|
|
112
|
+
main = home.create_wallet
|
113
|
+
remote = home.create_wallet
|
114
|
+
File.write(remote.path, File.read(main.path))
|
115
|
+
remote.add(Zold::Txn.new(1, Time.now, Zold::Amount.new(zld: 11.0), 'NOPREFIX', Zold::Id.new, 'fake'))
|
116
|
+
copies = home.copies(main)
|
117
|
+
copies.add(File.read(remote.path), 'fake-host', 80, 0)
|
118
|
+
Zold::Merge.new(wallets: home.wallets, copies: copies.root, log: test_log).run(
|
119
|
+
['merge', main.id.to_s, '--no-baseline']
|
120
|
+
)
|
121
|
+
assert_equal(Zold::Amount::ZERO, main.balance)
|
122
|
+
end
|
123
|
+
end
|
109
124
|
end
|
data/test/node/test_front.rb
CHANGED
data/test/test_patch.rb
CHANGED
@@ -46,13 +46,28 @@ class TestPatch < Minitest::Test
|
|
46
46
|
File.write(third.path, File.read(first.path))
|
47
47
|
t = third.sub(Zold::Amount.new(zld: 10.0), "NOPREFIX@#{Zold::Id.new}", key)
|
48
48
|
third.add(t.inverse(first.id))
|
49
|
-
patch = Zold::Patch.new(log: test_log)
|
49
|
+
patch = Zold::Patch.new(home.wallets, log: test_log)
|
50
50
|
patch.join(first)
|
51
51
|
patch.join(second)
|
52
52
|
patch.join(third)
|
53
53
|
FileUtils.rm(first.path)
|
54
54
|
assert_equal(true, patch.save(first.path))
|
55
|
-
assert_equal(Zold::Amount.new(zld: -
|
55
|
+
assert_equal(Zold::Amount.new(zld: -53.0), first.balance)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_rejects_fake_positives
|
60
|
+
FakeHome.new.run do |home|
|
61
|
+
first = home.create_wallet
|
62
|
+
second = home.create_wallet
|
63
|
+
File.write(second.path, File.read(first.path))
|
64
|
+
second.add(Zold::Txn.new(1, Time.now, Zold::Amount.new(zld: 11.0), 'NOPREFIX', Zold::Id.new, 'fake'))
|
65
|
+
patch = Zold::Patch.new(home.wallets, log: test_log)
|
66
|
+
patch.join(first)
|
67
|
+
patch.join(second)
|
68
|
+
FileUtils.rm(first.path)
|
69
|
+
assert_equal(true, patch.save(first.path))
|
70
|
+
assert_equal(Zold::Amount::ZERO, first.balance)
|
56
71
|
end
|
57
72
|
end
|
58
73
|
end
|
data/test/test_txn.rb
CHANGED
@@ -43,4 +43,16 @@ class TestTxn < Minitest::Test
|
|
43
43
|
assert_equal('-99.95', txn.amount.to_zld)
|
44
44
|
assert_equal('NOPREFIX', txn.prefix)
|
45
45
|
end
|
46
|
+
|
47
|
+
def test_accepts_text_as_details
|
48
|
+
details = 'How are you, dude?! I\'m @yegor256: *_hello_'
|
49
|
+
txn = Zold::Txn.parse(
|
50
|
+
Zold::Txn.new(
|
51
|
+
123, Time.now, Zold::Amount.new(zld: -99.95),
|
52
|
+
'NOPREFIX', Zold::Id.new,
|
53
|
+
details
|
54
|
+
).to_s
|
55
|
+
)
|
56
|
+
assert_equal(details, txn.details)
|
57
|
+
end
|
46
58
|
end
|
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.13.
|
4
|
+
version: 0.13.33
|
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-06-
|
11
|
+
date: 2018-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|