zold 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/bin/zold +21 -81
- data/fixtures/keys/1.pub +1 -0
- data/fixtures/keys/2 +51 -0
- data/fixtures/keys/2.pub +1 -0
- data/fixtures/scripts/print-helps.sh +15 -0
- data/fixtures/scripts/push-and-pull.sh +7 -3
- data/lib/zold/commands/clean.rb +24 -7
- data/lib/zold/commands/create.rb +16 -4
- data/lib/zold/commands/diff.rb +32 -9
- data/lib/zold/commands/fetch.rb +36 -9
- data/lib/zold/commands/invoice.rb +64 -0
- data/lib/zold/commands/list.rb +1 -1
- data/lib/zold/commands/merge.rb +31 -10
- data/lib/zold/commands/node.rb +6 -2
- data/lib/zold/commands/pay.rb +40 -17
- data/lib/zold/commands/propagate.rb +34 -14
- data/lib/zold/commands/push.rb +27 -9
- data/lib/zold/commands/remote.rb +63 -34
- data/lib/zold/commands/show.rb +34 -9
- data/lib/zold/copies.rb +4 -0
- data/lib/zold/http.rb +1 -1
- data/lib/zold/key.rb +17 -11
- data/lib/zold/node/farm.rb +8 -0
- data/lib/zold/node/front.rb +13 -3
- data/lib/zold/patch.rb +8 -8
- data/lib/zold/prefixes.rb +53 -0
- data/lib/zold/remotes.rb +6 -0
- data/lib/zold/score.rb +4 -4
- data/lib/zold/signature.rb +9 -9
- data/lib/zold/txn.rb +111 -0
- data/lib/zold/version.rb +1 -1
- data/lib/zold/wallet.rb +32 -59
- data/lib/zold/wallets.rb +2 -2
- data/test/commands/test_clean.rb +5 -4
- data/test/commands/test_create.rb +3 -3
- data/test/commands/test_diff.rb +16 -15
- data/test/commands/test_fetch.rb +13 -11
- data/test/commands/test_invoice.rb +46 -0
- data/test/commands/test_list.rb +4 -4
- data/test/commands/test_merge.rb +21 -22
- data/test/commands/test_node.rb +9 -9
- data/test/commands/test_pay.rb +12 -12
- data/test/commands/test_remote.rb +11 -11
- data/test/commands/test_show.rb +9 -7
- data/test/node/fake_node.rb +3 -3
- data/test/node/test_farm.rb +1 -1
- data/test/node/test_front.rb +6 -6
- data/test/test_amount.rb +1 -1
- data/test/test_copies.rb +1 -1
- data/test/test_http.rb +1 -1
- data/test/test_id.rb +1 -1
- data/test/test_key.rb +19 -1
- data/test/test_patch.rb +11 -11
- data/test/test_prefixes.rb +46 -0
- data/test/test_remotes.rb +1 -1
- data/test/test_score.rb +1 -1
- data/test/test_signature.rb +9 -11
- data/test/test_wallet.rb +22 -21
- data/test/test_wallets.rb +4 -4
- metadata +12 -1
data/lib/zold/commands/pay.rb
CHANGED
@@ -19,7 +19,7 @@
|
|
19
19
|
# SOFTWARE.
|
20
20
|
|
21
21
|
require 'slop'
|
22
|
-
require_relative '../log
|
22
|
+
require_relative '../log'
|
23
23
|
|
24
24
|
# PAY command.
|
25
25
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -28,30 +28,53 @@ require_relative '../log.rb'
|
|
28
28
|
module Zold
|
29
29
|
# Money sending command
|
30
30
|
class Pay
|
31
|
-
def initialize(
|
32
|
-
|
33
|
-
@payer = payer
|
34
|
-
@receiver = receiver
|
35
|
-
@amount = amount
|
31
|
+
def initialize(wallets:, pvtkey:, log: Log::Quiet.new)
|
32
|
+
@wallets = wallets
|
36
33
|
@pvtkey = pvtkey
|
37
|
-
@details = details
|
38
34
|
@log = log
|
39
35
|
end
|
40
36
|
|
41
37
|
def run(args = [])
|
42
|
-
opts = Slop.parse(args) do |o|
|
43
|
-
o.
|
38
|
+
opts = Slop.parse(args, help: true) do |o|
|
39
|
+
o.banner = "Usage: zold pay wallet invoice amount [details] [options]
|
40
|
+
Where:
|
41
|
+
'wallet' is the sender's wallet ID
|
42
|
+
'invoice' is the beneficiary's invoice number, generated by 'zold invoice'
|
43
|
+
'amount' is the amount to pay, in ZLD, for example '14.95'
|
44
|
+
'details' is the optional text to attach to the payment
|
45
|
+
Available options:"
|
46
|
+
o.bool '--force',
|
47
|
+
'Ignore all validations',
|
48
|
+
default: false
|
49
|
+
o.bool '--help', 'Print instructions'
|
44
50
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
if opts.help?
|
52
|
+
@log.info(opts.to_s)
|
53
|
+
return
|
54
|
+
end
|
55
|
+
raise 'Payer wallet ID is required' if opts.arguments[0].nil?
|
56
|
+
from = @wallets.find(Zold::Id.new(opts.arguments[0]))
|
57
|
+
raise 'Wallet doesn\'t exist, do \'fetch\' first' unless from.exists?
|
58
|
+
raise 'Recepient\'s invoice is required' if opts.arguments[1].nil?
|
59
|
+
invoice = opts.arguments[1]
|
60
|
+
raise 'Amount is required (in ZLD)' if opts.arguments[2].nil?
|
61
|
+
amount = Zold::Amount.new(zld: opts.arguments[2].to_f)
|
62
|
+
details = opts.arguments[3] ? opts.arguments[3] : '-'
|
63
|
+
pay(from, invoice, amount, details, opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
def pay(from, invoice, amount, details, opts)
|
67
|
+
unless opts.force?
|
68
|
+
raise 'The amount can\'t be zero' if amount.zero?
|
69
|
+
raise "The amount can't be negative: #{amount}" if amount.negative?
|
70
|
+
if !from.root? && from.balance < @amount
|
71
|
+
raise "There is not enough funds in #{from} to send #{amount}, \
|
72
|
+
only #{payer.balance} left"
|
50
73
|
end
|
51
74
|
end
|
52
|
-
txn =
|
53
|
-
@log.debug("#{
|
54
|
-
@log.info(txn
|
75
|
+
txn = from.sub(amount, invoice, @pvtkey, details)
|
76
|
+
@log.debug("#{amount} sent from #{from} to #{invoice}: #{details}")
|
77
|
+
@log.info(txn.id)
|
55
78
|
txn
|
56
79
|
end
|
57
80
|
end
|
@@ -18,9 +18,10 @@
|
|
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_relative '../log
|
22
|
-
require_relative '../wallet
|
23
|
-
require_relative '../wallets
|
21
|
+
require_relative '../log'
|
22
|
+
require_relative '../wallet'
|
23
|
+
require_relative '../wallets'
|
24
|
+
require_relative '../prefixes'
|
24
25
|
|
25
26
|
# PROPAGATE command.
|
26
27
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -29,25 +30,44 @@ require_relative '../wallets.rb'
|
|
29
30
|
module Zold
|
30
31
|
# PROPAGATE pulling command
|
31
32
|
class Propagate
|
32
|
-
def initialize(
|
33
|
-
@wallet = wallet
|
33
|
+
def initialize(wallets:, log: Log::Quiet.new)
|
34
34
|
@wallets = wallets
|
35
35
|
@log = log
|
36
36
|
end
|
37
37
|
|
38
|
-
def run(
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
def run(args = [])
|
39
|
+
opts = Slop.parse(args, help: true) do |o|
|
40
|
+
o.banner = "Usage: zold propagate [ID...] [options]
|
41
|
+
Available options:"
|
42
|
+
o.bool '--help', 'Print instructions'
|
43
|
+
end
|
44
|
+
if opts.help?
|
45
|
+
@log.info(opts.to_s)
|
46
|
+
return
|
47
|
+
end
|
48
|
+
raise 'At least one wallet ID is required' if opts.arguments.empty?
|
49
|
+
opts.arguments.each do |id|
|
50
|
+
propagate(@wallets.find(id), opts)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def propagate(wallet, _)
|
55
|
+
me = wallet.id
|
56
|
+
wallet.txns.select { |t| t.amount.negative? }.each do |t|
|
57
|
+
target = @wallets.find(t.bnf)
|
42
58
|
unless target.exists?
|
43
|
-
@log.debug("#{t
|
59
|
+
@log.debug("#{t.amount.mul(-1)} to #{t.bnf}: wallet is absent")
|
60
|
+
next
|
61
|
+
end
|
62
|
+
next if target.has?(t.id, me)
|
63
|
+
unless Prefixes.new(target).valid?(t.prefix)
|
64
|
+
@log.info("#{t.amount.mul(-1)} to #{t.bnf}: wrong prefix")
|
44
65
|
next
|
45
66
|
end
|
46
|
-
|
47
|
-
|
48
|
-
@log.info("#{t[:amount].mul(-1)} to #{t[:bnf]}")
|
67
|
+
target.add(t.inverse(me))
|
68
|
+
@log.info("#{t.amount.mul(-1)} to #{t.bnf}")
|
49
69
|
end
|
50
|
-
@log.debug(
|
70
|
+
@log.debug("Wallet #{me} propagated successfully")
|
51
71
|
end
|
52
72
|
end
|
53
73
|
end
|
data/lib/zold/commands/push.rb
CHANGED
@@ -18,9 +18,11 @@
|
|
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 'slop'
|
21
22
|
require 'net/http'
|
22
|
-
require_relative '../log
|
23
|
-
require_relative '../
|
23
|
+
require_relative '../log'
|
24
|
+
require_relative '../id'
|
25
|
+
require_relative '../http'
|
24
26
|
|
25
27
|
# PUSH command.
|
26
28
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -29,21 +31,37 @@ require_relative '../http.rb'
|
|
29
31
|
module Zold
|
30
32
|
# Wallet pushing command
|
31
33
|
class Push
|
32
|
-
def initialize(
|
33
|
-
@
|
34
|
+
def initialize(wallets:, remotes:, log: Log::Quiet.new)
|
35
|
+
@wallets = wallets
|
34
36
|
@remotes = remotes
|
35
37
|
@log = log
|
36
38
|
end
|
37
39
|
|
38
|
-
def run(
|
39
|
-
|
40
|
+
def run(args = [])
|
41
|
+
opts = Slop.parse(args, help: true) do |o|
|
42
|
+
o.banner = "Usage: zold push [ID...] [options]
|
43
|
+
Available options:"
|
44
|
+
o.bool '--help', 'Print instructions'
|
45
|
+
end
|
46
|
+
if opts.help?
|
47
|
+
@log.info(opts.to_s)
|
48
|
+
return
|
49
|
+
end
|
50
|
+
raise 'At least one wallet ID is required' if opts.arguments.empty?
|
51
|
+
opts.arguments.each do |id|
|
52
|
+
push(@wallets.find(Id.new(id)), opts)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def push(wallet, _)
|
57
|
+
raise 'The wallet is absent' unless wallet.exists?
|
40
58
|
remote = @remotes.all[0]
|
41
|
-
uri = URI("#{remote[:home]}/wallet/#{
|
42
|
-
response = Http.new(uri).put(File.read(
|
59
|
+
uri = URI("#{remote[:home]}/wallet/#{wallet.id}")
|
60
|
+
response = Http.new(uri).put(File.read(wallet.path))
|
43
61
|
unless response.code == '200'
|
44
62
|
raise "Failed to push to #{uri}: #{response.code}/#{response.message}"
|
45
63
|
end
|
46
|
-
@log.info("The #{
|
64
|
+
@log.info("The #{wallet.id} pushed to #{uri}")
|
47
65
|
end
|
48
66
|
end
|
49
67
|
end
|
data/lib/zold/commands/remote.rb
CHANGED
@@ -18,14 +18,15 @@
|
|
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 'slop'
|
21
22
|
require 'rainbow'
|
22
23
|
require 'net/http'
|
23
24
|
require 'json'
|
24
25
|
require 'time'
|
25
|
-
require_relative '../log
|
26
|
-
require_relative '../http
|
27
|
-
require_relative '../remotes
|
28
|
-
require_relative '../score
|
26
|
+
require_relative '../log'
|
27
|
+
require_relative '../http'
|
28
|
+
require_relative '../remotes'
|
29
|
+
require_relative '../score'
|
29
30
|
|
30
31
|
# REMOTE command.
|
31
32
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -56,48 +57,60 @@ Available commands:
|
|
56
57
|
#{Rainbow('remote update').green}
|
57
58
|
Check each registered remote node for availability
|
58
59
|
Available options:"
|
60
|
+
o.bool '--ignore-score-weakness',
|
61
|
+
'Don\'t complain when their score is too weak',
|
62
|
+
default: false
|
59
63
|
o.bool '--help', 'Print instructions'
|
60
64
|
end
|
61
65
|
command = args[0]
|
62
66
|
case command
|
63
67
|
when 'show'
|
64
|
-
|
65
|
-
score = Rainbow("/#{r[:score]}").color(r[:score] > 0 ? :green : :red)
|
66
|
-
@log.info(r[:host] + Rainbow(":#{r[:port]}").gray + score)
|
67
|
-
end
|
68
|
+
show
|
68
69
|
when 'clean'
|
69
|
-
|
70
|
-
@log.debug('All remote nodes deleted')
|
70
|
+
clean
|
71
71
|
when 'reset'
|
72
|
-
|
73
|
-
@log.debug('Remote nodes set back to default')
|
72
|
+
reset
|
74
73
|
when 'add'
|
75
|
-
|
76
|
-
port = args[2]
|
77
|
-
@remotes.add(host, port)
|
78
|
-
@log.info("#{host}:#{port}: added")
|
74
|
+
add(opts.arguments[1], opts.arguments[2].to_i)
|
79
75
|
when 'remove'
|
80
|
-
|
81
|
-
port = args[2]
|
82
|
-
@remotes.remove(host, port)
|
83
|
-
@log.info("#{host}:#{port}: removed")
|
76
|
+
remove(opts.arguments[1], opts.arguments[2].to_i)
|
84
77
|
when 'update'
|
85
|
-
update
|
86
|
-
total = @remotes.all.size
|
87
|
-
if total.zero?
|
88
|
-
@log.debug("The list of remotes is #{Rainbow('empty').red}!")
|
89
|
-
@log.debug(
|
90
|
-
"Run 'zold remote add b1.zold.io 80` and then `zold update`"
|
91
|
-
)
|
92
|
-
else
|
93
|
-
@log.debug("There are #{total} known remotes")
|
94
|
-
end
|
78
|
+
update(opts)
|
95
79
|
else
|
96
80
|
@log.info(opts.to_s)
|
97
81
|
end
|
98
82
|
end
|
99
83
|
|
100
|
-
def
|
84
|
+
def show
|
85
|
+
@remotes.all.each do |r|
|
86
|
+
score = Rainbow("/#{r[:score]}").color(r[:score] > 0 ? :green : :red)
|
87
|
+
@log.info(r[:host] + Rainbow(":#{r[:port]}").gray + score)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def clean
|
92
|
+
@remotes.clean
|
93
|
+
@log.debug('All remote nodes deleted')
|
94
|
+
end
|
95
|
+
|
96
|
+
def reset
|
97
|
+
@remotes.reset
|
98
|
+
@log.debug('Remote nodes set back to default')
|
99
|
+
end
|
100
|
+
|
101
|
+
def add(host, port)
|
102
|
+
@remotes.add(host, port)
|
103
|
+
@log.info("#{host}:#{port} added to the list")
|
104
|
+
@log.info("There are #{@remotes.all.count} remote nodes in the list")
|
105
|
+
end
|
106
|
+
|
107
|
+
def remove(host, port)
|
108
|
+
@remotes.remove(host, port)
|
109
|
+
@log.info("#{host}:#{port} removed from the list")
|
110
|
+
@log.info("There are #{@remotes.all.count} remote nodes in the list")
|
111
|
+
end
|
112
|
+
|
113
|
+
def update(opts)
|
101
114
|
@remotes.all.each do |r|
|
102
115
|
uri = URI("#{r[:home]}remotes")
|
103
116
|
res = Http.new(uri).get
|
@@ -111,7 +124,7 @@ Available options:"
|
|
111
124
|
begin
|
112
125
|
json = JSON.parse(res.body)
|
113
126
|
rescue JSON::ParserError => e
|
114
|
-
|
127
|
+
remove(r[:host], r[:port])
|
115
128
|
@log.info("#{Rainbow(r[:host]).red} \"#{e.message}\": #{res.body}")
|
116
129
|
next
|
117
130
|
end
|
@@ -120,19 +133,35 @@ Available options:"
|
|
120
133
|
r[:port], json['score']['suffixes']
|
121
134
|
)
|
122
135
|
unless score.valid?
|
123
|
-
|
136
|
+
remove(r[:host], r[:port])
|
124
137
|
@log.info("#{Rainbow(r[:host]).red} invalid score")
|
125
138
|
next
|
126
139
|
end
|
140
|
+
if score.strength < Score::STRENGTH && !opts['ignore-score-weakness']
|
141
|
+
remove(r[:host], r[:port])
|
142
|
+
@log.info(
|
143
|
+
"#{Rainbow(r[:host]).red} score too weak: #{score.strength}"
|
144
|
+
)
|
145
|
+
next
|
146
|
+
end
|
127
147
|
@remotes.rescore(r[:host], r[:port], score.value)
|
128
148
|
json['all'].each do |s|
|
129
149
|
unless @remotes.exists?(s['host'], s['port'])
|
130
|
-
|
150
|
+
add(s['host'], s['port'])
|
131
151
|
end
|
132
152
|
end
|
133
153
|
@log.info("#{r[:host]}:#{r[:port]}: #{Rainbow(score.value).green} \
|
134
154
|
(v.#{json['version']})")
|
135
155
|
end
|
156
|
+
total = @remotes.all.size
|
157
|
+
if total.zero?
|
158
|
+
@log.debug("The list of remotes is #{Rainbow('empty').red}!")
|
159
|
+
@log.debug(
|
160
|
+
"Run 'zold remote add b1.zold.io 80` and then `zold update`"
|
161
|
+
)
|
162
|
+
else
|
163
|
+
@log.debug("There are #{total} known remotes")
|
164
|
+
end
|
136
165
|
end
|
137
166
|
end
|
138
167
|
end
|
data/lib/zold/commands/show.rb
CHANGED
@@ -18,7 +18,10 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
19
|
# SOFTWARE.
|
20
20
|
|
21
|
-
|
21
|
+
require 'slop'
|
22
|
+
require_relative '../log'
|
23
|
+
require_relative '../id'
|
24
|
+
require_relative '../amount'
|
22
25
|
|
23
26
|
# SHOW command.
|
24
27
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -27,18 +30,40 @@ require_relative '../log.rb'
|
|
27
30
|
module Zold
|
28
31
|
# Show command
|
29
32
|
class Show
|
30
|
-
def initialize(
|
31
|
-
@
|
33
|
+
def initialize(wallets:, log: Log::Quiet.new)
|
34
|
+
@wallets = wallets
|
32
35
|
@log = log
|
33
36
|
end
|
34
37
|
|
35
|
-
def run(
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#{t[:amount]} #{t[:bnf]} #{t[:details]}")
|
38
|
+
def run(args = [])
|
39
|
+
opts = Slop.parse(args, help: true) do |o|
|
40
|
+
o.banner = "Usage: zold show [ID...] [options]
|
41
|
+
Available options:"
|
42
|
+
o.bool '--help', 'Print instructions'
|
41
43
|
end
|
44
|
+
if opts.help?
|
45
|
+
@log.info(opts.to_s)
|
46
|
+
return
|
47
|
+
end
|
48
|
+
if opts.arguments.empty?
|
49
|
+
require_relative 'list'
|
50
|
+
List.new(wallets: @wallets, log: @log).run(args)
|
51
|
+
else
|
52
|
+
total = Amount::ZERO
|
53
|
+
opts.arguments.each do |id|
|
54
|
+
total += show(@wallets.find(Id.new(id)), opts)
|
55
|
+
end
|
56
|
+
total
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def show(wallet, _)
|
61
|
+
balance = wallet.balance
|
62
|
+
wallet.txns.each do |t|
|
63
|
+
@log.info("##{t.id} #{t.date.utc.iso8601} \
|
64
|
+
#{t.amount} #{t.bnf} #{t.details}")
|
65
|
+
end
|
66
|
+
@log.info("The balance of #{wallet}: #{balance}")
|
42
67
|
balance
|
43
68
|
end
|
44
69
|
end
|
data/lib/zold/copies.rb
CHANGED
data/lib/zold/http.rb
CHANGED
data/lib/zold/key.rb
CHANGED
@@ -29,17 +29,23 @@ module Zold
|
|
29
29
|
# A key
|
30
30
|
class Key
|
31
31
|
def initialize(file: nil, text: nil)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
@body = lambda do
|
33
|
+
unless file.nil?
|
34
|
+
path = File.expand_path(file)
|
35
|
+
unless File.exist?(path)
|
36
|
+
raise "Can't find RSA key at #{file} (#{path})"
|
37
|
+
end
|
38
|
+
return File.read(path)
|
39
|
+
end
|
40
|
+
unless text.nil?
|
41
|
+
return [
|
42
|
+
'-----BEGIN PUBLIC KEY-----',
|
43
|
+
text.gsub(/(?<=\G.{64})/, "\n"),
|
44
|
+
'-----END PUBLIC KEY-----'
|
45
|
+
].join("\n")
|
46
|
+
end
|
47
|
+
raise 'Either file or text must be set'
|
36
48
|
end
|
37
|
-
return if text.nil?
|
38
|
-
@body = [
|
39
|
-
'-----BEGIN PUBLIC KEY-----',
|
40
|
-
text.gsub(/(?<=\G.{64})/, "\n"),
|
41
|
-
'-----END PUBLIC KEY-----'
|
42
|
-
].join("\n")
|
43
49
|
end
|
44
50
|
|
45
51
|
def to_s
|
@@ -61,7 +67,7 @@ module Zold
|
|
61
67
|
private
|
62
68
|
|
63
69
|
def rsa
|
64
|
-
text = @body.strip
|
70
|
+
text = @body.call.strip
|
65
71
|
unless text.start_with?('-----BEGIN')
|
66
72
|
text = OpenSSHKeyConverter.decode_pubkey(text.split[1])
|
67
73
|
end
|
data/lib/zold/node/farm.rb
CHANGED
@@ -39,6 +39,14 @@ module Zold
|
|
39
39
|
@semaphore = Mutex.new
|
40
40
|
end
|
41
41
|
|
42
|
+
def to_json
|
43
|
+
{
|
44
|
+
threads: @threads.count,
|
45
|
+
scores: @scores.size,
|
46
|
+
best: @best.count
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
42
50
|
def start(host, port, strength: 8, threads: 8)
|
43
51
|
@log.debug('Zero-threads farm won\'t score anything!') if threads.zero?
|
44
52
|
@scores = Queue.new
|
data/lib/zold/node/front.rb
CHANGED
@@ -47,8 +47,9 @@ module Zold
|
|
47
47
|
configure do
|
48
48
|
set :bind, '0.0.0.0'
|
49
49
|
set :logging, true
|
50
|
+
set :dump_errors, true
|
50
51
|
set :start, Time.now
|
51
|
-
set :lock,
|
52
|
+
set :lock, true
|
52
53
|
set :log, Log.new
|
53
54
|
set :show_exceptions, false
|
54
55
|
set :home, Dir.pwd
|
@@ -59,7 +60,9 @@ module Zold
|
|
59
60
|
before do
|
60
61
|
if request.env[Http::SCORE_HEADER]
|
61
62
|
s = Score.parse(request.env[Http::SCORE_HEADER])
|
62
|
-
|
63
|
+
error(400, 'The score is invalid') unless s.valid?
|
64
|
+
error(400, 'The score is too small') if s.value < 3
|
65
|
+
error(400, 'The score is weak') if s.strength < Score::STRENGTH
|
63
66
|
settings.remotes.add(s.host, s.port)
|
64
67
|
end
|
65
68
|
end
|
@@ -93,6 +96,7 @@ module Zold
|
|
93
96
|
wallets: {
|
94
97
|
total: wallets.all.count
|
95
98
|
},
|
99
|
+
farm: settings.farm.to_json,
|
96
100
|
date: `date --iso-8601=seconds -u`.strip,
|
97
101
|
age: Time.now - settings.start,
|
98
102
|
home: 'https://www.zold.io'
|
@@ -117,7 +121,7 @@ module Zold
|
|
117
121
|
request.body.rewind
|
118
122
|
cps = copies(id)
|
119
123
|
cps.add(request.body.read, 'remote', Remotes::PORT, 0)
|
120
|
-
Zold::Merge.new(
|
124
|
+
Zold::Merge.new(wallets: wallets, copies: cps.root).run([id.to_s])
|
121
125
|
"Success, #{wallet.id} balance is #{wallet.balance}"
|
122
126
|
end
|
123
127
|
|
@@ -141,6 +145,12 @@ module Zold
|
|
141
145
|
'Page not found'
|
142
146
|
end
|
143
147
|
|
148
|
+
error 400 do
|
149
|
+
status 400
|
150
|
+
content_type 'text/plain'
|
151
|
+
env['sinatra.error'].message
|
152
|
+
end
|
153
|
+
|
144
154
|
error do
|
145
155
|
status 503
|
146
156
|
e = env['sinatra.error']
|
data/lib/zold/patch.rb
CHANGED
@@ -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_relative 'wallet
|
21
|
+
require_relative 'wallet'
|
22
22
|
|
23
23
|
# Patch.
|
24
24
|
#
|
@@ -35,15 +35,15 @@ module Zold
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def join(wallet)
|
38
|
-
negative = @txns.select { |t| t
|
39
|
-
max = negative.empty? ? 0 : negative.max_by
|
38
|
+
negative = @txns.select { |t| t.amount.negative? }
|
39
|
+
max = negative.empty? ? 0 : negative.max_by(&:id).id
|
40
40
|
wallet.txns.each do |txn|
|
41
|
-
next if @txns.find { |t| t
|
41
|
+
next if @txns.find { |t| t == txn }
|
42
42
|
next if
|
43
|
-
txn
|
44
|
-
(txn
|
45
|
-
@txns.find { |t| t
|
46
|
-
@txns.map
|
43
|
+
txn.amount.negative? && !@txns.empty? &&
|
44
|
+
(txn.id <= max ||
|
45
|
+
@txns.find { |t| t.id == txn.id } ||
|
46
|
+
@txns.map(&:amount).inject(&:+) < txn.amount)
|
47
47
|
next unless Signature.new.valid?(@key, txn)
|
48
48
|
@txns << txn
|
49
49
|
end
|
@@ -0,0 +1,53 @@
|
|
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_relative 'key'
|
22
|
+
|
23
|
+
# Payment prefixes.
|
24
|
+
#
|
25
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
26
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
27
|
+
# License:: MIT
|
28
|
+
module Zold
|
29
|
+
# Payment prefixes
|
30
|
+
class Prefixes
|
31
|
+
def initialize(wallet)
|
32
|
+
@wallet = wallet
|
33
|
+
end
|
34
|
+
|
35
|
+
def create(length)
|
36
|
+
raise "Length #{length} is too small" if length < 8
|
37
|
+
raise "Length #{length} is too big" if length > 32
|
38
|
+
key = body
|
39
|
+
start = Random.new.rand(key.length - length)
|
40
|
+
key[start..(start + length - 1)]
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid?(prefix)
|
44
|
+
body.include?(prefix)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def body
|
50
|
+
@wallet.key.to_pub.gsub(/[^A-Z0-9a-z]/, '')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/zold/remotes.rb
CHANGED
@@ -60,6 +60,9 @@ module Zold
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def add(host, port = Remotes::PORT)
|
63
|
+
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
64
|
+
raise 'Port can\'t be negative' if port < 0
|
65
|
+
raise 'Port can\'t be over 65536' if port > 0xffff
|
63
66
|
list = load
|
64
67
|
list << { host: host, port: port, score: 0 }
|
65
68
|
list.uniq! { |r| "#{r[:host]}:#{r[:port]}" }
|
@@ -67,16 +70,19 @@ module Zold
|
|
67
70
|
end
|
68
71
|
|
69
72
|
def remove(host, port = Remotes::PORT)
|
73
|
+
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
70
74
|
list = load
|
71
75
|
list.reject! { |r| r[:host] == host && r[:port] == port }
|
72
76
|
save(list)
|
73
77
|
end
|
74
78
|
|
75
79
|
def score(host, port = Remotes::PORT)
|
80
|
+
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
76
81
|
load.find { |r| r[:host] == host && r[:port] == port }[:score]
|
77
82
|
end
|
78
83
|
|
79
84
|
def rescore(host, port, score)
|
85
|
+
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
80
86
|
list = load
|
81
87
|
list.find do |r|
|
82
88
|
r[:host] == host && r[:port] == port
|