eth 0.4.18 → 0.5.2

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