eth-custom 0.5.7

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +18 -0
  3. data/.github/workflows/codeql.yml +48 -0
  4. data/.github/workflows/docs.yml +26 -0
  5. data/.github/workflows/spec.yml +52 -0
  6. data/.gitignore +43 -0
  7. data/.gitmodules +3 -0
  8. data/.rspec +4 -0
  9. data/.yardopts +1 -0
  10. data/AUTHORS.txt +29 -0
  11. data/CHANGELOG.md +218 -0
  12. data/Gemfile +17 -0
  13. data/LICENSE.txt +202 -0
  14. data/README.md +347 -0
  15. data/Rakefile +6 -0
  16. data/bin/console +10 -0
  17. data/bin/setup +9 -0
  18. data/codecov.yml +6 -0
  19. data/eth.gemspec +51 -0
  20. data/lib/eth/abi/event.rb +137 -0
  21. data/lib/eth/abi/type.rb +178 -0
  22. data/lib/eth/abi.rb +446 -0
  23. data/lib/eth/address.rb +106 -0
  24. data/lib/eth/api.rb +223 -0
  25. data/lib/eth/chain.rb +157 -0
  26. data/lib/eth/client/http.rb +63 -0
  27. data/lib/eth/client/ipc.rb +50 -0
  28. data/lib/eth/client.rb +499 -0
  29. data/lib/eth/constant.rb +71 -0
  30. data/lib/eth/contract/event.rb +42 -0
  31. data/lib/eth/contract/function.rb +57 -0
  32. data/lib/eth/contract/function_input.rb +38 -0
  33. data/lib/eth/contract/function_output.rb +37 -0
  34. data/lib/eth/contract/initializer.rb +47 -0
  35. data/lib/eth/contract.rb +143 -0
  36. data/lib/eth/eip712.rb +184 -0
  37. data/lib/eth/key/decrypter.rb +146 -0
  38. data/lib/eth/key/encrypter.rb +207 -0
  39. data/lib/eth/key.rb +167 -0
  40. data/lib/eth/rlp/decoder.rb +114 -0
  41. data/lib/eth/rlp/encoder.rb +78 -0
  42. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  43. data/lib/eth/rlp/sedes/binary.rb +97 -0
  44. data/lib/eth/rlp/sedes/list.rb +84 -0
  45. data/lib/eth/rlp/sedes.rb +74 -0
  46. data/lib/eth/rlp.rb +63 -0
  47. data/lib/eth/signature.rb +163 -0
  48. data/lib/eth/solidity.rb +75 -0
  49. data/lib/eth/tx/eip1559.rb +337 -0
  50. data/lib/eth/tx/eip2930.rb +329 -0
  51. data/lib/eth/tx/legacy.rb +297 -0
  52. data/lib/eth/tx.rb +322 -0
  53. data/lib/eth/unit.rb +49 -0
  54. data/lib/eth/util.rb +235 -0
  55. data/lib/eth/version.rb +20 -0
  56. data/lib/eth.rb +35 -0
  57. metadata +184 -0
