eth 0.4.18 → 0.5.2

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +6 -2
  3. data/.github/workflows/docs.yml +1 -1
  4. data/.github/workflows/spec.yml +52 -0
  5. data/.gitignore +24 -24
  6. data/.gitmodules +3 -3
  7. data/.yardopts +1 -0
  8. data/AUTHORS.txt +27 -0
  9. data/CHANGELOG.md +63 -13
  10. data/Gemfile +12 -4
  11. data/LICENSE.txt +202 -22
  12. data/README.md +231 -76
  13. data/bin/console +4 -4
  14. data/bin/setup +5 -4
  15. data/codecov.yml +6 -0
  16. data/eth.gemspec +23 -19
  17. data/lib/eth/abi/type.rb +178 -0
  18. data/lib/eth/abi.rb +396 -0
  19. data/lib/eth/address.rb +57 -10
  20. data/lib/eth/api.rb +223 -0
  21. data/lib/eth/chain.rb +151 -0
  22. data/lib/eth/client/http.rb +63 -0
  23. data/lib/eth/client/ipc.rb +50 -0
  24. data/lib/eth/client.rb +232 -0
  25. data/lib/eth/constant.rb +71 -0
  26. data/lib/eth/eip712.rb +184 -0
  27. data/lib/eth/key/decrypter.rb +121 -85
  28. data/lib/eth/key/encrypter.rb +180 -99
  29. data/lib/eth/key.rb +134 -45
  30. data/lib/eth/rlp/decoder.rb +114 -0
  31. data/lib/eth/rlp/encoder.rb +78 -0
  32. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  33. data/lib/eth/rlp/sedes/binary.rb +97 -0
  34. data/lib/eth/rlp/sedes/list.rb +84 -0
  35. data/lib/eth/rlp/sedes.rb +74 -0
  36. data/lib/eth/rlp.rb +63 -0
  37. data/lib/eth/signature.rb +163 -0
  38. data/lib/eth/solidity.rb +75 -0
  39. data/lib/eth/tx/eip1559.rb +337 -0
  40. data/lib/eth/tx/eip2930.rb +329 -0
  41. data/lib/eth/tx/legacy.rb +297 -0
  42. data/lib/eth/tx.rb +269 -146
  43. data/lib/eth/unit.rb +49 -0
  44. data/lib/eth/util.rb +235 -0
  45. data/lib/eth/version.rb +18 -1
  46. data/lib/eth.rb +34 -67
  47. metadata +47 -95
  48. data/.github/workflows/build.yml +0 -36
  49. data/lib/eth/gas.rb +0 -7
  50. data/lib/eth/open_ssl.rb +0 -395
  51. data/lib/eth/secp256k1.rb +0 -5
  52. data/lib/eth/sedes.rb +0 -39
  53. data/lib/eth/utils.rb +0 -126
@@ -1,110 +1,146 @@
1
- require "json"
2
- require "scrypt"
3
-
4
- class Eth::Key::Decrypter
5
- include Eth::Utils
6
-
7
- def self.perform(data, password)
8
- new(data, password).perform
9
- end
10
-
11
- def initialize(data, password)
12
- @data = JSON.parse(data)
13
- @password = password
14
- end
15
-
16
- def perform
17
- derive_key password
18
- check_macs
19
- bin_to_hex decrypted_data
20
- end
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the {Eth} module.
16
+ module Eth
17
+
18
+ # The {Eth::Key::Decrypter} class to handle PBKDF2-SHA-256 decryption.
19
+ class Key::Decrypter
20
+
21
+ # Provides a specific decrypter error if decryption fails.
22
+ class DecrypterError < StandardError; end
23
+
24
+ # Class method {Eth::Key::Decrypter.perform} to perform an keystore
25
+ # decryption.
26
+ #
27
+ # @param data [JSON] encryption data including cypherkey.
28
+ # @param password [String] password to decrypt the key.
29
+ # @return [Eth::Key] decrypted key-pair.
30
+ def self.perform(data, password)
31
+ new(data, password).perform
32
+ end
21
33
 
22
- private
34
+ # Constructor of the {Eth::Key::Decrypter} class for secret key
35
+ # decryption. Should not be used; use {Eth::Key::Decrypter.perform}
36
+ # instead.
37
+ #
38
+ # @param data [JSON] encryption data including cypherkey.
39
+ # @param password [String] password to decrypt the key.
40
+ def initialize(data, password)
41
+ data = JSON.parse(data) if data.is_a? String
42
+ @data = data
43
+ @password = password
44
+ end
23
45
 
