nanocurrency 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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