eth 0.4.16 → 0.5.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +15 -5
  3. data/.github/workflows/docs.yml +26 -0
  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 +21 -0
  9. data/CHANGELOG.md +65 -10
  10. data/Gemfile +13 -2
  11. data/LICENSE.txt +202 -22
  12. data/README.md +199 -74
  13. data/bin/console +4 -4
  14. data/bin/setup +5 -4
  15. data/eth.gemspec +34 -29
  16. data/lib/eth/abi/type.rb +178 -0
  17. data/lib/eth/abi.rb +396 -0
  18. data/lib/eth/address.rb +55 -11
  19. data/lib/eth/api.rb +223 -0
  20. data/lib/eth/chain.rb +151 -0
  21. data/lib/eth/client/http.rb +63 -0
  22. data/lib/eth/client/ipc.rb +47 -0
  23. data/lib/eth/client.rb +232 -0
  24. data/lib/eth/constant.rb +71 -0
  25. data/lib/eth/eip712.rb +184 -0
  26. data/lib/eth/key/decrypter.rb +121 -88
  27. data/lib/eth/key/encrypter.rb +178 -99
  28. data/lib/eth/key.rb +136 -48
  29. data/lib/eth/rlp/decoder.rb +109 -0
  30. data/lib/eth/rlp/encoder.rb +78 -0
  31. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  32. data/lib/eth/rlp/sedes/binary.rb +97 -0
  33. data/lib/eth/rlp/sedes/list.rb +84 -0
  34. data/lib/eth/rlp/sedes.rb +74 -0
  35. data/lib/eth/rlp.rb +63 -0
  36. data/lib/eth/signature.rb +163 -0
  37. data/lib/eth/tx/eip1559.rb +336 -0
  38. data/lib/eth/tx/eip2930.rb +328 -0
  39. data/lib/eth/tx/legacy.rb +296 -0
  40. data/lib/eth/tx.rb +273 -143
  41. data/lib/eth/unit.rb +49 -0
  42. data/lib/eth/util.rb +235 -0
  43. data/lib/eth/version.rb +18 -1
  44. data/lib/eth.rb +33 -67
  45. metadata +50 -85
  46. data/.github/workflows/build.yml +0 -26
  47. data/lib/eth/gas.rb +0 -9
  48. data/lib/eth/open_ssl.rb +0 -264
  49. data/lib/eth/secp256k1.rb +0 -7
  50. data/lib/eth/sedes.rb +0 -40
  51. data/lib/eth/utils.rb +0 -130
@@ -1,113 +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
21
-
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
22
33
 
23
- 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
24
45
 
25
- 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
26
55
 
27
- def derive_key(password)
28
- case kdf
29
- when 'pbkdf2'
30
- @key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
31
- when 'scrypt'
32
- # 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)
33
- @key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
34
- else
35
- 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
36
71
  end
37
- end
38
72
 
39
- def check_macs
40
- mac1 = keccak256(key[(key_length/2), key_length] + ciphertext)
41
- 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"]
42
76
 
43
- if mac1 != mac2
44
- raise "Message Authentications Codes do not match!"
77
+ if mac1 != mac2
78
+ raise DecrypterError, "Message Authentications Codes do not match!"
79
+ end
45
80
  end
46
- end
47
81
 
48
- def decrypted_data
49
- @decrypted_data ||= cipher.update(ciphertext) + cipher.final
50
- end
82
+ def decrypted_data
83
+ @decrypted_data ||= cipher.update(ciphertext) + cipher.final
84
+ end
51
85
 
52
- def crypto_data
53
- @crypto_data ||= data['crypto'] || data['Crypto']
54
- end
86
+ def crypto_data
87
+ @crypto_data ||= data["crypto"] || data["Crypto"]
88
+ end
55
89
 
56
- def ciphertext
57
- hex_to_bin crypto_data['ciphertext']
58
- end
90
+ def ciphertext
91
+ Util.hex_to_bin crypto_data["ciphertext"]
92
+ end
59
93
 
60
- def cipher_name
61
- "aes-128-ctr"
62
- end
94
+ def cipher_name
95
+ "aes-128-ctr"
96
+ end
63
97
 
64
- def cipher
65
- @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
66
- cipher.decrypt
67
- cipher.key = key[0, (key_length/2)]
68
- 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
69
104
  end
70
- end
71
105
 
72
- def iv
73
- hex_to_bin crypto_data['cipherparams']['iv']
74
- end
106
+ def iv
107
+ Util.hex_to_bin crypto_data["cipherparams"]["iv"]
108
+ end
75
109
 
76
- def salt
77
- hex_to_bin crypto_data['kdfparams']['salt']
78
- end
110
+ def salt
111
+ Util.hex_to_bin crypto_data["kdfparams"]["salt"]
112
+ end
79
113
 
80
- def iterations
81
- crypto_data['kdfparams']['c'].to_i
82
- end
114
+ def iterations
115
+ crypto_data["kdfparams"]["c"].to_i
116
+ end
83
117
 
84
- def kdf
85
- crypto_data['kdf']
86
- end
118
+ def kdf
119
+ crypto_data["kdf"]
120
+ end
87
121
 