@@ -0,0 +1,207 @@
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
40
+
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
65
+
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
75
+
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
114
+
115
+ private
116
+
117
+ attr_reader :derived_key, :encrypted_key, :key, :options
118
+
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
126
+
127
+ def digest
128
+ @digest ||= OpenSSL::Digest.new digest_name
129
+ end
130
+
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
138
+
139
+ def encrypt
140
+ @encrypted_key = cipher.update(Util.hex_to_bin key.private_hex) + cipher.final
141
+ end
142
+
143
+ def mac
144
+ Util.keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
145
+ end
146
+
147
+ def kdf
148
+ options[:kdf] || "pbkdf2"
149
+ end
150
+
151
+ def cipher_name
152
+ "aes-128-ctr"
153
+ end
154
+
155
+ def digest_name
156
+ "sha256"
157
+ end
158
+
159
+ def prf
160
+ "hmac-#{digest_name}"
161
+ end
162
+
163
+ def key_length
164
+ 32
165
+ end
166
+
167
+ def salt_length
168
+ 32
169
+ end
170
+
171
+ def iv_length
172
+ 16
173
+ end
174
+
175
+ def id
176
+ @id ||= options[:id] || SecureRandom.uuid
177
+ end
178
+
179
+ def iterations
180
+ options[:iterations] || 262_144
181
+ end
182
+
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
190
+
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
198
+
199
+ def parallelization
200
+ options[:parallelization] || 8
201
+ end
202
+
203
+ def block_size
204
+ options[:block_size] || 1
205
+ end
206
+ end
207
+ end
data/lib/eth/key.rb ADDED
@@ -0,0 +1,167 @@
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
+ require "json"
16
+ require "openssl"
17
+ require "rbsecp256k1"
18
+ require "scrypt"
19
+ require "securerandom"
20
+
21
+ # Provides the {Eth} module.
22
+ module Eth
23
+
24
+ # The {Eth::Key} class to handle Secp256k1 private/public key-pairs.
25
+ class Key
26
+
27
+ # The {Eth::Key::Decrypter} class to handle PBKDF2-SHA-256 decryption.
28
+ autoload :Decrypter, "eth/key/decrypter"
29
+
30
+ # The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.
31
+ autoload :Encrypter, "eth/key/encrypter"
32
+
33
+ # The `Secp256k1::PrivateKey` of the {Eth::Key} pair.
34
+ attr_reader :private_key
35
+
36
+ # The `Secp256k1::PublicKey` of the {Eth::Key} pair.
37
+ attr_reader :public_key
38
+
39
+ # Constructor of the {Eth::Key} class. Creates a new random key-pair
40
+ # if no `priv` key is provided.
41
+ #
42
+ # @param priv [String] binary string of private key data.
43
+ def initialize(priv: nil)
44
+
45
+ # Creates a new, randomized libsecp256k1 context.
46
+ ctx = Secp256k1::Context.new context_randomization_bytes: SecureRandom.random_bytes(32)
47
+
48
+ # Creates a new random key pair (public, private).
49
+ key = ctx.generate_key_pair
50
+
51
+ unless priv.nil?
52
+
53
+ # Converts hex private keys to binary strings.
54
+ priv = Util.hex_to_bin priv if Util.is_hex? priv
55
+
56
+ # Creates a keypair from existing private key data.
57
+ key = ctx.key_pair_from_private_key priv
58
+ end
59
+
60
+ # Sets the attributes.
61
+ @private_key = key.private_key
62
+ @public_key = key.public_key
63
+ end
64
+
65
+ # Signs arbitrary data without validation. Should not be used unless really
66
+ # desired. See also: {Key.personal_sign}, {Key.sign_typed_data}, and
67
+ # {Signature.recover}.
68
+ #
69
+ # @param blob [Object] that arbitrary data to be signed.
70
+ # @param chain_id [Integer] the chain id the signature should be generated on.
71
+ # @return [String] a hexa-decimal signature.
72
+ def sign(blob, chain_id = nil)
73
+ context = Secp256k1::Context.new
74
+ compact, recovery_id = context.sign_recoverable(@private_key, blob).compact
75
+ signature = compact.bytes
76
+ v = Chain.to_v recovery_id, chain_id
77
+ is_leading_zero = true
78
+ [v].pack("N").unpack("C*").each do |byte|
79
+ is_leading_zero = false if byte > 0 and is_leading_zero
80
+ signature.append byte unless is_leading_zero and byte === 0
81
+ end
82
+ Util.bin_to_hex signature.pack "c*"
83
+ end
84
+
85
+ # Prefixes a message with `\x19Ethereum Signed Message:` and signs
86
+ # it in the common way used by many web3 wallets. Complies with
87
+ # EIP-191 prefix `0x19` and version byte `0x45` (`E`). See also
88
+ # {Signature.personal_recover}.
89
+ # Ref: https://eips.ethereum.org/EIPS/eip-191
90
+ #
91
+ # @param message [String] the message string to be prefixed and signed.
92
+ # @param chain_id [Integer] the chain id the signature should be generated on.
93
+ # @return [String] an EIP-191 conform, hexa-decimal signature.
94
+ def personal_sign(message, chain_id = nil)
95
+ prefixed_message = Signature.prefix_message message
96
+ hashed_message = Util.keccak256 prefixed_message
97
+ sign hashed_message, chain_id
98
+ end
99
+
100
+ # Prefixes, hashes, and signes a typed data structure in the common
101
+ # way used by many web3 wallets. Complies with EIP-191 prefix `0x19`
102
+ # and EIP-712 version byte `0x01`. Supports `V3`, `V4`. See also
103
+ # {Signature.recover_typed_data}.
104
+ # Ref: https://eips.ethereum.org/EIPS/eip-712
105
+ #
106
+ # @param typed_data [Array] all the data in the typed data structure to be signed.
107
+ # @param chain_id [Integer] the chain id the signature should be generated on.
108
+ # @return [String] an EIP-712 conform, hexa-decimal signature.
109
+ def sign_typed_data(typed_data, chain_id = nil)
110
+ hash_to_sign = Eip712.hash typed_data
111
+ sign hash_to_sign, chain_id
112
+ end
113
+
114
+ # Converts the private key data into a hexa-decimal string.
115
+ #
116
+ # @return [String] private key as hexa-decimal string.
117
+ def private_hex
118
+ Util.bin_to_hex @private_key.data
119
+ end
120
+
121
+ # Exports the private key bytes in a wrapper function to maintain
122
+ # backward-compatibility with older versions of {Eth::Key}.
123
+ #
124
+ # @return [String] private key as packed byte-string.
125
+ def private_bytes
126
+ @private_key.data
127
+ end
128
+
129
+ # Converts the public key data into an uncompressed
130
+ # hexa-decimal string.
131
+ #
132
+ # @return [String] public key as uncompressed hexa-decimal string.
133
+ def public_hex
134
+ Util.bin_to_hex @public_key.uncompressed
135
+ end
136
+
137
+ # Converts the public key data into an compressed
138
+ # hexa-decimal string.
139
+ #
140
+ # @return [String] public key as compressed hexa-decimal string.
141
+ def public_hex_compressed
142
+ Util.bin_to_hex @public_key.compressed
143
+ end
144
+
145
+ # Exports the uncompressed public key bytes in a wrapper function to
146
+ # maintain backward-compatibility with older versions of {Eth::Key}.
147
+ #
148
+ # @return [String] uncompressed public key as packed byte-string.
149
+ def public_bytes
150
+ @public_key.uncompressed
151
+ end
152
+
153
+ # Exports the compressed public key bytes.
154
+ #
155
+ # @return [String] compressed public key as packed byte-string.
156
+ def public_bytes_compressed
157
+ @public_key.compressed
158
+ end
159
+
160
+ # Exports the checksummed public address.
161
+ #
162
+ # @return [Eth::Address] compressed address as packed hex prefixed string.
163
+ def address
164
+ Util.public_key_to_address public_bytes
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,114 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides an recursive-length prefix (RLP) encoder and decoder.
21
+ module Rlp
22
+
23
+ # Provides an RLP-decoder.
24
+ module Decoder
25
+ extend self
26
+
27
+ # Decodes an RLP-encoded object.
28
+ #
29
+ # @param rlp [String] an RLP-encoded object.
30
+ # @return [Object] the decoded and maybe deserialized object.
31
+ # @raise [Eth::Rlp::DecodingError] if the input string does not end after
32
+ # the root item.
33
+ def perform(rlp)
34
+ rlp = Util.hex_to_bin rlp if Util.is_hex? rlp
35
+ rlp = Util.str_to_bytes rlp
36
+ begin
37
+ item, next_start = consume_item rlp, 0
38
+ rescue Exception => e
39
+ raise DecodingError, "Cannot decode rlp string: #{e}"
40
+ end
41
+ raise DecodingError, "RLP string ends with #{rlp.size - next_start} superfluous bytes" if next_start != rlp.size
42
+ return item
43
+ end
44
+
45
+ private
46
+
47
+ # Consume an RLP-encoded item from the given start.
48
+ def consume_item(rlp, start)
49
+ t, l, s = consume_length_prefix rlp, start
50
+ consume_payload rlp, s, t, l
51
+ end
52
+
53
+ # Consume an RLP length prefix at the given position.
54
+ def consume_length_prefix(rlp, start)
55
+ b0 = rlp[start].ord
56
+ if b0 < Constant::PRIMITIVE_PREFIX_OFFSET
57
+
58
+ # single byte
59
+ [:str, 1, start]
60
+ elsif b0 < Constant::PRIMITIVE_PREFIX_OFFSET + Constant::SHORT_LENGTH_LIMIT
61
+ raise DecodingError, "Encoded as short string although single byte was possible" if (b0 - Constant::PRIMITIVE_PREFIX_OFFSET == 1) && rlp[start + 1].ord < Constant::PRIMITIVE_PREFIX_OFFSET
62
+
63
+ # short string
64
+ [:str, b0 - Constant::PRIMITIVE_PREFIX_OFFSET, start + 1]
65
+ elsif b0 < Constant::LIST_PREFIX_OFFSET
66
+ enforce_no_zero_bytes rlp, start
67
+
68
+ # long string
69
+ ll = b0 - Constant::PRIMITIVE_PREFIX_OFFSET - Constant::SHORT_LENGTH_LIMIT + 1
70
+ l = Util.big_endian_to_int rlp[(start + 1)...(start + 1 + ll)]
71
+ raise DecodingError, "Long string prefix used for short string" if l < Constant::SHORT_LENGTH_LIMIT
72
+ [:str, l, start + 1 + ll]
73
+ elsif b0 < Constant::LIST_PREFIX_OFFSET + Constant::SHORT_LENGTH_LIMIT
74
+
75
+ # short list
76
+ [:list, b0 - Constant::LIST_PREFIX_OFFSET, start + 1]
77
+ else
78
+ enforce_no_zero_bytes rlp, start
79
+
80
+ # long list
81
+ ll = b0 - Constant::LIST_PREFIX_OFFSET - Constant::SHORT_LENGTH_LIMIT + 1
82
+ l = Util.big_endian_to_int rlp[(start + 1)...(start + 1 + ll)]
83
+ raise DecodingError, "Long list prefix used for short list" if l < Constant::SHORT_LENGTH_LIMIT
84
+ [:list, l, start + 1 + ll]
85
+ end
86
+ end
87
+
88
+ # Enforce RLP slices to not start with empty bytes.
89
+ def enforce_no_zero_bytes(rlp, start)
90
+ raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == Constant::BYTE_ZERO
91
+ end
92
+
93
+ # Consume an RLP payload at the given position of given type and size.
94
+ def consume_payload(rlp, start, type, length)
95
+ case type
96
+ when :str
97
+ [rlp[start...(start + length)], start + length]
98
+ when :list
99
+ items = []
100
+ next_item_start = start
101
+ payload_end = next_item_start + length
102
+ while next_item_start < payload_end
103
+ item, next_item_start = consume_item rlp, next_item_start
104
+ items.push item
105
+ end
106
+ raise DecodingError, "List length prefix announced a too small length" if next_item_start > payload_end
107
+ [items, next_item_start]
108
+ else
109
+ raise TypeError, "Type must be either :str or :list"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,78 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides an recursive-length prefix (RLP) encoder and decoder.
21
+ module Rlp
22
+
23
+ # Provides an RLP-encoder.
24
+ module Encoder
25
+ extend self
26
+
27
+ # Encodes a Ruby object in RLP format.
28
+ #
29
+ # @param obj [Object] a Ruby object.
30
+ # @return [String] the RLP encoded item.
31
+ # @raise [Eth::Rlp::EncodingError] in the rather unlikely case that the item
32
+ # is too big to encode (will not happen).
33
+ # @raise [Eth::Rlp::SerializationError] if the serialization fails.
34
+ def perform(obj)
35
+ item = Sedes.infer(obj).serialize(obj)
36
+ result = encode_raw item
37
+ end
38
+
39
+ private
40
+
41
+ # Encodes the raw item.
42
+ def encode_raw(item)
43
+ return item if item.instance_of? Rlp::Data
44
+ return encode_primitive item if Util.is_primitive? item
45
+ return encode_list item if Util.is_list? item
46
+ raise EncodingError "Cannot encode object of type #{item.class.name}"
47
+ end
48
+
49
+ # Encodes a single primitive.
50
+ def encode_primitive(item)
51
+ return Util.str_to_bytes item if item.size == 1 && item.ord < Constant::PRIMITIVE_PREFIX_OFFSET
52
+ payload = Util.str_to_bytes item
53
+ prefix = length_prefix payload.size, Constant::PRIMITIVE_PREFIX_OFFSET
54
+ "#{prefix}#{payload}"
55
+ end
56
+
57
+ # Encodes a single list.
58
+ def encode_list(list)
59
+ payload = list.map { |item| encode_raw item }.join
60
+ prefix = length_prefix payload.size, Constant::LIST_PREFIX_OFFSET
61
+ "#{prefix}#{payload}"
62
+ end
63
+
64
+ # Determines a length prefix.
65
+ def length_prefix(length, offset)
66
+ if length < Constant::SHORT_LENGTH_LIMIT
67
+ (offset + length).chr
68
+ elsif length < Constant::LONG_LENGTH_LIMIT
69
+ length_string = Util.int_to_big_endian length
70
+ length_len = (offset + Constant::SHORT_LENGTH_LIMIT - 1 + length_string.size).chr
71
+ "#{length_len}#{length_string}"
72
+ else
73
+ raise EncodingError, "Length greater than 256**8: #{length}"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,66 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides an recursive-length prefix (RLP) encoder and decoder.
21
+ module Rlp
22
+
23
+ # Provides serializable and deserializable types (SeDes).
24
+ module Sedes
25
+
26
+ # A serializable, big-endian, unsigned integer type.
27
+ class BigEndianInt
28
+
29
+ # Create a serializable, big-endian, unsigned integer.
30
+ #
31
+ # @param size [Integer] the size of the big endian.
32
+ def initialize(size = nil)
33
+ @size = size
34
+ end
35
+
36
+ # Serialize a big-endian integer.
37
+ #
38
+ # @param obj [Integer] the integer to be serialized.
39
+ # @return [String] a serialized big-endian integer.
40
+ # @raise [SerializationError] if provided object is not an integer.
41
+ # @raise [SerializationError] if provided integer is negative.
42
+ # @raise [SerializationError] if provided integer is too big for @size.
43
+ def serialize(obj)
44
+ raise SerializationError, "Can only serialize integers" unless obj.is_a?(Integer)
45
+ raise SerializationError, "Cannot serialize negative integers" if obj < 0
46
+ raise SerializationError, "Integer too large (does not fit in #{@size} bytes)" if @size && obj >= 256 ** @size
47
+ s = obj == 0 ? Constant::BYTE_EMPTY : Util.int_to_big_endian(obj)
48
+ @size ? "#{Constant::BYTE_ZERO * [0, @size - s.size].max}#{s}" : s
49
+ end
50
+
51
+ # Deserializes an unsigned integer.
52
+ #
53
+ # @param serial [String] the serialized integer.
54
+ # @return [Integer] a number.
55
+ # @raise [DeserializationError] if provided serial is of wrong size.
56
+ # @raise [DeserializationError] if provided serial is not of minimal length.
57
+ def deserialize(serial)
58
+ raise DeserializationError, "Invalid serialization (wrong size)" if @size && serial.size != @size
59
+ raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == Constant::BYTE_ZERO
60
+ serial = serial || Constant::BYTE_ZERO
61
+ Util.big_endian_to_int(serial)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end