24
- attr_reader :data, :key, :password
46
+ # Method to decrypt key using password.
47
+ #
48
+ # @return [Eth::Key] decrypted key.
49
+ def perform
50
+ derive_key password
51
+ check_macs
52
+ private_key = Util.bin_to_hex decrypted_data
53
+ Eth::Key.new priv: private_key
54
+ end
25
55
 
26
- def derive_key(password)
27
- case kdf
28
- when "pbkdf2"
29
- @key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
30
- when "scrypt"
31
- # OpenSSL 1.1 inclues OpenSSL::KDF.scrypt, but it is not available usually, otherwise we could do: OpenSSL::KDF.scrypt(password, salt: salt, N: n, r: r, p: p, length: key_length)
32
- @key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
33
- else
34
- raise "Unsupported key derivation function: #{kdf}!"
56
+ private
57
+
58
+ attr_reader :data
59
+ attr_reader :key
60
+ attr_reader :password
61
+
62
+ def derive_key(password)
63
+ case kdf
64
+ when "pbkdf2"
65
+ @key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
66
+ when "scrypt"
67
+ @key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
68
+ else
69
+ raise DecrypterError, "Unsupported key derivation function: #{kdf}!"
70
+ end
35
71
  end
36
- end
37
72
 
38
- def check_macs
39
- mac1 = keccak256(key[(key_length / 2), key_length] + ciphertext)
40
- mac2 = hex_to_bin crypto_data["mac"]
73
+ def check_macs
74
+ mac1 = Util.keccak256(key[(key_length / 2), key_length] + ciphertext)
75
+ mac2 = Util.hex_to_bin crypto_data["mac"]
41
76
 
42
- if mac1 != mac2
43
- raise "Message Authentications Codes do not match!"
77
+ if mac1 != mac2
78
+ raise DecrypterError, "Message Authentications Codes do not match!"
79
+ end
44
80
  end
45
- end
46
81
 
47
- def decrypted_data
48
- @decrypted_data ||= cipher.update(ciphertext) + cipher.final
49
- end
82
+ def decrypted_data
83
+ @decrypted_data ||= cipher.update(ciphertext) + cipher.final
84
+ end
50
85
 
51
- def crypto_data
52
- @crypto_data ||= data["crypto"] || data["Crypto"]
53
- end
86
+ def crypto_data
87
+ @crypto_data ||= data["crypto"] || data["Crypto"]
88
+ end
54
89
 
55
- def ciphertext
56
- hex_to_bin crypto_data["ciphertext"]
57
- end
90
+ def ciphertext
91
+ Util.hex_to_bin crypto_data["ciphertext"]
92
+ end
58
93
 
59
- def cipher_name
60
- "aes-128-ctr"
61
- end
94
+ def cipher_name
95
+ "aes-128-ctr"
96
+ end
62
97
 
63
- def cipher
64
- @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
65
- cipher.decrypt
66
- cipher.key = key[0, (key_length / 2)]
67
- cipher.iv = iv
98
+ def cipher
99
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
100
+ cipher.decrypt
101
+ cipher.key = key[0, (key_length / 2)]
102
+ cipher.iv = iv
103
+ end
68
104
  end
69
- end
70
105
 
71
- def iv
72
- hex_to_bin crypto_data["cipherparams"]["iv"]
73
- end
106
+ def iv
107
+ Util.hex_to_bin crypto_data["cipherparams"]["iv"]
108
+ end
74
109
 
75
- def salt
76
- hex_to_bin crypto_data["kdfparams"]["salt"]
77
- end
110
+ def salt
111
+ Util.hex_to_bin crypto_data["kdfparams"]["salt"]
112
+ end
78
113
 
79
- def iterations
80
- crypto_data["kdfparams"]["c"].to_i
81
- end
114
+ def iterations
115
+ crypto_data["kdfparams"]["c"].to_i
116
+ end
82
117
 
83
- def kdf
84
- crypto_data["kdf"]
85
- end
118
+ def kdf
119
+ crypto_data["kdf"]
120
+ end
86
121
 
87
- def key_length
88
- crypto_data["kdfparams"]["dklen"].to_i
89
- end
122
+ def key_length
123
+ crypto_data["kdfparams"]["dklen"].to_i
124
+ end
90
125
 
91
- def n
92
- crypto_data["kdfparams"]["n"].to_i
93
- end
126
+ def n
127
+ crypto_data["kdfparams"]["n"].to_i
128
+ end
94
129
 
95
- def r
96
- crypto_data["kdfparams"]["r"].to_i
97
- end
130
+ def r
131
+ crypto_data["kdfparams"]["r"].to_i
132
+ end
98
133
 
99
- def p
100
- crypto_data["kdfparams"]["p"].to_i
101
- end
134
+ def p
135
+ crypto_data["kdfparams"]["p"].to_i
136
+ end
102
137
 
