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