zold 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +1 -0
- data/README.md +0 -38
- data/bin/zold +2 -12
- data/fixtures/scripts/calculate-scores.sh +8 -0
- data/fixtures/scripts/print-helps.sh +1 -1
- data/lib/zold/commands/calculate.rb +84 -0
- data/lib/zold/commands/push.rb +4 -0
- data/lib/zold/score.rb +16 -10
- data/lib/zold/signature.rb +1 -1
- data/lib/zold/version.rb +1 -1
- data/test/commands/test_calculate.rb +37 -0
- data/test/test_score.rb +8 -0
- data/wp/.gitignore +1 -0
- data/wp/logo.png +0 -0
- data/wp/wp.tex +147 -45
- data/zold.gemspec +1 -1
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2005a5319083f1a638f7c4764776cf02c22fec87
|
4
|
+
data.tar.gz: 9ffb162b8a5c5daa797b0c1402283d49a81f5d26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39f357589c34b08f98d405b8ee8307a8044c5809e51fce67a00e87ce53d713be7179ba2098ee548e4b7b9788aa276e9b40ebe271c4ade47cc7917d859a08079a
|
7
|
+
data.tar.gz: 18edefa475f83289160649fbfdd07df40e7c494722f5c5352650d3ae1e16d945ea269d430294a9e61e8f6f4d202c1f3efe7c66b4f72b0657dac7a9434e73e2ea
|
data/.gitattributes
CHANGED
data/README.md
CHANGED
@@ -45,8 +45,6 @@ yet, you can run:
|
|
45
45
|
$ ssh-keygen -t rsa -b 4096
|
46
46
|
```
|
47
47
|
|
48
|
-
## Operations
|
49
|
-
|
50
48
|
### Remote
|
51
49
|
|
52
50
|
Each node maintains a list of visible "remote" nodes.
|
@@ -157,42 +155,6 @@ Each HTTP response contains `Content-type` header.
|
|
157
155
|
|
158
156
|
## Files
|
159
157
|
|
160
|
-
Each wallet is a text file with the name equal to the wallet ID, for example:
|
161
|
-
|
162
|
-
```text
|
163
|
-
12345678abcdef
|
164
|
-
AAAAB3NzaC1yc2EAAAADAQABAAABAQCuLuVr4Tl2sXoN5Zb7b6SKMPrVjLxb...
|
165
|
-
|
166
|
-
34;2017-07-19T21:24:51Z;-560700;98bb82c81735c4ee;for services;SKMPrVjLxbM5oDm0IhniQQy3shF...
|
167
|
-
35;2017-07-19T21:25:07Z;-56990;98bb82c81735c4ee;;QCuLuVr4Tl2sXoN5Zb7b6SKMPrVjLxb...
|
168
|
-
134;2017-07-19T21:29:11Z;647388;18bb82dd1735b6e9;;
|
169
|
-
36;2017-07-19T22:18:43Z;-884733;38ab8fc8e735c4fc;for fun;2sXoN5Zb7b6SKMPrVjLxb7b6SKMPrVjLx...
|
170
|
-
```
|
171
|
-
|
172
|
-
Lines are separated by either CR or CRLF, doesn't matter. There is a
|
173
|
-
header and a ledger, separated by an empty line.
|
174
|
-
The header includes two lines:
|
175
|
-
|
176
|
-
* Wallet ID, a 64-bit unsigned integer;
|
177
|
-
* Public RSA key of the wallet owner.
|
178
|
-
|
179
|
-
The ledger includes transactions, one per line. Each transaction line
|
180
|
-
contains fields separated by a semi-colon:
|
181
|
-
|
182
|
-
* Transaction ID, an unsigned 16-bit integer;
|
183
|
-
* Date and time, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601);
|
184
|
-
* Amount (integer);
|
185
|
-
* Wallet ID of the beneficiary;
|
186
|
-
* Details: `/[a-zA-Z0-9 -.]{1,128}/`;
|
187
|
-
* RSA signature of the sender of "ID;amount;beneficiary;details" text.
|
188
|
-
|
189
|
-
Transactions with positive amount don't
|
190
|
-
have RSA signatures. Their IDs point to ID fields of corresponding
|
191
|
-
beneficiaries' wallets.
|
192
|
-
|
193
|
-
The combination "ID+Beneficiary" is unique in the entire wallet.
|
194
|
-
|
195
|
-
The directory `.zold` is automatically created and contains system data.
|
196
158
|
|
197
159
|
`.zold/remotes` is a comma-separated file with a list of remote nodes with
|
198
160
|
these columns:
|
data/bin/zold
CHANGED
@@ -167,18 +167,8 @@ Available options:"
|
|
167
167
|
require_relative '../lib/zold/commands/push'
|
168
168
|
Zold::Push.new(wallets: wallets, remotes: remotes, log: log).run(args)
|
169
169
|
when 'score'
|
170
|
-
require_relative '../lib/zold/
|
171
|
-
|
172
|
-
host = args[0]
|
173
|
-
port = args[1].to_i
|
174
|
-
invoice = args[2]
|
175
|
-
strength = args[3].to_i
|
176
|
-
raise "Invalid strength: #{strength}" if strength <= 0 || strength > 8
|
177
|
-
score = Zold::Score.new(Time.now, host, port, invoice, strength: strength)
|
178
|
-
loop do
|
179
|
-
log.info(score.to_s)
|
180
|
-
score = score.next
|
181
|
-
end
|
170
|
+
require_relative '../lib/zold/commands/calculate'
|
171
|
+
Zold::Calculate.new(log: log).run(args)
|
182
172
|
else
|
183
173
|
raise "Command '#{command}' is not supported"
|
184
174
|
end
|
@@ -0,0 +1,84 @@
|
|
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 'slop'
|
22
|
+
require_relative '../log'
|
23
|
+
require_relative '../score'
|
24
|
+
|
25
|
+
# SCORE command.
|
26
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
27
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
28
|
+
# License:: MIT
|
29
|
+
module Zold
|
30
|
+
# Calculate score
|
31
|
+
class Calculate
|
32
|
+
def initialize(log: Log::Quiet.new)
|
33
|
+
@log = log
|
34
|
+
end
|
35
|
+
|
36
|
+
def run(args = [])
|
37
|
+
opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
|
38
|
+
o.banner = "Usage: zold push [ID...] [options]
|
39
|
+
Available options:"
|
40
|
+
o.string '--invoice',
|
41
|
+
'The invoice you want to collect money to'
|
42
|
+
o.integer '--port',
|
43
|
+
"TCP port to open for the Net (default: #{Remotes::PORT})",
|
44
|
+
default: Remotes::PORT
|
45
|
+
o.string '--host', 'Host name (default: 127.0.0.1)',
|
46
|
+
default: '127.0.0.1'
|
47
|
+
o.integer '--strength',
|
48
|
+
"The strength of the score (default: #{Score::STRENGTH})",
|
49
|
+
default: Score::STRENGTH
|
50
|
+
o.integer '--max',
|
51
|
+
'Maximum value to find and then stop (default: 8)',
|
52
|
+
default: 8
|
53
|
+
o.bool '--hide-hash', 'Don\'t print hash',
|
54
|
+
default: false
|
55
|
+
o.bool '--help', 'Print instructions'
|
56
|
+
end
|
57
|
+
if opts.help?
|
58
|
+
@log.info(opts.to_s)
|
59
|
+
return
|
60
|
+
end
|
61
|
+
calculate(opts)
|
62
|
+
end
|
63
|
+
|
64
|
+
def calculate(opts)
|
65
|
+
start = Time.now
|
66
|
+
strength = opts[:strength]
|
67
|
+
raise "Invalid strength: #{strength}" if strength <= 0 || strength > 8
|
68
|
+
score = Zold::Score.new(
|
69
|
+
Time.now, opts[:host], opts[:port].to_i,
|
70
|
+
opts[:invoice], strength: strength
|
71
|
+
)
|
72
|
+
loop do
|
73
|
+
msg = score.to_s
|
74
|
+
msg += (score.value > 0 ? ' ' + score.hash : '') unless opts['hide-hash']
|
75
|
+
@log.info(msg)
|
76
|
+
break if score.value >= opts[:max].to_i
|
77
|
+
score = score.next
|
78
|
+
end
|
79
|
+
seconds = (Time.now - start).round(2)
|
80
|
+
@log.info("Took #{seconds} seconds, #{seconds / score.value} per value")
|
81
|
+
score
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/zold/commands/push.rb
CHANGED
@@ -66,20 +66,24 @@ Available options:"
|
|
66
66
|
next
|
67
67
|
end
|
68
68
|
unless response.code == '200'
|
69
|
+
@remotes.error(r[:host], r[:port])
|
69
70
|
@log.error("#{uri} failed as #{response.code}/#{response.message}")
|
70
71
|
next
|
71
72
|
end
|
72
73
|
json = JSON.parse(response.body)['score']
|
73
74
|
score = Score.parse_json(json)
|
74
75
|
unless score.valid?
|
76
|
+
@remotes.error(r[:host], r[:port])
|
75
77
|
@log.error("#{uri} invalid score: #{score}")
|
76
78
|
next
|
77
79
|
end
|
78
80
|
if score.expired?
|
81
|
+
@remotes.error(r[:host], r[:port])
|
79
82
|
@log.error("#{uri} expired score: #{score}")
|
80
83
|
next
|
81
84
|
end
|
82
85
|
if score.strength < Score::STRENGTH
|
86
|
+
@remotes.error(r[:host], r[:port])
|
83
87
|
@log.error("#{uri} score is too weak")
|
84
88
|
next
|
85
89
|
end
|
data/lib/zold/score.rb
CHANGED
@@ -96,13 +96,20 @@ module Zold
|
|
96
96
|
)
|
97
97
|
end
|
98
98
|
|
99
|
+
def hash
|
100
|
+
raise 'Score has zero value, there is no hash' if @suffixes.empty?
|
101
|
+
@suffixes.reduce(prefix) do |pfx, suffix|
|
102
|
+
Digest::SHA256.hexdigest(pfx + ' ' + suffix)[0, 63]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
99
106
|
def to_text
|
100
|
-
|
107
|
+
pfx, bnf = @invoice.split('@')
|
101
108
|
[
|
102
109
|
@time.to_i.to_s(16),
|
103
110
|
@host,
|
104
111
|
@port.to_s(16),
|
105
|
-
|
112
|
+
pfx,
|
106
113
|
bnf,
|
107
114
|
@suffixes.join(' ')
|
108
115
|
].join(' ')
|
@@ -127,7 +134,8 @@ module Zold
|
|
127
134
|
invoice: @invoice,
|
128
135
|
time: @time.utc.iso8601,
|
129
136
|
suffixes: @suffixes,
|
130
|
-
strength: @strength
|
137
|
+
strength: @strength,
|
138
|
+
hash: value.zero? ? nil : hash
|
131
139
|
}
|
132
140
|
end
|
133
141
|
|
@@ -160,14 +168,12 @@ module Zold
|
|
160
168
|
@time < Time.now - 24 * 60
|
161
169
|
end
|
162
170
|
|
171
|
+
def prefix
|
172
|
+
"#{@time.utc.iso8601} #{@host} #{@port} #{@invoice}"
|
173
|
+
end
|
174
|
+
|
163
175
|
def valid?
|
164
|
-
|
165
|
-
@suffixes.reduce(start) do |prefix, suffix|
|
166
|
-
hex = Digest::SHA256.hexdigest(prefix + ' ' + suffix)
|
167
|
-
return false unless hex.end_with?('0' * @strength)
|
168
|
-
hex[0, 63]
|
169
|
-
end
|
170
|
-
true
|
176
|
+
@suffixes.empty? || hash.end_with?('0' * @strength)
|
171
177
|
end
|
172
178
|
|
173
179
|
def value
|
data/lib/zold/signature.rb
CHANGED
data/lib/zold/version.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
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 'minitest/autorun'
|
22
|
+
require_relative '../../lib/zold/log'
|
23
|
+
require_relative '../../lib/zold/commands/calculate'
|
24
|
+
|
25
|
+
# SCORE test.
|
26
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
27
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
28
|
+
# License:: MIT
|
29
|
+
class TestCalculate < Minitest::Test
|
30
|
+
def test_calculates_score
|
31
|
+
score = Zold::Calculate.new.run(
|
32
|
+
['score', '--strength=2', '--max=8', '--invoice=NOSUFFIX@ffffffffffffffff']
|
33
|
+
)
|
34
|
+
assert(score.valid?)
|
35
|
+
assert_equal(8, score.value)
|
36
|
+
end
|
37
|
+
end
|
data/test/test_score.rb
CHANGED
@@ -95,4 +95,12 @@ class TestScore < Minitest::Test
|
|
95
95
|
assert(score.valid?)
|
96
96
|
assert(!score.expired?)
|
97
97
|
end
|
98
|
+
|
99
|
+
def test_correct_number_of_zeroes
|
100
|
+
score = Zold::Score.new(
|
101
|
+
Time.now, 'localhost', 443,
|
102
|
+
'NOPREFIX@ffffffffffffffff', strength: 3
|
103
|
+
).next
|
104
|
+
assert(score.hash.end_with?('000'))
|
105
|
+
end
|
98
106
|
end
|
data/wp/.gitignore
CHANGED
data/wp/logo.png
ADDED
Binary file
|
data/wp/wp.tex
CHANGED
@@ -1,22 +1,28 @@
|
|
1
1
|
\documentclass[11pt,oneside]{article}
|
2
2
|
\usepackage[utf8]{inputenc}
|
3
3
|
\usepackage[american]{babel}
|
4
|
-
% \usepackage[
|
5
|
-
% paperwidth=6in, paperheight=9in,
|
6
|
-
% bindingoffset=0.25in, left=0.75in, right=0.75in, top=0.75in, bottom=1.25in
|
7
|
-
% ]{geometry}
|
8
4
|
\usepackage{setspace}
|
9
5
|
\usepackage{microtype}
|
10
6
|
\usepackage{mathpazo} % Palantino font
|
7
|
+
\usepackage{mdframed}
|
11
8
|
\usepackage{minted}
|
12
|
-
\setminted{fontsize=\
|
9
|
+
\setminted{fontsize=\small}
|
13
10
|
\setminted{breaklines}
|
14
11
|
\usemintedstyle{bw}
|
12
|
+
\BeforeBeginEnvironment{minted}{\vspace{6pt}\begin{mdframed}[topline=false,rightline=false,bottomline=false,linecolor=black,linewidth=2pt]}
|
13
|
+
\AfterEndEnvironment{minted}{\end{mdframed}}
|
14
|
+
\usepackage{xcolor}
|
15
|
+
\usepackage{graphicx}
|
16
|
+
\newcommand\dd[1]{\colorbox{gray!30}{\texttt{#1}}}
|
15
17
|
\usepackage{hyperref}
|
18
|
+
\hypersetup{colorlinks=true,allcolors=blue!40!black}
|
16
19
|
\pagestyle{empty}
|
17
20
|
\setstretch{1.2}
|
21
|
+
\setlength{\topskip}{6pt}
|
22
|
+
\setlength{\parindent}{0pt} % indent first line
|
23
|
+
\setlength{\parskip}{6pt} % before par
|
18
24
|
|
19
|
-
\title{Zold
|
25
|
+
\title{\includegraphics[scale=0.05]{logo.png}\\Zold: Lightweight Crypto Currency}
|
20
26
|
\author{Yegor Bugayenko\\\texttt{yegor@zold.io}}
|
21
27
|
|
22
28
|
\begin{document}
|
@@ -49,69 +55,101 @@ of existing digital currencies.
|
|
49
55
|
|
50
56
|
\section{Principles}
|
51
57
|
|
52
|
-
\textbf{Open
|
53
|
-
Zold is a command line tool. Its entire code base is open source
|
58
|
+
\textbf{Open Source}.
|
59
|
+
Zold is a command line tool. Its entire code base is open source
|
60
|
+
and hosted at the GitHub \href{https://github.com/yegor256/zold}{yegor256/zold}
|
61
|
+
repository.
|
62
|
+
|
63
|
+
\textbf{No Trust}.
|
64
|
+
The network of communicating nodes maintains wallets of users.
|
65
|
+
Anyone can add a node to the network.
|
66
|
+
It is assumed that any node may contain corrupted data, either by mistake or intentionally.
|
67
|
+
|
68
|
+
\textbf{Proof of work}.
|
69
|
+
Each node, in order to earn trust, must invest its CPU power
|
70
|
+
and find hash suffixes, performing certain calculations.
|
71
|
+
|
72
|
+
\textbf{No General Ledger}.
|
73
|
+
There is no central ledger, each wallet has its own personal ledger.
|
74
|
+
All transactions in each ledger are confirmed by
|
75
|
+
\href{https://en.wikipedia.org/wiki/RSA_(cryptosystem)}{RSA signatures}
|
76
|
+
of their owners;
|
54
77
|
|
55
78
|
\textbf{Capacity}.
|
56
79
|
One currency unit is called ZLD.
|
57
|
-
One ZLD by convention equals to $2^24$ (16,777,216)
|
80
|
+
One ZLD by convention equals to $2^{24}$ \emph{zents} (16,777,216).
|
58
81
|
All amounts are stored as signed 64-bit integers.
|
59
82
|
Thus, the technical capacity of the currency is 549,755,813,888 ZLD (half a trillion).
|
60
83
|
|
61
|
-
\textbf{Zero
|
62
|
-
There is no mining, the only way to get ZLD is to receive it from someone else.
|
63
|
-
The wallet with the \
|
84
|
+
\textbf{Zero Wallet}.
|
85
|
+
There is no ``mining,'' the only way to get ZLD is to receive it from someone else.
|
86
|
+
The wallet with the \dd{0x00} ID belongs to the
|
64
87
|
issuer and may have a negative balance. All other wallets
|
65
|
-
may have
|
66
|
-
|
67
|
-
\textbf{No general ledger}.
|
68
|
-
There is no central ledger, each wallet has its own personal ledger.
|
69
|
-
Each transaction in the ledger is confirmed by RSA signature;
|
70
|
-
|
71
|
-
\textbf{No trust}.
|
72
|
-
The network of communicating nodes maintains wallets of users.
|
73
|
-
Anyone can add a node to the network.
|
74
|
-
It is assumed that any node may contain corrupted data, either by mistake or intentionally.
|
88
|
+
may only have positive balances.
|
75
89
|
|
76
90
|
\section{Proof of Work}
|
77
91
|
|
78
|
-
|
79
|
-
|
92
|
+
The system consists of nodes (server machines), which maintain the data.
|
93
|
+
In order to guarantee data consistency among all distributed nodes
|
94
|
+
there has to be an algorithm of data segregation.
|
95
|
+
Corrupted data must be detected earlier and filtered out as quickly as possible.
|
96
|
+
Bitcoin introduced such an algorithm and called it \emph{proof of work}.
|
97
|
+
|
98
|
+
Its fundamental principle is that each block of data must have a special
|
99
|
+
number attached to it, known as \emph{nonce}, which is rather difficult to calculate,
|
100
|
+
because it requires a lot of CPU power. It is assumed that at any moment
|
101
|
+
of time the majority of nodes in the network invest their CPU power into
|
102
|
+
calculating the nonces for the data that is not corrupted. If and when for any reason
|
103
|
+
some data gets corrupted, the amount of CPU power a part of the network
|
104
|
+
decides to invest into its nonces calculation would be smaller than what
|
105
|
+
the other part of the network invests into legal data. The latter part
|
106
|
+
will quickly dominate the former and the nodes with corrupted data will
|
107
|
+
be ostracized and eventually ignored.
|
108
|
+
|
109
|
+
Zold has borrowed this principle, although modified it. We also require
|
110
|
+
our nodes to invest their CPU power into meaninless and repetative
|
111
|
+
calculations just to help us identify which part of the network they belong to:
|
112
|
+
corrupted or not. Each Zold node has to calculate its \emph{trust score},
|
113
|
+
which is as big as much CPU power the node has invested into its calculation.
|
114
|
+
|
115
|
+
Similar to Bitcoin nonces we repetatively calculate cryptographic hashes,
|
116
|
+
looking for consecutive zeros inside them. First, in order to calculate a score,
|
117
|
+
a node makes the \emph{prefix}, which consists of four parts,
|
80
118
|
separated by spaces:
|
81
119
|
|
82
|
-
\begin{
|
83
|
-
\item The current timestamp in UTC ISO 8601,
|
84
|
-
\item The host name or IP address, e.g. \
|
85
|
-
\item The TCP port number,
|
120
|
+
\begin{enumerate}
|
121
|
+
\item The current timestamp in UTC, in \href{https://en.wikipedia.org/wiki/ISO_8601}{ISO 8601},
|
122
|
+
\item The host name or IP address, e.g. \dd{b2.zold.io},
|
123
|
+
\item The \href{https://en.wikipedia.org/wiki/Port_(computer_networking)}{TCP port} number,
|
86
124
|
\item The invoice.
|
87
|
-
\end{
|
125
|
+
\end{enumerate}
|
88
126
|
|
89
|
-
For example, the
|
127
|
+
For example, the prefix may look like this:
|
90
128
|
|
91
129
|
\begin{minted}{text}
|
92
|
-
|
130
|
+
2018-05-17T03:50:59Z b2.zold.io 4096 THdonv1E@0000000000000000
|
93
131
|
\end{minted}
|
94
132
|
|
95
|
-
Then,
|
96
|
-
|
97
|
-
|
98
|
-
in the hexadecimal format. For example, this would be the
|
99
|
-
with \
|
133
|
+
Then, the node attempts to append any arbitrary text, which has to match
|
134
|
+
\dd{/[a-zA-Z0-9]+/} regular expression, to the end of the prefix and calculates
|
135
|
+
\href{https://en.wikipedia.org/wiki/SHA-2}{SHA-256 hash}
|
136
|
+
of the text in the hexadecimal format. For example, this would be the prefix
|
137
|
+
with the attached \dd{117b1f} suffix:
|
100
138
|
|
101
139
|
\begin{minted}{text}
|
102
|
-
|
140
|
+
2018-05-17T03:50:59Z b2.zold.io 4096 THdonv1E@0000000000000000 117b1f
|
103
141
|
\end{minted}
|
104
142
|
|
105
|
-
The
|
143
|
+
The hash of this text will be:
|
106
144
|
|
107
145
|
\begin{minted}{text}
|
108
|
-
|
146
|
+
670baa704726fe2c837c5ca764202adca5ab12c9b90c94d9fb1b8d629000000
|
109
147
|
\end{minted}
|
110
148
|
|
111
149
|
The node attempts to try different sufficies until one of them produces
|
112
|
-
|
113
|
-
|
114
|
-
(it took
|
150
|
+
a hash that ends with a few tailing zeroes. The one above ends
|
151
|
+
with six zeroes
|
152
|
+
(it took three minutes to find it on 2.3GHz Intel Core i7):
|
115
153
|
|
116
154
|
When the first suffix is found, the score is 1. Then, to
|
117
155
|
increase the score by one, the next suffix has to be found, which
|
@@ -119,18 +157,82 @@ can be added to the first 64 characters of the previous hash
|
|
119
157
|
in order to obtain a new hash with trailing zeros, for example:
|
120
158
|
|
121
159
|
\begin{minted}{text}
|
122
|
-
|
160
|
+
2018-05-17T03:50:59Z b2.zold.io 4096 THdonv1E@0000000000000000 117b1f 1546e35
|
161
|
+
\end{minted}
|
162
|
+
|
163
|
+
Produces:
|
164
|
+
|
165
|
+
\begin{minted}{text}
|
166
|
+
99dcd18e4bd03004e1205437866b5b68035cc8985240ae52cbd37640a000000
|
123
167
|
\end{minted}
|
124
168
|
|
125
169
|
And so on.
|
126
170
|
|
127
171
|
The score is valid only when the starting time point is earlier than
|
128
|
-
current time, but not earlier than 24 hours ago. The strength of the score
|
172
|
+
the current time, but not earlier than 24 hours ago. The strength of the score
|
129
173
|
is the amount of the trailing zeros in the hash. In the example above the
|
130
|
-
strength
|
174
|
+
strength is six.
|
131
175
|
|
132
176
|
\section{Wallets}
|
133
177
|
|
178
|
+
There is no central ledger in Zold.
|
179
|
+
Each user has \emph{wallets} (any number of them).
|
180
|
+
Each wallet is an ASCII-text file with the name equal to the wallet ID.
|
181
|
+
For example, the wallet in the file \dd{12345678abcdef} may include:
|
182
|
+
|
183
|
+
\begin{minted}{text}
|
184
|
+
12345678abcdef
|
185
|
+
AAAAB3NzaC1yc2EAAAADAQABAAABAQCuLuVr4Tl2sXoN5Zb7b6SKMPrVjLxb...
|
186
|
+
|
187
|
+
34;2017-07-19T21:24:51Z;-560700;Ui0wpLu7;98bb82c81735c4ee;For services;SKMPrVjLxbM5oDm0IhniQQy3shF...
|
188
|
+
35;2017-07-19T21:25:07Z;-56990;xksQuJa9;98bb82c81735c4ee;For food;QCuLuVr4Tl2sXoN5Zb7b6SKMPrVjLxb...
|
189
|
+
134;2017-07-19T21:29:11Z;647388;kkIZo09s;18bb82dd1735b6e9;-;
|
190
|
+
36;2017-07-19T22:18:43Z;-884733;pplIe28s;38ab8fc8e735c4fc;For programming;2sXoN5Zb7b6SKMPrVjLxb7b6SKMPrVjLx...
|
191
|
+
\end{minted}
|
192
|
+
|
193
|
+
Lines are separated by either CR or CRLF.
|
194
|
+
There is a header and a ledger, separated by an empty line.
|
195
|
+
The header includes two lines:
|
196
|
+
|
197
|
+
\begin{enumerate}
|
198
|
+
\item Wallet ID, a 64-bit unsigned integer in hexadecimal format;
|
199
|
+
\item Public \href{https://en.wikipedia.org/wiki/RSA_(cryptosystem)}{RSA}
|
200
|
+
key of the wallet owner, in \href{https://en.wikipedia.org/wiki/Base64}{Base64}.
|
201
|
+
\end{enumerate}
|
202
|
+
|
203
|
+
The ledger includes transactions, one per line. Each transaction line
|
204
|
+
contains fields separated by a semi-colon:
|
205
|
+
|
206
|
+
\begin{enumerate}
|
207
|
+
\item Transaction ID, an unsigned 16-bit integer;
|
208
|
+
\item Date and time, in \href{https://en.wikipedia.org/wiki/ISO_8601}{ISO 8601} format;
|
209
|
+
\item Amount, a signed 64-bit integer;
|
210
|
+
\item Payment prefix, 8-32 symbols;
|
211
|
+
\item Wallet ID of the beneficiary;
|
212
|
+
\item Details, matching \dd{/[a-zA-Z0-9 -.]\{1,128\}/};
|
213
|
+
\item \href{https://en.wikipedia.org/wiki/RSA_(cryptosystem)}{RSA} signature,
|
214
|
+
684 symbols in \href{https://en.wikipedia.org/wiki/Base64}{Base64}.
|
215
|
+
\end{enumerate}
|
216
|
+
|
217
|
+
Transactions with positive amount don't have signatures.
|
218
|
+
Their IDs point to ID fields of corresponding beneficiaries' wallets.
|
219
|
+
|
220
|
+
The combination \dd{ID+Beneficiary} is unique in the entire wallet.
|
221
|
+
|
222
|
+
The \href{https://en.wikipedia.org/wiki/RSA_(cryptosystem)}{RSA}
|
223
|
+
signature is calculated using the private RSA key of the
|
224
|
+
wallet and the following fields of transaction, separated by spaces:
|
225
|
+
|
226
|
+
\begin{enumerate}
|
227
|
+
\item Wallet ID;
|
228
|
+
\item Transaction ID, an unsigned 16-bit integer;
|
229
|
+
\item Date and time, in ISO 8601 format;
|
230
|
+
\item Amount, a signed 64-bit integer;
|
231
|
+
\item Payment prefix;
|
232
|
+
\item Wallet ID of the beneficiary;
|
233
|
+
\item Details, matching \dd{/[a-zA-Z0-9 -.]{1,128}/}.
|
234
|
+
\end{enumerate}
|
235
|
+
|
134
236
|
\section{Push}
|
135
237
|
|
136
238
|
\section{Fetch and Merge}
|
data/zold.gemspec
CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.homepage = 'http://github.com/zerocracy/zold'
|
40
40
|
s.files = `git ls-files`.split($RS)
|
41
41
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
42
|
-
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
42
|
+
s.test_files = s.files.grep(%r{^(test|spec|features|wp)/})
|
43
43
|
s.rdoc_options = ['--charset=UTF-8']
|
44
44
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
45
45
|
s.add_runtime_dependency 'concurrent-ruby', '1.0.5'
|
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.6.
|
4
|
+
version: 0.6.3
|
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-05-
|
11
|
+
date: 2018-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -271,10 +271,12 @@ files:
|
|
271
271
|
- fixtures/keys/1.pub
|
272
272
|
- fixtures/keys/2
|
273
273
|
- fixtures/keys/2.pub
|
274
|
+
- fixtures/scripts/calculate-scores.sh
|
274
275
|
- fixtures/scripts/print-helps.sh
|
275
276
|
- fixtures/scripts/push-and-pull.sh
|
276
277
|
- lib/zold.rb
|
277
278
|
- lib/zold/amount.rb
|
279
|
+
- lib/zold/commands/calculate.rb
|
278
280
|
- lib/zold/commands/clean.rb
|
279
281
|
- lib/zold/commands/create.rb
|
280
282
|
- lib/zold/commands/diff.rb
|
@@ -308,6 +310,7 @@ files:
|
|
308
310
|
- lib/zold/wallet.rb
|
309
311
|
- lib/zold/wallets.rb
|
310
312
|
- resources/remotes
|
313
|
+
- test/commands/test_calculate.rb
|
311
314
|
- test/commands/test_clean.rb
|
312
315
|
- test/commands/test_create.rb
|
313
316
|
- test/commands/test_diff.rb
|
@@ -341,6 +344,7 @@ files:
|
|
341
344
|
- test/test_zold.rb
|
342
345
|
- wp/.gitignore
|
343
346
|
- wp/Makefile
|
347
|
+
- wp/logo.png
|
344
348
|
- wp/wp.tex
|
345
349
|
- zold.gemspec
|
346
350
|
homepage: http://github.com/zerocracy/zold
|
@@ -373,6 +377,7 @@ test_files:
|
|
373
377
|
- features/gem_package.feature
|
374
378
|
- features/step_definitions/steps.rb
|
375
379
|
- features/support/env.rb
|
380
|
+
- test/commands/test_calculate.rb
|
376
381
|
- test/commands/test_clean.rb
|
377
382
|
- test/commands/test_create.rb
|
378
383
|
- test/commands/test_diff.rb
|
@@ -404,3 +409,7 @@ test_files:
|
|
404
409
|
- test/test_wallet.rb
|
405
410
|
- test/test_wallets.rb
|
406
411
|
- test/test_zold.rb
|
412
|
+
- wp/.gitignore
|
413
|
+
- wp/Makefile
|
414
|
+
- wp/logo.png
|
415
|
+
- wp/wp.tex
|