103
- def digest
104
- OpenSSL::Digest.new digest_name
105
- end
138
+ def digest
139
+ OpenSSL::Digest.new digest_name
140
+ end
106
141
 
107
- def digest_name
108
- "sha256"
142
+ def digest_name
143
+ "sha256"
144
+ end
109
145
  end
110
146
  end
@@ -1,126 +1,207 @@
1
- require "json"
2
- require "securerandom"
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the {Eth} module.
16
+ module Eth
17
+
18
+ # The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.
19
+ class Key::Encrypter
20
+
21
+ # Provides a specific encrypter error if decryption fails.
22
+ class EncrypterError < StandardError; end
23
+
24
+ # Class method {Eth::Key::Encrypter.perform} to performa an key-store
25
+ # encryption.
26
+ #
27
+ # @param key [Eth::Key] representing a secret key-pair used for encryption.
28
+ # @param options [Hash] the options to encrypt with.
29
+ # @option options [String] :kdf key derivation function defaults to pbkdf2.
30
+ # @option options [String] :id uuid given to the secret key.
31
+ # @option options [String] :iterations number of iterations for the hash function.
32
+ # @option options [String] :salt passed to PBKDF.
33
+ # @option options [String] :iv 128-bit initialisation vector for the cipher.
34
+ # @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8.
35
+ # @option options [Integer] :block_size for scrypt, defaults to 1.
36
+ # @return [JSON] formatted with encrypted key (cyphertext) and [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256).
37
+ def self.perform(key, password, options = {})
38
+ new(key, options).perform(password)
39
+ end
3
40
 
4
- class Eth::Key::Encrypter
5
- include Eth::Utils
41
+ # Constructor of the {Eth::Key::Encrypter} class for secret key
42
+ # encryption. Should not be used; use {Eth::Key::Encrypter.perform}
43
+ # instead.
44
+ #
45
+ # @param key [Eth::Key] representing a secret key-pair used for encryption.
46
+ # @param options [Hash] the options to encrypt with.
47
+ # @option options [String] :kdf key derivation function defaults to pbkdf2.
48
+ # @option options [String] :id uuid given to the secret key.
49
+ # @option options [String] :iterations number of iterations for the hash function.
50
+ # @option options [String] :salt passed to PBKDF.
51
+ # @option options [String] :iv 128-bit initialisation vector for the cipher.
52
+ # @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8.
53
+ # @option options [Integer] :block_size for scrypt, defaults to 1.
54
+ def initialize(key, options = {})
55
+ key = Key.new(priv: key) if key.is_a? String
56
+ @key = key
57
+ @options = options
58
+
59
+ # the key derivation functions default to pbkdf2 if no option is specified
60
+ # however, if an option is given then it must be either pbkdf2 or scrypt
61
+ if kdf != "scrypt" && kdf != "pbkdf2"
62
+ raise EncrypterError, "Unsupported key derivation function: #{kdf}!"
63
+ end
64
+ end
6
65
 
7
- def self.perform(key, password, options = {})
8
- new(key, options).perform(password)
9
- end
66
+ # Encrypt the key with a given password.
67
+ #
68
+ # @param password [String] a secret key used for encryption
69
+ # @return [String] a JSON-formatted keystore string.
70
+ def perform(password)
71
+ derive_key password
72
+ encrypt
73
+ data.to_json
74
+ end
10
75
 
11
- def initialize(key, options = {})
12
- @key = key
13
- @options = options
14
- end
76
+ # Output containing the encrypted key and
77
+ # [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256)
78
+ #
79
+ # @return [Hash] the encrypted keystore data.
80
+ def data
81
+ # default to pbkdf2
82
+ kdfparams = if kdf == "scrypt"
83
+ {
84
+ dklen: 32,
85
+ n: iterations,
86
+ p: parallelization,
87
+ r: block_size,
88
+ salt: Util.bin_to_hex(salt),
89
+ }
90
+ else
91
+ {
92
+ c: iterations,
93
+ dklen: 32,
94
+ prf: prf,
95
+ salt: Util.bin_to_hex(salt),
96
+ }
97
+ end
98
+
99
+ {
100
+ crypto: {
101
+ cipher: cipher_name,
102
+ cipherparams: {
103
+ iv: Util.bin_to_hex(iv),
104
+ },
105
+ ciphertext: Util.bin_to_hex(encrypted_key),
106
+ kdf: kdf,
107
+ kdfparams: kdfparams,
108
+ mac: Util.bin_to_hex(mac),
109
+ },
110
+ id: id,
111
+ version: 3,
112
+ }
113
+ end
15
114
 
16
- def perform(password)
17
- derive_key password
18
- encrypt
115
+ private
19
116
 