88
- def key_length
89
- crypto_data['kdfparams']['dklen'].to_i
90
- end
122
+ def key_length
123
+ crypto_data["kdfparams"]["dklen"].to_i
124
+ end
91
125
 
92
- def n
93
- crypto_data['kdfparams']['n'].to_i
94
- end
126
+ def n
127
+ crypto_data["kdfparams"]["n"].to_i
128
+ end
95
129
 
96
- def r
97
- crypto_data['kdfparams']['r'].to_i
98
- end
130
+ def r
131
+ crypto_data["kdfparams"]["r"].to_i
132
+ end
99
133
 
100
- def p
101
- crypto_data['kdfparams']['p'].to_i
102
- end
134
+ def p
135
+ crypto_data["kdfparams"]["p"].to_i
136
+ end
103
137
 
104
- def digest
105
- OpenSSL::Digest.new digest_name
106
- end
138
+ def digest
139
+ OpenSSL::Digest.new digest_name
140
+ end
107
141
 
108
- def digest_name
109
- "sha256"
142
+ def digest_name
143
+ "sha256"
144
+ end
110
145
  end
111
-
112
-
113
146
  end
@@ -1,128 +1,207 @@
1
- require 'json'
2
- require 'securerandom'
3
-
4
- class Eth::Key::Encrypter
5
- include Eth::Utils
6
-
7
- def self.perform(key, password, options = {})
8
- new(key, options).perform(password)
9
- end
10
-
11
- def initialize(key, options = {})
12
- @key = key
13
- @options = options
14
- 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::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
15
40
 
16
- def perform(password)
17
- derive_key password
18
- encrypt
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
19
65
 
20
- data.to_json
21
- 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
22
75
 
23
- def data
24
- {
25
- crypto: {
26
- cipher: cipher_name,
27
- cipherparams: {
28
- iv: bin_to_hex(iv),
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),
29
109
  },
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]
110
+ id: id,
111
+ version: 3,
112
+ }
44
113
  end
45
- end
46
114
 
47
- def id
48
- @id ||= options[:id] || SecureRandom.uuid
49
- end
115
+ private
50
116
 
117
+ attr_reader :derived_key, :encrypted_key, :key, :options
51
118
 
52
- private
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
125
+ end
53
126
 
54
- attr_reader :derived_key, :encrypted_key, :key, :options
127
+ def digest
128
+ @digest ||= OpenSSL::Digest.new digest_name
129
+ end
55
130
 
56
- def cipher
57
- @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
58
- cipher.encrypt
59
- cipher.iv = iv
60
- cipher.key = derived_key[0, (key_length/2)]
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
61
137
  end
62
- end
63
138
 
64
- def digest
65
- @digest ||= OpenSSL::Digest.new digest_name
66
- end
139
+ def encrypt
140
+ @encrypted_key = cipher.update(Util.hex_to_bin key.private_hex) + cipher.final
141
+ end
67
142
 
68
- def derive_key(password)
69
- @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
70
- end
143
+ def mac
144
+ Util.keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
145
+ end
71
146
 
72
- def encrypt
73
- @encrypted_key = cipher.update(hex_to_bin key) + cipher.final
74
- end
147
+ def kdf
148
+ options[:kdf] || "pbkdf2"
149
+ end
75
150
 
76
- def mac
77
- keccak256(derived_key[(key_length/2), key_length] + encrypted_key)
78
- end
151
+ def cipher_name
152
+ "aes-128-ctr"
153
+ end
79
154
 
80
- def cipher_name
81
- "aes-128-ctr"
82
- end
155
+ def digest_name
156
+ "sha256"
157
+ end
83
158
 
84
- def digest_name
85
- "sha256"
86
- end
159
+ def prf
160
+ "hmac-#{digest_name}"
161
+ end
87
162
 
88
- def prf
89
- "hmac-#{digest_name}"
90
- end
163
+ def key_length
164
+ 32
165
+ end
91
166
 
92
- def key_length
93
- 32
94
- end
167
+ def salt_length
168
+ 32
169
+ end
95
170
 
96
- def salt_length
97
- 32
98
- end
171
+ def iv_length
172
+ 16
173
+ end
99
174
 
100
- def iv_length
101
- 16
102
- end
175
+ def id
176
+ @id ||= options[:id] || SecureRandom.uuid
177
+ end
103
178
 
104
- def iterations
105
- options[:iterations] || 262_144
106
- end
179
+ def iterations
180
+ options[:iterations] || 262_144
181
+ end
107
182
 
108
- def salt
109
- @salt ||= if options[:salt]
110
- hex_to_bin options[:salt]
111
- else
112
- SecureRandom.random_bytes(salt_length)
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
113
189
  end
114
- end
115
190
 
116
- def iv
117
- @iv ||= if options[:iv]
118
- hex_to_bin options[:iv]
119
- else
120
- SecureRandom.random_bytes(iv_length)
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
121
197
  end
122
- end
123
198
 
124
- def address
125
- Eth::Key.new(priv: key).address
126
- end
199
+ def parallelization
200
+ options[:parallelization] || 8
201
+ end
127
202
 
203
+ def block_size
204
+ options[:block_size] || 1
205
+ end
206
+ end
128
207
  end