nanocurrency 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +40 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +43 -0
  10. data/Rakefile +16 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/ext/.DS_Store +0 -0
  14. data/ext/nanocurrency_ext/blake2-config.h +72 -0
  15. data/ext/nanocurrency_ext/blake2-impl.h +160 -0
  16. data/ext/nanocurrency_ext/blake2.h +195 -0
  17. data/ext/nanocurrency_ext/blake2b-load-sse2.h +68 -0
  18. data/ext/nanocurrency_ext/blake2b-load-sse41.h +402 -0
  19. data/ext/nanocurrency_ext/blake2b-ref.c +373 -0
  20. data/ext/nanocurrency_ext/blake2b-round.h +157 -0
  21. data/ext/nanocurrency_ext/curve25519-donna-32bit.h +579 -0
  22. data/ext/nanocurrency_ext/curve25519-donna-64bit.h +413 -0
  23. data/ext/nanocurrency_ext/curve25519-donna-helpers.h +67 -0
  24. data/ext/nanocurrency_ext/curve25519-donna-sse2.h +1112 -0
  25. data/ext/nanocurrency_ext/ed25519-donna-32bit-sse2.h +513 -0
  26. data/ext/nanocurrency_ext/ed25519-donna-32bit-tables.h +61 -0
  27. data/ext/nanocurrency_ext/ed25519-donna-64bit-sse2.h +436 -0
  28. data/ext/nanocurrency_ext/ed25519-donna-64bit-tables.h +53 -0
  29. data/ext/nanocurrency_ext/ed25519-donna-64bit-x86-32bit.h +435 -0
  30. data/ext/nanocurrency_ext/ed25519-donna-64bit-x86.h +351 -0
  31. data/ext/nanocurrency_ext/ed25519-donna-basepoint-table.h +259 -0
  32. data/ext/nanocurrency_ext/ed25519-donna-batchverify.h +275 -0
  33. data/ext/nanocurrency_ext/ed25519-donna-impl-base.h +364 -0
  34. data/ext/nanocurrency_ext/ed25519-donna-impl-sse2.h +390 -0
  35. data/ext/nanocurrency_ext/ed25519-donna-portable-identify.h +103 -0
  36. data/ext/nanocurrency_ext/ed25519-donna-portable.h +135 -0
  37. data/ext/nanocurrency_ext/ed25519-donna.h +115 -0
  38. data/ext/nanocurrency_ext/ed25519-hash-custom.c +28 -0
  39. data/ext/nanocurrency_ext/ed25519-hash-custom.h +30 -0
  40. data/ext/nanocurrency_ext/ed25519-hash.h +219 -0
  41. data/ext/nanocurrency_ext/ed25519-randombytes-custom.h +10 -0
  42. data/ext/nanocurrency_ext/ed25519-randombytes.h +91 -0
  43. data/ext/nanocurrency_ext/ed25519.c +150 -0
  44. data/ext/nanocurrency_ext/ed25519.h +30 -0
  45. data/ext/nanocurrency_ext/extconf.rb +3 -0
  46. data/ext/nanocurrency_ext/fuzz/README.md +173 -0
  47. data/ext/nanocurrency_ext/fuzz/build-nix.php +134 -0
  48. data/ext/nanocurrency_ext/fuzz/curve25519-ref10.c +1272 -0
  49. data/ext/nanocurrency_ext/fuzz/curve25519-ref10.h +8 -0
  50. data/ext/nanocurrency_ext/fuzz/ed25519-donna-sse2.c +3 -0
  51. data/ext/nanocurrency_ext/fuzz/ed25519-donna.c +1 -0
  52. data/ext/nanocurrency_ext/fuzz/ed25519-donna.h +34 -0
  53. data/ext/nanocurrency_ext/fuzz/ed25519-ref10.c +4647 -0
  54. data/ext/nanocurrency_ext/fuzz/ed25519-ref10.h +9 -0
  55. data/ext/nanocurrency_ext/fuzz/fuzz-curve25519.c +172 -0
  56. data/ext/nanocurrency_ext/fuzz/fuzz-ed25519.c +219 -0
  57. data/ext/nanocurrency_ext/modm-donna-32bit.h +469 -0
  58. data/ext/nanocurrency_ext/modm-donna-64bit.h +361 -0
  59. data/ext/nanocurrency_ext/rbext.c +164 -0
  60. data/ext/nanocurrency_ext/regression.h +1024 -0
  61. data/lib/nano/account.rb +59 -0
  62. data/lib/nano/base32.rb +87 -0
  63. data/lib/nano/block.rb +142 -0
  64. data/lib/nano/check.rb +65 -0
  65. data/lib/nano/conversion.rb +102 -0
  66. data/lib/nano/hash.rb +43 -0
  67. data/lib/nano/key.rb +69 -0
  68. data/lib/nano/utils.rb +45 -0
  69. data/lib/nano/work.rb +51 -0
  70. data/lib/nanocurrency.rb +7 -0
  71. data/lib/nanocurrency/version.rb +3 -0
  72. data/lib/nanocurrency_ext.bundle +0 -0
  73. data/nanocurrency.gemspec +44 -0
  74. metadata +192 -0