20
- data.to_json
21
- end
117
+ attr_reader :derived_key, :encrypted_key, :key, :options
22
118
 
23
- def data
24
- {
25
- crypto: {
26
- cipher: cipher_name,
27
- cipherparams: {
28
- iv: bin_to_hex(iv),
29
- },
30
- ciphertext: bin_to_hex(encrypted_key),
31
- kdf: "pbkdf2",
32
- kdfparams: {
33
- c: iterations,
34
- dklen: 32,
35
- prf: prf,
36
- salt: bin_to_hex(salt),
37
- },
38
- mac: bin_to_hex(mac),
39
- },
40
- id: id,
41
- version: 3,
42
- }.tap do |data|
43
- data[:address] = address unless options[:skip_address]
119
+ def cipher
120
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
121
+ cipher.encrypt
122
+ cipher.iv = iv
123
+ cipher.key = derived_key[0, (key_length / 2)]
124
+ end
44
125
  end
45
- end
46
126
 
47
- def id
48
- @id ||= options[:id] || SecureRandom.uuid
49
- end
50
-
51
- private
127
+ def digest
128
+ @digest ||= OpenSSL::Digest.new digest_name
129
+ end
52
130
 
53
- attr_reader :derived_key, :encrypted_key, :key, :options
131
+ def derive_key(password)
132
+ if kdf == "scrypt"
133
+ @derived_key = SCrypt::Engine.scrypt(password, salt, iterations, block_size, parallelization, key_length)
134
+ else
135
+ @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
136
+ end
137
+ end
54
138
 
55
- def cipher
56
- @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
57
- cipher.encrypt
58
- cipher.iv = iv
59
- cipher.key = derived_key[0, (key_length / 2)]
139
+ def encrypt
140
+ @encrypted_key = cipher.update(Util.hex_to_bin key.private_hex) + cipher.final
60
141
  end
61
- end
62
142
 
63
- def digest
64
- @digest ||= OpenSSL::Digest.new digest_name
65
- end
143
+ def mac
144
+ Util.keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
145
+ end
66
146
 
67
- def derive_key(password)
68
- @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
69
- end
147
+ def kdf
148
+ options[:kdf] || "pbkdf2"
149
+ end
70
150
 
71
- def encrypt
72
- @encrypted_key = cipher.update(hex_to_bin key) + cipher.final
73
- end
151
+ def cipher_name
152
+ "aes-128-ctr"
153
+ end
74
154
 
75
- def mac
76
- keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
77
- end
155
+ def digest_name
156
+ "sha256"
157
+ end
78
158
 
79
- def cipher_name
80
- "aes-128-ctr"
81
- end
159
+ def prf
160
+ "hmac-#{digest_name}"
161
+ end
82
162
 
83
- def digest_name
84
- "sha256"
85
- end
163
+ def key_length
164
+ 32
165
+ end
86
166
 
87
- def prf
88
- "hmac-#{digest_name}"
89
- end
167
+ def salt_length
168
+ 32
169
+ end
90
170
 
91
- def key_length
92
- 32
93
- end
171
+ def iv_length
172
+ 16
173
+ end
94
174
 
95
- def salt_length
96
- 32
97
- end
175
+ def id
176
+ @id ||= options[:id] || SecureRandom.uuid
177
+ end
98
178
 
99
- def iv_length
100
- 16
101
- end
179
+ def iterations
180
+ options[:iterations] || 262_144
181
+ end
102
182
 
103
- def iterations
104
- options[:iterations] || 262_144
105
- end
183
+ def salt
184
+ @salt ||= if options[:salt]
185
+ Util.hex_to_bin options[:salt]
186
+ else
187
+ SecureRandom.random_bytes(salt_length)
188
+ end
189
+ end
106
190
 
107
- def salt
108
- @salt ||= if options[:salt]
109
- hex_to_bin options[:salt]
110
- else
111
- SecureRandom.random_bytes(salt_length)
112
- end
113
- end
191
+ def iv
192
+ @iv ||= if options[:iv]
193
+ Util.hex_to_bin options[:iv]
194
+ else
195
+ SecureRandom.random_bytes(iv_length)
196
+ end
197
+ end
114
198
 
115
- def iv
116
- @iv ||= if options[:iv]
117
- hex_to_bin options[:iv]
118
- else
119
- SecureRandom.random_bytes(iv_length)
120
- end
121
- end
199
+ def parallelization
200
+ options[:parallelization] || 8
201
+ end
122
202
 
123
- def address
124
- Eth::Key.new(priv: key).address
203
+ def block_size
204
+ options[:block_size] || 1
205
+ end
125
206
  end
126
207
  end