zold 0.6.2 → 0.6.3

checksums.yaml CHANGED
 @@ -1,7 +1,7 @@ 1 1 --- 2 2 SHA1: 3 - metadata.gz: c34164f9da323f0a19a831a982d4904b2c3eaffd 4 - data.tar.gz: 77d4935724eab082b127abdc5505be79d02fe6e8 3 + metadata.gz: 2005a5319083f1a638f7c4764776cf02c22fec87 4 + data.tar.gz: 9ffb162b8a5c5daa797b0c1402283d49a81f5d26 5 5 SHA512: 6 - metadata.gz: fa900d58a11e33b2f8855b62a7bd8503a39c68d1cdd3a270ad85defac1529fb120c38b3be8437513120911a0ec6725d8d23749503c92bd0ac8809dd8aa1767ac 7 - data.tar.gz: 9d73f149efa4a79c9acf55edec7caeb5c12613ec115310a5023ec962fd7786e6975b54c1070965bb21bc7484ff4ad01cb1a1e63ac79bd2a8c2895a4a29331e38 6 + metadata.gz: 39f357589c34b08f98d405b8ee8307a8044c5809e51fce67a00e87ce53d713be7179ba2098ee548e4b7b9788aa276e9b40ebe271c4ade47cc7917d859a08079a 7 + data.tar.gz: 18edefa475f83289160649fbfdd07df40e7c494722f5c5352650d3ae1e16d945ea269d430294a9e61e8f6f4d202c1f3efe7c66b4f72b0657dac7a9434e73e2ea
data/.gitattributes CHANGED
 @@ -5,3 +5,4 @@ 5 5 * text=auto eol=lf 6 6 *.rb ident 7 7 *.xml ident 8 + *.png binary
 @@ -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/score' 171 - raise 'Exactly four args required: host, port, invoice, strength' if args.length != 4 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,8 @@ 1 + #!/bin/bash 2 + set -x 3 + set -e 4 + shopt -s expand_aliases 5 + 6 + alias zold="$1 --ignore-global-config --trace" 7 + 8 + zold score --host=zold.io --port=4096 --invoice=NOSUFFIX@ffffffffffffffff --strength=2 --max=5
 @@ -3,7 +3,7 @@ set -x 3 3 set -e 4 4 shopt -s expand_aliases 5 5 6 - alias zold="$1" 6 + alias zold="$1 --ignore-global-config --trace" 7 7 8 8 zold --help 9 9 declare -a commands=(node create invoice remote pay show fetch clean diff merge propagate pull push)
 @@ -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
 @@ -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 - prefix, bnf = @invoice.split('@') 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 - prefix, 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 - start = "#{@time.utc.iso8601} #{@host} #{@port} #{@invoice}" 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
 @@ -42,7 +42,7 @@ module Zold 42 42 private 43 43 44 44 def body(id, t) 45 - [id, t.id, t.amount.to_i, t.prefix, t.bnf, t.details].join(' ') 45 + [id, t.id, t.date.utc.iso8601, t.amount.to_i, t.prefix, t.bnf, t.details].join(' ') 46 46 end 47 47 end 48 48 end
data/lib/zold/version.rb CHANGED
 @@ -23,5 +23,5 @@ 23 23 # Copyright:: Copyright (c) 2018 Yegor Bugayenko 24 24 # License:: MIT 25 25 module Zold 26 - VERSION = '0.6.2'.freeze 26 + VERSION = '0.6.3'.freeze 27 27 end
 @@ -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
 @@ -1,3 +1,4 @@ 1 + _minted-wp 1 2 wp.aux 2 3 wp.bcf 3 4 wp.log
 @@ -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=\footnotesize} 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, Lightweight Crypto Currency} 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 source}. 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) \emph{zents}. 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 wallet}. 62 - There is no mining, the only way to get ZLD is to receive it from someone else. 63 - The wallet with the \texttt{0x00} ID belongs to 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 only positive balances. 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 - Each node calculates its own score. 79 - First, it builds the initial text body, which consists of four parts, 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{itemize} 83 - \item The current timestamp in UTC ISO 8601, 84 - \item The host name or IP address, e.g. \texttf{b2.zold.io}, 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{itemize} 125 + \end{enumerate} 88 126 89 - For example, the body may look like this: 127 + For example, the prefix may look like this: 90 128 91 129 \begin{minted}{text} 92 - 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 130 + 2018-05-17T03:50:59Z b2.zold.io 4096 THdonv1E@0000000000000000 93 131 \end{minted} 94 132 95 - Then, it attempts to append any 96 - arbitrary text (has to match \texttt{[a-zA-Z0-9]+} regular expression) 97 - to the end of it and to calculate SHA-256 of the text 98 - in the hexadecimal format. For example, this would be the body 99 - with \texttt{abcdef} suffix: 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 - 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 abcdef 140 + 2018-05-17T03:50:59Z b2.zold.io 4096 THdonv1E@0000000000000000 117b1f 103 141 \end{minted} 104 142 105 - The SHA-256 of this body will be: 143 + The hash of this text will be: 106 144 107 145 \begin{minted}{text} 108 - 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 abcdef 146 + 670baa704726fe2c837c5ca764202adca5ab12c9b90c94d9fb1b8d629000000 109 147 \end{minted} 110 148 111 149 The node attempts to try different sufficies until one of them produces 112 - SHA-256 hash that ends with \texttt{000000} (six zeros). For example, this 113 - suffix \texttt{...} works 114 - (it took about an hour to find it on 2.3GHz Intel Core i7): 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 - 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 abcdef abcdef 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 was equal to six. 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}