zold 0.6 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 01a2323174e903dabb35708bfd1d0f756e930035
4
- data.tar.gz: a4f6582b8e645cd480ab74e374cc5cefe3698034
3
+ metadata.gz: 42d45258927844c4cc2c07418af8b5077787731b
4
+ data.tar.gz: 508255ebe906bd0c4dd9fcb88ed12dee32a1973a
5
5
  SHA512:
6
- metadata.gz: 8e6c51f38a8f4d44c86c6cf1d2c02d3db2a1495504613c29676922a91bc03b325524b87d592dabdeb406fc9ca6906725de8666b805ea98731a7fe4271c950123
7
- data.tar.gz: '0906447919a3f02950152613a65b19d648ca46cf480405659751ab6ab4eee1d08b01f8f1dfef2a60de4c5e55b17433913637daf37c0d7451f13e8320aa33c572'
6
+ metadata.gz: 4783b3ade202e80fb6b252ed58cab12176a0bcb70cc4584e67e3e6fe49f1ec9fcd2fdb1df0020f7a2d13b20e865e99c591a14604e402b8725acc9168a6365b2b
7
+ data.tar.gz: c8c189ee8908b4479b3b9e71f73776995ddcfc407cb2bdb6e32b15339c63bbdfc86a6f5a49845ad064bbd92cc1f50c2da557be6ae9d3a2673dbe478f4c80c0cc
@@ -6,7 +6,7 @@ AllCops:
6
6
  TargetRubyVersion: 2.2
7
7
 
8
8
  Metrics/CyclomaticComplexity:
9
- Max: 15
9
+ Max: 25
10
10
  Metrics/MethodLength:
11
11
  Enabled: false
12
12
  Layout/MultilineMethodCallIndentation:
@@ -17,13 +17,13 @@ Metrics/BlockLength:
17
17
  Max: 100
18
18
  Metrics/ClassLength:
19
19
  Max: 200
20
- Style/EndOfLine:
20
+ Layout/EndOfLine:
21
21
  EnforcedStyle: lf
22
22
  Metrics/ParameterLists:
23
23
  Max: 10
24
24
  Layout/AlignParameters:
25
25
  Enabled: false
26
26
  Metrics/PerceivedComplexity:
27
- Max: 15
27
+ Max: 25
28
28
  Metrics/LineLength:
29
29
  Max: 120
data/README.md CHANGED
@@ -17,21 +17,6 @@
17
17
  **NOTICE**: It's an experiment and a very early draft! Please, feel free to
18
18
  submit your ideas or pull requests.
19
19
 