@@ -0,0 +1,59 @@
1
+ module Nano
2
+ ##
3
+ # The Account class is used to simplify conversion from account to
4
+ # public key
5
+ class Account
6
+
7
+ # @return [String] The base32 encoded account address
8
+ attr_reader :address
9
+
10
+ # @return [String] The public key for the account encoded in hexadecimal
11
+ attr_reader :public_key
12
+
13
+ ##
14
+ # The Account intiailizer.
15
+ # @param value [Hash] This hash can contain the keys `:account` and
16
+ # `:public_key` which will be stored within the object
17
+ def initialize(val)
18
+ if val[:address]
19
+ @address = val[:address]
20
+ end
21
+
22
+ if val[:public_key]
23
+ @public_key = val[:public_key]
24
+ end
25
+ end
26
+
27
+ ##
28
+ # A class initializer to create an Account object from the account
29
+ # base32 address.
30
+ #
31
+ # @param input [String] The base32 account address
32
+ #
33
+ # @return [Account] Returns a created account given a valid account address
34
+ def self.from_address(input)
35
+ return nil unless input.is_a? String
36
+
37
+ prefix_length = nil
38
+
39
+ if input.start_with? "nano_"
40
+ prefix_length = 5
41
+ elsif input.start_with? "xrb_"
42
+ prefix_length = 4
43
+ end
44
+
45
+ return nil if prefix_length.nil?
46
+
47
+ public_key_bytes = Nano::Base32.decode(input[prefix_length, 52])
48
+ checksum = Nano::Base32.decode(input[(prefix_length + 52)..-1])
49
+ public_key_bin = Nano::Utils.hex_to_bin public_key_bytes
50
+ computed_check = Blake2b.hex(
51
+ public_key_bin, Blake2b::Key.none, 5
52
+ ).reverse.upcase
53
+
54
+ return nil if computed_check == checksum
55
+
56
+ Account.new(:address => input, :public_key => public_key_bytes)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,87 @@
1
+ require_relative './utils'
2
+
3
+ module Nano
4
+ ##
5
+ # This module performs encoding and decoding of Base32 as specified by
6
+ # the Nano protocol.
7
+ module Base32
8
+ extend self
9
+
10
+ ##
11
+ # The Base32 alphabet of characters.
12
+ ALPHABET = "13456789abcdefghijkmnopqrstuwxyz".freeze
13
+
14
+ ##
15
+ # Encode an array of bytes into Base32 string.
16
+ #
17
+ # @param bytes [Array<Int8>] The byte array to encode.
18
+ #
19
+ # @return [String] The base32 encoded representation of the bytes
20
+ def encode(bytes)
21
+ length = bytes.length
22
+ leftover = (length * 8) % 5
23
+ offset = leftover == 0 ? 0 : 5 - leftover
24
+ value = 0
25
+ output = ""
26
+ bits = 0
27
+
28
+ length.times do |i|
29
+ value = (value << 8) | bytes[i]
30
+ bits += 8
31
+
32
+ while bits >= 5
33
+ output += ALPHABET[(value >> bits + offset - 5) & 31]
34
+ bits -= 5
35
+ end
36
+ end
37
+
38
+ if bits > 0
39
+ output += ALPHABET[(value << (5 - (bits + offset))) & 31]
40
+ end
41
+
42
+ output
43
+ end
44
+
45
+ ##
46
+ # Decodes a Base32 encoded string into a hex string
47
+ #
48
+ # @param str [String] The base32 encoded string
49
+ #
50
+ # @returns [String] The hexadecimal decoded base32 string
51
+ def decode(str)
52
+ length = str.length
53
+ leftover = (length * 5) % 8
54
+ offset = leftover == 0 ? 0 : 8 - leftover
55
+
56
+ bits = 0
57
+ value = 0
58
+
59
+ output = Array.new
60
+
61
+ length.times do |i|
62
+ value = (value << 5) | read_char(str[i])
63
+ bits += 5
64
+
65
+ if bits >= 8
66
+ output.push((value >> (bits + offset - 8)) & 255)
67
+ bits -= 8
68
+ end
69
+ end
70
+
71
+ if bits > 0
72
+ output.push((value << (bits + offset - 8)) & 255)
73
+ end
74
+
75
+ output = output.drop(1) unless leftover == 0
76
+ Nano::Utils.bytes_to_hex(output)
77
+ end
78
+
79
+ def read_char(chr)
80
+ idx = ALPHABET.index(chr)
81
+ raise ArgumentError, "Character #{chr} not base32 compliant" if idx.nil?
82
+ idx
83
+ end
84
+
85
+ module_function :read_char
86
+ end
87
+ end
data/lib/nano/block.rb ADDED
@@ -0,0 +1,142 @@
1
+ require_relative "./hash"
2
+ require_relative "./work"
3
+ require_relative "./check"
4
+ require "nanocurrency_ext"
5
+
6
+ module Nano
7
+ ##
8
+ # A class representing a state block in the Nano network.
9
+ # Can be initialized as an existing block, with a work and signature
10
+ # or as an incomplete block.
11
+ class Block
12
+
13
+ # @return [String] The type of the block, locked to 'state' currently
14
+ attr_reader :type
15
+
16
+ # @return [String] The account associated with the block.
17
+ attr_reader :account
18
+
19
+ # @return [String] The account representative as set by the block
20
+ attr_reader :representative
21
+
22
+ # @return [String] The previous block to this block
23
+ attr_reader :previous
24
+
25
+ # @return [String] The link for this block
26
+ attr_reader :link
27
+
28
+ # @return [String] The balance for the account at this block
29
+ attr_reader :balance
30
+
31
+ # @return [String?] The proof of work computed for this block
32
+ attr_reader :work
33
+
34
+ # @return [String?] The signature for the block
35
+ attr_reader :signature
36
+
37
+ ##
38
+ # The block initializer, requires certain parameters to be valid.
39
+ # @param params [Hash] The block parameters to construct the block with.
40
+ # `:previous` - The previous block hash as a string
41
+ # `:account` - The associated account address as a string
42
+ # `:representative` - The account representative as a string
43
+ # `:balance` - The account balance after this block in raw unit
44
+ # `:link` - The link hash associated with this block.
45
+ # `:work` - The proof of work for the block (optional)
46
+ # `:signature` - The signature for this block (optional)
47
+ def initialize(params)
48
+ @type = "state"
49
+
50
+ @previous = params[:previous]
51
+
52
+ raise ArgumentError, "Missing data for previous" if @previous.nil?
53
+ raise(
54
+ ArgumentError, "Invalid previous hash #{@previous}"
55
+ ) unless Nano::Check.is_valid_hash? @previous
56
+
57
+ @account = params[:account]
58
+ raise ArgumentError, "Missing data for account" if @account.nil?
59
+ raise(
60
+ ArgumentError, "Invalid account #{@account}"
61
+ ) unless Nano::Check.is_valid_account? @account
62
+
63
+ @representative = params[:representative]
64
+ raise(
65
+ ArgumentError, "Missing data for representative"
66
+ ) if @representative.nil?
67
+ raise(
68
+ ArgumentError, "Invalid representative #{@representative}"
69
+ ) unless Nano::Check.is_valid_account? @representative
70
+
71
+ @balance = params[:balance]
72
+ raise(
73
+ ArgumentError, "Missing data for balance"
74
+ ) if @balance.nil?
75
+ raise(
76
+ ArgumentError, "Invalid balance #{@balance}"
77
+ ) unless Nano::Check.is_balance_valid? @balance
78
+
79
+ @link = params[:link]
80
+ raise(
81
+ ArgumentError, "Missing data for link"
82
+ ) if @link.nil?
83
+ raise(
84
+ ArgumentError, "Invalid link #{@link}"
85
+ ) unless Nano::Check.is_hash_valid? @link
86
+
87
+ @work = params[:work]
88
+ @signature = params[:signature]
89
+ end
90
+
91
+ # @return The link for the account converted to an address.
92
+ # May not be valid in the case the link is a block hash
93
+ def link_as_account
94
+ Nano::Key.derive_address(@link)
95
+ end
96
+
97
+ ##
98
+ # This method signs the block using the secret key given. This method
99
+ # will fail if the key is incorrect. If this method succeeds, the block
100
+ # may still be invalid if the key does not belong to the block account.
101
+ # This method modifies the block's existing signature.
102
+ #
103
+ # @param secret_key [String] The hexidecimal representation of the
104
+ # secret key. Should match the account but does not perform a check,
105
+ # currently.
106
+ #
107
+ # @return [String] Returns the signature for the block, whilst also
108
+ # setting the signature internally.
109
+ def sign!(secret_key)
110
+ throw ArgumentError, "Invalid key" unless Nano::Check.is_key?(secret_key)
111
+
112
+ hash_bin = Nano::Utils.hex_to_bin(hash)
113
+ secret_bin = Nano::Utils.hex_to_bin(secret_key)
114
+
115
+ @signature = Nano::Utils.bin_to_hex(
116
+ NanocurrencyExt.sign(hash_bin, secret_bin)
117
+ ).upcase
118
+
119
+ @signature
120
+ end
121
+
122
+ ##
123
+ # Computes the proof of work for the block, setting the internal work
124
+ # value and overwriting the existing value. The method uses a native
125
+ # extension to improve the performance.
126
+ #
127
+ # @return [String] The computed work value, also setting it internally.
128
+ def compute_work!
129
+ base_prev = "".rjust(64, "0")
130
+ is_first = previous == base_prev
131
+ hash = is_first ? Nano::Key.derive_public_key(@account) : previous
132
+ @work = Nano::Work.compute_work(hash)
133
+ @work
134
+ end
135
+
136
+ ##
137
+ # @return [String] The computed hash for the block.
138
+ def hash
139
+ Nano::Hash.hash_block(self)
140
+ end
141
+ end
142
+ end
data/lib/nano/check.rb ADDED
@@ -0,0 +1,65 @@
1
+ module Nano
2
+ ##
3
+ # The Check module contains some basic sanity and type checks to ensure
4
+ # robustness throughout the Gem.
5
+ module Check
6
+
7
+ MIN_INDEX = 0
8
+ MAX_INDEX = 2 ** 32 - 1
9
+
10
+ extend self
11
+
12
+ def is_valid_hex?(value)
13
+ value.is_a?(String) && value.match?(/^[0-9a-fA-F]{32}$/)
14
+ end
15
+
16
+ def is_valid_hash?(value)
17
+ is_hash_valid?(value)
18
+ end
19
+
20
+ def is_hex_valid?(value)
21
+ is_valid_hex?(value)
22
+ end
23
+
24
+ def is_numerical?(value)
25
+ return false unless value.is_a?(String)
26
+ return false if value.start_with?(".")
27
+ return false if value.end_with?(".")
28
+
29
+ number_without_dot = value.sub(".", "")
30
+
31
+ # More than one '.' in the number.
32
+ return false unless number_without_dot.count(".") == 0
33
+
34
+ is_balance_valid?(number_without_dot)
35
+ end
36
+
37
+ def is_balance_valid?(value)
38
+ value.match?(/^[0-9]*$/)
39
+ end
40
+
41
+ def is_seed_valid?(seed)
42
+ seed.is_a?(String) && seed.match?(/^[0-9a-fA-F]{64}$/)
43
+ end
44
+
45
+ def is_index_valid?(index)
46
+ index.is_a?(Integer) && index >= MIN_INDEX && index <= MAX_INDEX
47
+ end
48
+
49
+ def is_key?(input)
50
+ input.is_a?(String) && input.match?(/^[0-9a-fA-F]{64}$/)
51
+ end
52
+
53
+ def is_hash_valid?(hash)
54
+ is_seed_valid?(hash)
55
+ end
56
+
57
+ def is_work_valid?(input)
58
+ input.is_a?(String) && input.match?(/^[0-9a-fA-F]{16}$/)
59
+ end
60
+
61
+ def is_valid_account?(input)
62
+ !Nano::Account.from_address(input).nil?
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,102 @@
1
+ require "set"
2
+ require "bigdecimal"
3
+ require_relative "./check"
4
+
5
+ module Nano
6
+
7
+ ##
8
+ # The Unit module provides utilities to allow conversion for the different
9
+ # unit types referenced in the Nano protocol.
10
+ module Unit
11
+ extend self
12
+
13
+ # A set of unit symbols found in the Nano protocol.
14
+ UNIT_SET = [
15
+ :hex, :raw, :nano, :knano, :Nano, :NANO, :KNano, :MNano
16
+ ].to_set.freeze
17
+
18
+ # A hash of the unit types and number of zeros the unit is offset by.
19
+ UNIT_ZEROS = {
20
+ :hex => 0,
21
+ :raw => 0,
22
+ :nano => 20,
23
+ :knano => 24,
24
+ :Nano => 30,
25
+ :NANO => 30,
26
+ :KNano => 33,
27
+ :MNano => 36
28
+ }.freeze
29
+
30
+ ##
31
+ # @return [Boolean] True if the unit is contained within the unit set.
32
+ def valid_unit?(unit)
33
+ UNIT_SET === unit
34
+ end
35
+
36
+ ##
37
+ # Converts the value in a unit type into another representation
38
+ #
39
+ # @param value [String] A number string of the value in the base unit
40
+ # @param from [Symbol] A valid unit which denotes the base unit
41
+ # @param to [Symbol] The unit type to convert the value into.
42
+ # @return [String] The converted value as the unit from to
43
+ def convert(value, from, to)
44
+ raise ArgumentError, "Invalid from unit type" unless Unit.valid_unit?(from)
45
+ raise ArgumentError, "Invalid to unit type" unless Unit.valid_unit?(to)
46
+
47
+ from_zeros = zeros_for_unit(from)
48
+ to_zeros = zeros_for_unit(to)
49
+
50
+ raise ArgumentError, "Value must be a string" unless value.is_a? String
51
+
52
+ if from == :hex
53
+ is_hex = Nano::Check.is_valid_hex? value
54
+ raise ArgumentError, "Invalid hex value string" unless is_hex
55
+ else
56
+ is_number = Nano::Check.is_numerical? value
57
+ raise ArgumentError, "Invalid number value string" unless is_number
58
+ end
59
+
60
+ zero_difference = from_zeros - to_zeros
61
+
62
+ big_number = 0
63
+ if from == :hex
64
+ big_number = BigDecimal.new(value.to_i(16))
65
+ else
66
+ big_number = BigDecimal.new(value)
67
+ end
68
+
69
+ is_increase = zero_difference > 0
70
+ zero_difference.abs.times do |i|
71
+ if is_increase
72
+ big_number = big_number * 10
73
+ else
74
+ big_number = big_number / 10
75
+ end
76
+ end
77
+
78
+ if to == :hex
79
+ big_number.to_i.to_s(16).rjust(32, "0")
80
+ else
81
+ if big_number.to_i == big_number
82
+ big_number.to_i.to_s
83
+ else
84
+ big_number.to_s("F")
85
+ end
86
+ end
87
+ end
88
+
89
+
90
+ # @return [Hash] The unit zeros constant
91
+ def unit_zeros
92
+ UNIT_ZEROS
93
+ end
94
+
95
+ # @param [Symbol] The unit type
96
+ # @return [Integer] The number of zeros for a unit type
97
+ def zeros_for_unit(unit)
98
+ raise ArgumentError, "Invalid unit" unless valid_unit? unit
99
+ UNIT_ZEROS[unit]
100
+ end
101
+ end
102
+ end