20
- ZOLD principles include:
21
-
22
- * The entire code base is open source;
23
- * There is no mining, the only way to get ZOLD is to receive it from someone else;
24
- * Only 2<sup>63</sup> numerals (no fractions) can technically be issued;
25
- * The first wallet belongs to the issuer and may have a negative balance;
26
- * A wallet is a plain text file;
27
- * There is no central ledger, each wallet has its own personal ledger;
28
- * Each transaction in the ledger is confirmed by [RSA](https://simple.wikipedia.org/wiki/RSA_%28algorithm%29) encryption;
29
- * The network of communicating nodes maintains wallets of users;
30
- * Anyone can add a node to the network.
31
-
32
- 1 ZLD by convention equals to 2<sup>24</sup> (16,777,216) _zents_.
33
- Thus, the technical capacity of the currency is 549,755,813,888 ZLD (half a trillion).
34
-
35
20
  ## How to Use
36
21
 
37
22
  Install Ruby 2.2+, [Rubygems](https://rubygems.org/pages/download), and then run:
@@ -44,20 +29,9 @@ $ gem install zold
44
29
  Then, either run it as a node:
45
30
 
46
31
  ```bash
47
- $ zold start
32
+ $ zold node
48
33
  ```
49
34
 
50
- Or do one of the following:
51
-
52
- * `zold remote` manipulates the list off remote nodes;
53
- * `zold create` creates a new wallet (you have to provide PGP keys);
54
- * `zold fetch` downloads all copies of the wallet from the network;
55
- * `zold merge` merges all copies of the wallet into the local one;
56
- * `zold pull` first `fetch`, then `merge`;
57
- * `zold show` prints out all known details of a wallet (incl. its balance);
58
- * `zold pay` creates a new transaction;
59
- * `zold push` pushes a wallet to the network.
60
-
61
35
  For more options and commands just run:
62
36
 
63
37
  ```bash
@@ -71,60 +45,6 @@ yet, you can run:
71
45
  $ ssh-keygen -t rsa -b 4096
72
46
  ```
73
47
 
74
- ## Glossary
75
-
76
- **Node** is an HTTP server with a RESTful API, a maintainer of wallets
77
- and a command line Ruby gem [`zold`](https://rubygems.org/gems/zold).
78
-
79
- **Network** is a set of all nodes available online.
80
-
81
- **Score** is the amount of "hash sufficies" a node has at any given moment of time.
82
-
83
- **Wallet** is a text file with a ledger of all transactions inside.
84
-
85
- **Transaction** is a money transferring operation between two wallets.
86
-
87
- **MSS** (minimum summary score) is a summary of all scores required to trust a wallet.
88
-
89
- ## Score
90
-
91
- Each node calculates its own score. First, it takes the current timestamp
92
- in UTC [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601),
93
- for example `2017-07-19T21:24:51Z ` (with a trailing space). Then, it appends
94
- its own host name or IP address to it, space, TCP port number, and a space.
95
- Then, it attempts to append any
96
- arbitrary text (has to match `[a-zA-Z0-9]+`) to the end of it and to calculate SHA-256 of the text
97
- in the hexadecimal format, for example:
98
-
99
- ```
100
- Input: "2017-07-19T21:24:51Z b1.zold.io 4096 the-suffix"
101
- SHA-256: "eba36e52e1ee674d198f486e07c8496853ffc8879e7fe25329523177646a96a0"
102
- ```
103
-
104
- The node attempts to try different sufficies until one of them produces
105
- SHA-256 hash that ends with `0000000` (seven zeros). For example, this
106
- suffix `11edb424c` works (it took 212 minutes to find it on 2.3GHz Intel Core i7):
107
-
108
- ```
109
- Input: "2017-07-19T21:24:51Z b1.zold.io 4096 11edb424c"
110
- SHA-256: "34f48e0eee1ed12ad74cb39418f2f6e7442a776a7b6182697957650e00000000"
111
- ```
112
-
113
- When the first suffix is found, the score of the node is 1. Then, to
114
- increase the score by one, the node has to find the next suffix, which
115
- can be added to the first 20 characters of the previous hash
116
- in order to obtain a new hash with trailing zeros, for example:
117
-
118
- ```
119
- Input: "34f48e0eee1ed12ad74c "
120
- SHA-256: "..."
121
- ```
122
-
123
- And so on.
124
-
125
- The score is valid only when the starting time point is earlier than
126
- current time, but not earlier than 24 hours ago.
127
-
128
48
  ## Operations
129
49
 
130
50
  ### Remote
data/bin/zold CHANGED
@@ -41,7 +41,7 @@ Encoding.default_internal = Encoding::UTF_8
41
41
  log = Zold::Log.new
42
42
 
43
43
  args = []
44
- unless ENV['RACK_ENV'] == 'test'
44
+ unless ENV['RACK_ENV'] == 'test' || ARGV.find { |a| a == '--ignore-global-config' }
45
45
  config = File.expand_path('~/.zold')
46
46
  if File.exist?(config)
47
47
  body = File.read(config)
@@ -92,6 +92,7 @@ Available options:"
92
92
  default: Dir.pwd
93
93
  o.bool '-h', '--help', 'Show these instructions'
94
94
  o.bool '--trace', 'Show full stack trace in case of a problem'
95
+ o.bool '--ignore-global-config', 'Don\'t read options from the ~/.zold file'
95
96
  o.on '--no-colors', 'Disable colors in the ouput' do
96
97
  Rainbow.enabled = false
97
98
  end
@@ -104,16 +105,17 @@ Available options:"
104
105
  end
105
106
  end
106
107
 
107
- raise 'Try --help' if opts.arguments.empty? && !opts.help?
108
+ commands = opts.arguments.reject { |a| a.start_with?('-') }
109
+ command = commands[0]
108
110
 
109
- if opts.help? && opts.arguments.empty?
111
+ if command.nil?
112
+ raise 'A command required, try --help' unless opts.help?
110
113
  log.info(opts.to_s)
111
114
  exit
112
115
  end
113
116
 
114
- command = opts.arguments[0]
115
-
116
- args = args[(args.index(command) + 1)..-1]
117
+ args = opts.arguments
118
+ args << '--help' if opts.help?
117
119
 
118
120
  wallets = Zold::Wallets.new(opts['dir'])
119
121
  remotes = Zold::Remotes.new(File.join(opts['dir'], '.zoldata/remotes'))
@@ -12,6 +12,6 @@ Feature: Command Line Processing
12
12
  Then Exit code is zero
13
13
 
14
14
  Scenario: Wallet can be created
15
- When I run bin/zold with "--trace create --public-key=id_rsa.pub"
15
+ When I run bin/zold with "--trace --public-key=id_rsa.pub create"
16
16
  Then Exit code is zero
17
17
 
@@ -9,5 +9,5 @@ zold --help
9
9
  declare -a commands=(node create invoice remote pay show fetch clean diff merge propagate pull push)
10
10
  for c in "${commands[@]}"
11
11
  do
12
- zold --trace $c --help
12
+ zold --ignore-global-config --trace $c --help
13
13
  done
@@ -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
  port=`python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'`
9
9
 
@@ -21,21 +21,21 @@ while ! nc -z localhost ${port}; do
21
21
  ((c++)) && ((c==20)) && break
22
22
  done
23
23
 
24
- zold --trace remote clean
25
- zold --trace remote add localhost ${port}
26
- zold --trace remote show
24
+ zold remote clean
25
+ zold remote add localhost ${port}
26
+ zold remote show
27
27
 
28
- zold --trace create --public-key=id_rsa.pub 0000000000000000
28
+ zold create --public-key=id_rsa.pub 0000000000000000
29
29
  target=`zold create --public-key=id_rsa.pub`
30
30
  invoice=`zold invoice ${target}`
31
- zold --trace pay --private-key=id_rsa 0000000000000000 ${invoice} 14.99 'To save the world!'
32
- zold --trace propagate 0000000000000000
33
- zold --trace show
34
- zold --trace show 0000000000000000
31
+ zold pay --private-key=id_rsa 0000000000000000 ${invoice} 14.99 'To save the world!'
32
+ zold propagate 0000000000000000
33
+ zold show
34
+ zold show 0000000000000000
35
35
 
36
- zold --trace remote show
37
- zold --trace push 0000000000000000
38
- zold --trace fetch 0000000000000000 --ignore-score-weakness
39
- zold --trace diff 0000000000000000
40
- zold --trace merge 0000000000000000
41
- zold --trace clean 0000000000000000
36
+ zold remote show
37
+ zold push 0000000000000000
38
+ zold fetch 0000000000000000 --ignore-score-weakness
39
+ zold diff 0000000000000000
40
+ zold merge 0000000000000000
41
+ zold clean 0000000000000000
@@ -39,7 +39,7 @@ module Zold
39
39
  end
40
40
 
41
41
  def run(args = [])
42
- opts = Slop.parse(args, help: true) do |o|
42
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
43
43
  o.banner = "Usage: zold clean [ID...] [options]
44
44
  Available options:"
45
45
  o.bool '--help', 'Print instructions'
@@ -48,8 +48,9 @@ Available options:"
48
48
  @log.info(opts.to_s)
49
49
  return
50
50
  end
51
- raise 'At least one wallet ID is required' if opts.arguments.empty?
52
- opts.arguments.each do |id|
51
+ mine = opts.arguments[1..-1]
52
+ raise 'At least one wallet ID is required' if mine.empty?
53
+ mine.each do |id|
53
54
  clean(Copies.new(File.join(@copies, id)), opts)
54
55
  end
55
56
  end
@@ -35,7 +35,7 @@ module Zold
35
35
  end
36
36
 
37
37
  def run(args = [])
38
- opts = Slop.parse(args, help: true) do |o|
38
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
39
39
  o.banner = "Usage: zold create [options]
40
40
  Available options:"
41
41
  o.string '--public-key',
@@ -48,7 +48,8 @@ Available options:"
48
48
  @log.info(opts.to_s)
49
49
  return
50
50
  end
51
- create(opts.arguments.empty? ? Id.new : Id.new(opts.arguments[0]), opts)
51
+ mine = opts.arguments[1..-1]
52
+ create(mine.empty? ? Id.new : Id.new(mine[0]), opts)
52
53
  end
53
54
 
54
55
  def create(id, opts)
@@ -39,7 +39,7 @@ module Zold
39
39
  end
40
40
 
41
41
  def run(args = [])
42
- opts = Slop.parse(args, help: true) do |o|
42
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
43
43
  o.banner = "Usage: zold diff [ID...] [options]
44
44
  Available options:"
45
45
  o.bool '--help', 'Print instructions'
@@ -48,9 +48,10 @@ Available options:"
48
48
  @log.info(opts.to_s)
49
49
  return
50
50
  end
51
- raise 'At least one wallet ID is required' if opts.arguments.empty?
51
+ mine = opts.arguments[1..-1]
52
+ raise 'At least one wallet ID is required' if mine.empty?
52
53
  stdout = ''
53
- opts.arguments.each do |id|
54
+ mine.each do |id|
54
55
  stdout += diff(
55
56
  @wallets.find(Id.new(id)),
56
57
  Copies.new(File.join(@copies, id)),
@@ -41,7 +41,7 @@ module Zold
41
41
  end
42
42
 
43
43
  def run(args = [])
44
- opts = Slop.parse(args, help: true) do |o|
44
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
45
45
  o.banner = "Usage: zold fetch [ID...] [options]
46
46
  Available options:"
47
47
  o.bool '--ignore-score-weakness',
@@ -56,8 +56,9 @@ Available options:"
56
56
  @log.info(opts.to_s)
57
57
  return
58
58
  end
59
- raise 'At least one wallet ID is required' if opts.arguments.empty?
60
- opts.arguments.each do |id|
59
+ mine = opts.arguments[1..-1]
60
+ raise 'At least one wallet ID is required' if mine.empty?
61
+ mine.each do |id|
61
62
  fetch(id, Copies.new(File.join(@copies, id)), opts)
62
63
  end
63
64
  end
@@ -35,7 +35,7 @@ module Zold
35
35
  end
36
36
 
37
37
  def run(args = [])
38
- opts = Slop.parse(args, help: true) do |o|
38
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
39
39
  o.banner = "Usage: zold invoice ID [options]
40
40
  Where:
41
41
  'ID' is the wallet ID of the money receiver
@@ -49,8 +49,9 @@ Available options:"
49
49
  @log.info(opts.to_s)
50
50
  return
51
51
  end
52
- raise 'Receiver wallet ID is required' if opts.arguments[0].nil?
53
- wallet = @wallets.find(Zold::Id.new(opts.arguments[0]))
52
+ mine = opts.arguments[1..-1]
53
+ raise 'Receiver wallet ID is required' if mine[0].nil?
54
+ wallet = @wallets.find(Zold::Id.new(mine[0]))
54
55
  raise 'Wallet doesn\'t exist, do \'fetch\' first' unless wallet.exists?
55
56
  invoice(wallet, opts)
56
57
  end
@@ -39,7 +39,7 @@ module Zold
39
39
 
40
40
  # Returns the array of modified wallets (IDs)
41
41
  def run(args = [])
42
- opts = Slop.parse(args, help: true) do |o|
42
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
43
43
  o.banner = "Usage: zold merge [ID...] [options]
44
44
  Available options:"
45
45
  o.bool '--help', 'Print instructions'
@@ -48,9 +48,10 @@ Available options:"
48
48
  @log.info(opts.to_s)
49
49
  return
50
50
  end
51
- raise 'At least one wallet ID is required' if opts.arguments.empty?
51
+ mine = opts.arguments[1..-1]
52
+ raise 'At least one wallet ID is required' if mine.empty?
52
53
  modified = []
53
- opts.arguments.each do |id|
54
+ mine.each do |id|
54
55
  wallet = @wallets.find(Id.new(id))
55
56
  next unless merge(wallet, Copies.new(File.join(@copies, id)), opts)
56
57
  modified << Id.new(id)
@@ -38,7 +38,7 @@ module Zold
38
38
  end
39
39
 
40
40
  def run(args = [])
41
- opts = Slop.parse(args, help: true) do |o|
41
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
42
42
  o.banner = 'Usage: zold node [options]'
43
43
  o.string '--invoice',
44
44
  'The invoice you want to collect money to'
@@ -34,7 +34,7 @@ module Zold
34
34
  end
35
35
 
36
36
  def run(args = [])
37
- opts = Slop.parse(args, help: true) do |o|
37
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
38
38
  o.banner = "Usage: zold pay wallet invoice amount [details] [options]
39
39
  Where:
40
40
  'wallet' is the sender's wallet ID
@@ -55,14 +55,15 @@ Available options:"
55
55
  @log.info(opts.to_s)
56
56
  return
57
57
  end
58
- raise 'Payer wallet ID is required' if opts.arguments[0].nil?
59
- from = @wallets.find(Zold::Id.new(opts.arguments[0]))
58
+ mine = opts.arguments[1..-1]
59
+ raise 'Payer wallet ID is required as the first argument' if mine[0].nil?
60
+ from = @wallets.find(Zold::Id.new(mine[0]))
60
61
  raise 'Wallet doesn\'t exist, do \'fetch\' first' unless from.exists?
61
- raise 'Recepient\'s invoice is required' if opts.arguments[1].nil?
62
- invoice = opts.arguments[1]
63
- raise 'Amount is required (in ZLD)' if opts.arguments[2].nil?
64
- amount = Zold::Amount.new(zld: opts.arguments[2].to_f)
65
- details = opts.arguments[3] ? opts.arguments[3] : '-'
62
+ raise 'Recepient\'s invoice is required as the second argument' if mine[1].nil?
63
+ invoice = mine[1]
64
+ raise 'Amount is required (in ZLD) as the third argument' if mine[2].nil?
65
+ amount = Zold::Amount.new(zld: mine[2].to_f)
66
+ details = mine[3] ? mine[3] : '-'
66
67
  pay(from, invoice, amount, details, opts)
67
68
  end
68
69
 
@@ -37,7 +37,7 @@ module Zold
37
37
 
38
38
  # Returns list of Wallet IDs which were affected
39
39
  def run(args = [])
40
- opts = Slop.parse(args, help: true) do |o|
40
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
41
41
  o.banner = "Usage: zold propagate [ID...] [options]
42
42
  Available options:"
43
43
  o.bool '--help', 'Print instructions'
@@ -46,9 +46,10 @@ Available options:"
46
46
  @log.info(opts.to_s)
47
47
  return
48
48
  end
49
- raise 'At least one wallet ID is required' if opts.arguments.empty?
49
+ mine = opts.arguments[1..-1]
50
+ raise 'At least one wallet ID is required' if mine.empty?
50
51
  modified = []
51
- opts.arguments.each do |id|
52
+ mine.each do |id|
52
53
  modified += propagate(@wallets.find(id), opts)
53
54
  end
54
55
  modified
@@ -39,7 +39,7 @@ module Zold
39
39
  end
40
40
 
41
41
  def run(args = [])
42
- opts = Slop.parse(args, help: true) do |o|
42
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
43
43
  o.banner = "Usage: zold push [ID...] [options]
44
44
  Available options:"
45
45
  o.bool '--help', 'Print instructions'
@@ -48,8 +48,9 @@ Available options:"
48
48
  @log.info(opts.to_s)
49
49
  return
50
50
  end
51
- raise 'At least one wallet ID is required' if opts.arguments.empty?
52
- opts.arguments.each do |id|
51
+ mine = opts.arguments[1..-1]
52
+ raise 'At least one wallet ID is required' if mine.empty?
53
+ mine.each do |id|
53
54
  push(@wallets.find(Id.new(id)), opts)
54
55
  end
55
56
  end
@@ -41,7 +41,7 @@ module Zold
41
41
  end
42
42
 
43
43
  def run(args = [])
44
- opts = Slop.parse(args, help: true) do |o|
44
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
45
45
  o.banner = "Usage: zold remote <command> [options]
46
46
  Available commands:
47
47
  #{Rainbow('remote show').green}
@@ -62,7 +62,12 @@ Available options:"
62
62
  default: false
63
63
  o.bool '--help', 'Print instructions'
64
64
  end
65
- command = opts.arguments[0]
65
+ if opts.help?
66
+ @log.info(opts.to_s)
67
+ return
68
+ end
69
+ mine = opts.arguments[1..-1]
70
+ command = mine[0]
66
71
  case command
67
72
  when 'show'
68
73
  show
@@ -71,14 +76,14 @@ Available options:"
71
76
  when 'reset'
72
77
  reset
73
78
  when 'add'
74
- add(opts.arguments[1], opts.arguments[2] ? opts.arguments[2].to_i : Remotes::PORT)
79
+ add(mine[1], mine[2] ? mine[2].to_i : Remotes::PORT)
75
80
  when 'remove'
76
- remove(opts.arguments[1], opts.arguments[2] ? opts.arguments[2].to_i : Remotes::PORT)
81
+ remove(mine[1], mine[2] ? mine[2].to_i : Remotes::PORT)
77
82
  when 'update'
78
83
  update(opts)
79
84
  update(opts, false)
80
85
  else
81
- @log.info(opts.to_s)
86
+ raise "Unknown command '#{command}'"
82
87
  end
83
88
  end
84
89
 
@@ -36,7 +36,7 @@ module Zold
36
36
  end
37
37
 
38
38
  def run(args = [])
39
- opts = Slop.parse(args, help: true) do |o|
39
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
40
40
  o.banner = "Usage: zold show [ID...] [options]
41
41
  Available options:"
42
42
  o.bool '--help', 'Print instructions'
@@ -45,12 +45,13 @@ Available options:"
45
45
  @log.info(opts.to_s)
46
46
  return
47
47
  end
48
- if opts.arguments.empty?
48
+ mine = opts.arguments[1..-1]
49
+ if mine.empty?
49
50
  require_relative 'list'
50
51
  List.new(wallets: @wallets, log: @log).run(args)
51
52
  else
52
53
  total = Amount::ZERO
53
- opts.arguments.each do |id|
54
+ mine.each do |id|
54
55
  total += show(@wallets.find(Id.new(id)), opts)
55
56
  end
56
57
  total
@@ -41,7 +41,7 @@ module Zold
41
41
  end
42
42
 
43
43
  def run(args = [])
44
- opts = Slop.parse(args, help: true) do |o|
44
+ opts = Slop.parse(args, help: true, suppress_errors: true) do |o|
45
45
  o.banner = "Usage: zold taxes command [options]
46
46
  Available commands:
47
47
  #{Rainbow('taxes pay').green} wallet
@@ -57,21 +57,22 @@ Available options:"
57
57
  default: '~/.ssh/id_rsa'
58
58
  o.bool '--help', 'Print instructions'
59
59
  end
60
- command = opts.arguments[0]
60
+ mine = opts.arguments[1..-1]
61
+ command = mine[0]
61
62
  case command
62
63
  when 'show'
63
- raise 'At least one wallet ID is required' unless opts.arguments[1]
64
- opts.arguments[1..-1].each do |id|
64
+ raise 'At least one wallet ID is required' unless mine[1]
65
+ mine[1..-1].each do |id|
65
66
  show(@wallets.find(Id.new(id), opts))
66
67
  end
67
68
  when 'debt'
68
- raise 'At least one wallet ID is required' unless opts.arguments[1]
69
- opts.arguments[1..-1].each do |id|
69
+ raise 'At least one wallet ID is required' unless mine[1]
70
+ mine[1..-1].each do |id|
70
71
  debt(@wallets.find(Id.new(id), opts))
71
72
  end
72
73
  when 'pay'
73
- raise 'At least one wallet ID is required' unless opts.arguments[1]
74
- opts.arguments[1..-1].each do |id|
74
+ raise 'At least one wallet ID is required' unless mine[1]
75
+ mine[1..-1].each do |id|
75
76
  pay(@wallets.find(Id.new(id)), opts)
76
77
  end
77
78
  else
@@ -55,10 +55,10 @@ module Zold
55
55
  copies.add(body, 'remote', Remotes::PORT, 0)
56
56
  Fetch.new(
57
57
  remotes: @remotes, copies: copies.root, log: @log
58
- ).run([id.to_s, "--ignore-node=#{@address}"])
58
+ ).run(['fetch', id.to_s, "--ignore-node=#{@address}"])
59
59
  modified = Merge.new(
60
60
  wallets: @wallets, copies: copies.root, log: @log
61
- ).run([id.to_s])
61
+ ).run(['merge', id.to_s])
62
62
  debt = Tax.new(@wallets.find(id)).debt
63
63
  if debt > Tax::TRIAL
64
64
  raise "Taxes are not paid, the debt is #{debt} (#{debt.to_i} zents), won't promote the wallet"
@@ -67,7 +67,7 @@ module Zold
67
67
  modified.each do |m|
68
68
  Push.new(
69
69
  wallets: @wallets, remotes: @remotes, log: @log
70
- ).run([m.to_s])
70
+ ).run(['push', m.to_s])
71
71
  end
72
72
  modified
73
73
  end
@@ -165,7 +165,7 @@ module Zold
165
165
  @suffixes.reduce(start) do |prefix, suffix|
166
166
  hex = Digest::SHA256.hexdigest(prefix + ' ' + suffix)
167
167
  return false unless hex.end_with?('0' * @strength)
168
- hex[0, 19]
168
+ hex[0, 63]
169
169
  end
170
170
  true
171
171
  end
@@ -34,19 +34,25 @@ module Zold
34
34
  attr_reader :id, :date, :amount, :prefix, :bnf, :details, :sign
35
35
  attr_writer :sign, :amount, :bnf
36
36
  def initialize(id, date, amount, prefix, bnf, details)
37
+ raise 'The ID can\'t be NIL' if id.nil?
37
38
  raise "ID of transaction can't be negative: #{id}" if id < 1
38
39
  @id = id
40
+ raise 'The time can\'t be NIL' if date.nil?
39
41
  raise 'Time have to be of type Time' unless date.is_a?(Time)
40
42
  raise "Time can't be in the future: #{date}" if date > Time.now
41
43
  @date = date
44
+ raise 'The amount can\'t be NIL' if amount.nil?
42
45
  raise 'The amount has to be of type Amount' unless amount.is_a?(Amount)
43
46
  raise 'The amount can\'t be zero' if amount.zero?
44
47
  @amount = amount
48
+ raise 'The bnf can\'t be NIL' if bnf.nil?
45
49
  raise 'The bnf has to be of type Id' unless bnf.is_a?(Id)
46
50
  @bnf = bnf
51
+ raise 'Prefix can\'t be NIL' if prefix.nil?
47
52
  raise "Prefix is too short: \"#{prefix}\"" if prefix.length < 8
48
53
  raise "Prefix is too long: \"#{prefix}\"" if prefix.length > 32
49
54
  @prefix = prefix
55
+ raise 'Details can\'t be NIL' if details.nil?
50
56
  raise 'Details can\'t be empty' if details.empty?
51
57
  raise "Details are too long: \"#{details}\"" if details.length > 128
52
58
  raise "Details are wrong: \"#{details}\"" unless details =~ /^[a-zA-Z0-9 -\.,]{1,128}$/
@@ -23,5 +23,5 @@
23
23
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
24
24
  # License:: MIT
25
25
  module Zold
26
- VERSION = '0.6'.freeze
26
+ VERSION = '0.6.1'.freeze
27
27
  end
@@ -35,7 +35,7 @@ class TestClean < Minitest::Test
35
35
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
36
36
  copies.add('a1', 'host-1', 80, 1, Time.now - 26 * 60)
37
37
  copies.add('a2', 'host-2', 80, 2, Time.now - 26 * 60)
38
- Zold::Clean.new(copies: copies.root).run([id.to_s])
38
+ Zold::Clean.new(copies: copies.root).run(['clean', id.to_s])
39
39
  assert(copies.all.empty?)
40
40
  end
41
41
  end
@@ -32,7 +32,7 @@ class TestCreate < Minitest::Test
32
32
  def test_creates_wallet
33
33
  Dir.mktmpdir 'test' do |dir|
34
34
  wallet = Zold::Create.new(wallets: Zold::Wallets.new(dir)).run(
35
- ['--public-key=fixtures/id_rsa.pub']
35
+ ['create', '--public-key=fixtures/id_rsa.pub']
36
36
  )
37
37
  assert wallet.balance.zero?
38
38
  assert(
@@ -47,14 +47,14 @@ class TestDiff < Minitest::Test
47
47
  File.write(second.path, File.read(wallet.path))
48
48
  Zold::Pay.new(
49
49
  wallets: Zold::Wallets.new(dir)
50
- ).run([id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
50
+ ).run(['pay', id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
51
51
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
52
52
  copies.add(File.read(first.path), 'host-1', 80, 5)
53
53
  copies.add(File.read(second.path), 'host-2', 80, 5)
54
54
  diff = Zold::Diff.new(
55
55
  wallets: Zold::Wallets.new(dir),
56
56
  copies: copies.root
57
- ).run([id.to_s])
57
+ ).run(['diff', id.to_s])
58
58
  assert(diff.include?('-1;'))
59
59
  end
60
60
  end
@@ -58,7 +58,7 @@ class TestFetch < Minitest::Test
58
58
  remotes.add('fake-2', 80)
59
59
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
60
60
  Zold::Fetch.new(copies: copies.root, remotes: remotes).run(
61
- ['--ignore-score-weakness', id.to_s]
61
+ ['fetch', '--ignore-score-weakness', id.to_s]
62
62
  )
63
63
  assert_equal(copies.all[0][:name], '1')
64
64
  assert_equal(copies.all[0][:score], 0)
@@ -79,7 +79,7 @@ class TestFetch < Minitest::Test
79
79
  )
80
80
  remotes.add('fake-1', 80)
81
81
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
82
- Zold::Fetch.new(copies: copies.root, remotes: remotes).run([id.to_s])
82
+ Zold::Fetch.new(copies: copies.root, remotes: remotes).run(['fetch', id.to_s])
83
83
  assert_equal(copies.all[0][:name], '1')
84
84
  assert_equal(copies.all[0][:score], 0)
85
85
  end
@@ -38,7 +38,7 @@ class TestInvoice < Minitest::Test
38
38
  source = wallets.find(id)
39
39
  source.init(id, Zold::Key.new(file: 'fixtures/id_rsa.pub'))
40
40
  invoice = Zold::Invoice.new(wallets: wallets).run(
41
- [id.to_s, '--length=16']
41
+ ['invoice', id.to_s, '--length=16']
42
42
  )
43
43
  assert_equal(33, invoice.length)
44
44
  end
@@ -24,6 +24,7 @@ require 'json'
24
24
  require 'time'
25
25
  require 'webmock/minitest'
26
26
  require_relative '../../lib/zold/wallet'
27
+ require_relative '../../lib/zold/wallets'
27
28
  require_relative '../../lib/zold/id'
28
29
  require_relative '../../lib/zold/copies'
29
30
  require_relative '../../lib/zold/key'
@@ -49,14 +50,14 @@ class TestMerge < Minitest::Test
49
50
  File.write(second.path, File.read(wallet.path))
50
51
  Zold::Pay.new(
51
52
  wallets: Zold::Wallets.new(dir)
52
- ).run([id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
53
+ ).run(['pay', id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
53
54
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
54
55
  copies.add(File.read(first.path), 'host-1', 80, 5)
55
56
  copies.add(File.read(second.path), 'host-2', 80, 5)
56
57
  modified = Zold::Merge.new(
57
58
  wallets: Zold::Wallets.new(dir),
58
59
  copies: copies.root
59
- ).run([id.to_s])
60
+ ).run(['merge', id.to_s])
60
61
  assert(1, modified.count)
61
62
  assert(id, modified[0])
62
63
  end
@@ -74,14 +75,14 @@ class TestMerge < Minitest::Test
74
75
  File.write(second.path, File.read(wallet.path))
75
76
  Zold::Pay.new(
76
77
  wallets: Zold::Wallets.new(dir)
77
- ).run([id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
78
+ ).run(['pay', id.to_s, second.id.to_s, '14.95', '--force', '--private-key=fixtures/id_rsa'])
78
79
  copies = Zold::Copies.new(File.join(dir, "copies/#{id}"))
79
80
  copies.add(File.read(first.path), 'host-1', 80, 5)
80
81
  copies.add(File.read(second.path), 'host-2', 80, 5)
81
82
  modified = Zold::Merge.new(
82
83
  wallets: Zold::Wallets.new(dir),
83
84
  copies: copies.root
84
- ).run([id.to_s])
85
+ ).run(['merge', id.to_s])
85
86
  assert(1, modified.count)
86
87
  assert(id, modified[0])
87
88
  end
@@ -46,8 +46,8 @@ class TestNode < Minitest::Test
46
46
  remotes = Zold::Remotes.new(File.join(dir, 'remotes.csv'))
47
47
  remotes.clean
48
48
  remotes.add('localhost', port)
49
- Zold::Push.new(wallet: wallet, remotes: remotes).run
50
- Zold::Fetch.new(wallet: wallet, copies: copies, remotes: remotes).run
49
+ Zold::Push.new(wallet: wallet, remotes: remotes).run(['push'])
50
+ Zold::Fetch.new(wallet: wallet, copies: copies, remotes: remotes).run(['fetch'])
51
51
  assert_equal(copies.all[0][:name], '1')
52
52
  assert_equal(copies.all[0][:score], 0)
53
53
  end
@@ -41,7 +41,7 @@ class TestPay < Minitest::Test
41
41
  amount = Zold::Amount.new(zld: 14.95)
42
42
  Zold::Pay.new(wallets: wallets).run(
43
43
  [
44
- '--force', '--private-key=fixtures/id_rsa',
44
+ 'pay', '--force', '--private-key=fixtures/id_rsa',
45
45
  id.to_s, target.to_s, amount.to_zld, 'For the car'
46
46
  ]
47
47
  )
@@ -57,11 +57,11 @@ class TestRemote < Minitest::Test
57
57
  status: 404
58
58
  )
59
59
  cmd = Zold::Remote.new(remotes: remotes)
60
- cmd.run(['clean'])
61
- cmd.run(['add', zero.host, zero.port.to_s])
62
- cmd.run(%w[add localhost 2])
60
+ cmd.run(%w[remote clean])
61
+ cmd.run(['remote', 'add', zero.host, zero.port.to_s])
62
+ cmd.run(%w[remote add localhost 2])
63
63
  assert_equal(2, remotes.all.count)
64
- cmd.run(['update', '--ignore-score-weakness'])
64
+ cmd.run(['remote', 'update', '--ignore-score-weakness'])
65
65
  assert_equal(1, remotes.all.count)
66
66
  end
67
67
  end
@@ -37,7 +37,7 @@ class TestShow < Minitest::Test
37
37
  wallets = Zold::Wallets.new(dir)
38
38
  wallet = wallets.find(id)
39
39
  wallet.init(Zold::Id.new, Zold::Key.new(file: 'fixtures/id_rsa.pub'))
40
- balance = Zold::Show.new(wallets: wallets).run([id.to_s])
40
+ balance = Zold::Show.new(wallets: wallets).run(['show', id.to_s])
41
41
  assert_equal(Zold::Amount::ZERO, balance)
42
42
  end
43
43
  end
@@ -59,7 +59,7 @@ class TestTaxes < Minitest::Test
59
59
  )
60
60
  Zold::Taxes.new(
61
61
  wallets: wallets, remotes: remotes
62
- ).run(['pay', '--private-key=fixtures/id_rsa', id.to_s])
62
+ ).run(['taxes', '--private-key=fixtures/id_rsa', id.to_s])
63
63
  assert_equal(Zold::Amount.new(coins: 335_376_547), wallet.balance)
64
64
  end
65
65
  end
@@ -0,0 +1,8 @@
1
+ OPTS=-shell-escape -halt-on-error -interaction=errorstopmode -output-directory=.
2
+
3
+ wp.pdf: wp.tex
4
+ pdflatex ${OPTS} wp.tex
5
+
6
+ clean:
7
+ rm -rf wp.log wp.pdf wp.out wp.aux
8
+
data/wp/wp.tex CHANGED
@@ -1,37 +1,141 @@
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}
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
8
  \usepackage{setspace}
9
- \usepackage{indentfirst}
10
-
11
- \pagestyle{empty}
12
- \setlength{\topskip}{6pt}
13
- \setlength{\parindent}{0pt} % indent first line
14
- \setlength{\parskip}{0pt} % before par
15
- \interfootnotelinepenalty=10000
16
- \setstretch{1.1}
17
-
9
+ \usepackage{microtype}
18
10
  \usepackage{mathpazo} % Palantino font
19
-
11
+ \usepackage{minted}
12
+ \setminted{fontsize=\footnotesize}
13
+ \setminted{breaklines}
14
+ \usemintedstyle{bw}
20
15
  \usepackage{hyperref}
21
- \usepackage[style=authoryear,sorting=nyt,backend=biber,
22
- hyperref=true,abbreviate=true,
23
- maxcitenames=1,maxbibnames=1]{biblatex}
24
- \addbibresource{main.bib}
16
+ \pagestyle{empty}
17
+ \setstretch{1.2}
25
18
 
26
19
  \title{Zold, Lightweight Crypto Currency}
27
- \author{Yegor Bugayenko\\
28
- CEO of Zerocracy, Inc.\\
29
- 555 Bryant, Ste 470, Palo Alto, CA 94301\\
30
- \texttt{yegor@zerocracy.com}\\
31
- 408.692.4742}
32
- \begin{document}
20
+ \author{Yegor Bugayenko\\\texttt{yegor@zold.io}}
33
21
 
22
+ \begin{document}
34
23
  \raggedbottom
24
+ \maketitle
25
+ \begin{abstract}
26
+ Works for you?
27
+ \end{abstract}
28
+
29
+ \section{Motivation}
30
+
31
+ Bitcoin, the first decentralized digital currency, was released in
32
+ January 2009. Since then a number of similar Blockchain-based products have been
33
+ created, including Etherium, Litecoin, and others.
34
+
35
+ Zold is also a decentralized digital currency that maintains its transactions
36
+ in an unpredicable amount of zero-trust server nodes, trying to guarantee
37
+ data consistency. However, the architecture of Zold is not based on Blockchain
38
+ principles. The development of Zold was motivated by the desire to overcome
39
+ a few obvious disadvantages of existing solutions.
40
+
41
+ First, the speed of transaction processing is rather low.
42
+
43
+ Second, mining commissions are high.
44
+
45
+ Third, the technology is too complex.
46
+
47
+ Zold was created as an attempt to resolve these mentioned problems
48
+ of existing digital currencies.
49
+
50
+ \section{Principles}
51
+
52
+ \textbf{Open source}.
53
+ Zold is a command line tool. Its entire code base is open source.
54
+
55
+ \textbf{Capacity}.
56
+ One currency unit is called ZLD.
57
+ One ZLD by convention equals to $2^24$ (16,777,216) \emph{zents}.
58
+ All amounts are stored as signed 64-bit integers.
59
+ Thus, the technical capacity of the currency is 549,755,813,888 ZLD (half a trillion).
60
+
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
64
+ 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.
75
+
76
+ \section{Proof of Work}
77
+
78
+ Each node calculates its own score.
79
+ First, it builds the initial text body, which consists of four parts,
80
+ separated by spaces:
81
+
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,
86
+ \item The invoice.
87
+ \end{itemize}
88
+
89
+ For example, the body may look like this:
90
+
91
+ \begin{minted}{text}
92
+ 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000
93
+ \end{minted}
94
+
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:
100
+
101
+ \begin{minted}{text}
102
+ 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 abcdef
103
+ \end{minted}
104
+
105
+ The SHA-256 of this body will be:
106
+
107
+ \begin{minted}{text}
108
+ 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 abcdef
109
+ \end{minted}
110
+
111
+ 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):
115
+
116
+ When the first suffix is found, the score is 1. Then, to
117
+ increase the score by one, the next suffix has to be found, which
118
+ can be added to the first 64 characters of the previous hash
119
+ in order to obtain a new hash with trailing zeros, for example:
120
+
121
+ \begin{minted}{text}
122
+ 2017-07-19T21:24:51Z b2.zold.io 4096 Yt0lOy6Rgf@0000000000000000 abcdef abcdef
123
+ \end{minted}
124
+
125
+ And so on.
126
+
127
+ 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
129
+ is the amount of the trailing zeros in the hash. In the example above the
130
+ strength was equal to six.
131
+
132
+ \section{Wallets}
133
+
134
+ \section{Push}
135
+
136
+ \section{Fetch and Merge}
137
+
138
+ \section{Threats Analysis}
35
139
 
36
140
  how are you?
37
141
 
metadata CHANGED
@@ -1,7 +1,7 @@
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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -340,6 +340,7 @@ files:
340
340
  - test/test_wallets.rb
341
341
  - test/test_zold.rb
342
342
  - wp/.gitignore
343
+ - wp/Makefile
343
344
  - wp/wp.tex
344
345
  - zold.gemspec
345
346
  homepage: http://github.com/zerocracy/zold