eth 0.5.0 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/codeql.yml +4 -0
- data/.github/workflows/spec.yml +14 -3
- data/.yardopts +1 -0
- data/AUTHORS.txt +5 -0
- data/CHANGELOG.md +63 -13
- data/README.md +57 -2
- data/bin/console +2 -1
- data/bin/setup +3 -4
- data/eth.gemspec +5 -7
- data/lib/eth/abi/type.rb +8 -7
- data/lib/eth/abi.rb +17 -11
- data/lib/eth/address.rb +12 -5
- data/lib/eth/api.rb +223 -0
- data/lib/eth/chain.rb +31 -28
- data/lib/eth/client/http.rb +63 -0
- data/lib/eth/client/ipc.rb +47 -0
- data/lib/eth/client.rb +232 -0
- data/lib/eth/constant.rb +71 -0
- data/lib/eth/eip712.rb +2 -2
- data/lib/eth/key/decrypter.rb +16 -13
- data/lib/eth/key/encrypter.rb +27 -25
- data/lib/eth/key.rb +21 -16
- data/lib/eth/rlp/decoder.rb +109 -0
- data/lib/eth/rlp/encoder.rb +78 -0
- data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
- data/lib/eth/rlp/sedes/binary.rb +97 -0
- data/lib/eth/rlp/sedes/list.rb +84 -0
- data/lib/eth/rlp/sedes.rb +74 -0
- data/lib/eth/rlp.rb +63 -0
- data/lib/eth/signature.rb +11 -8
- data/lib/eth/tx/eip1559.rb +21 -14
- data/lib/eth/tx/eip2930.rb +22 -15
- data/lib/eth/tx/legacy.rb +13 -10
- data/lib/eth/tx.rb +9 -10
- data/lib/eth/unit.rb +1 -1
- data/lib/eth/util.rb +68 -11
- data/lib/eth/version.rb +3 -3
- data/lib/eth.rb +8 -2
- metadata +22 -23
- data/lib/eth/abi/constant.rb +0 -63
data/lib/eth/constant.rb
ADDED
@@ -0,0 +1,71 @@
|
|
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 commonly used constants, such as zero bytes or zero keys.
|
21
|
+
module Constant
|
22
|
+
|
23
|
+
# The empty byte is defined as "".
|
24
|
+
BYTE_EMPTY = "".freeze
|
25
|
+
|
26
|
+
# The zero byte is 0x00.
|
27
|
+
BYTE_ZERO = "\x00".freeze
|
28
|
+
|
29
|
+
# The byte one is 0x01.
|
30
|
+
BYTE_ONE = "\x01".freeze
|
31
|
+
|
32
|
+
# The size of a 32-bit number.
|
33
|
+
TT32 = (2 ** 32).freeze
|
34
|
+
|
35
|
+
# The size of a 256-bit number.
|
36
|
+
TT256 = (2 ** 256).freeze
|
37
|
+
|
38
|
+
# The maximum possible value of an UInt256.
|
39
|
+
UINT_MAX = (2 ** 256 - 1).freeze
|
40
|
+
|
41
|
+
# The minimum possible value of an UInt256.
|
42
|
+
UINT_MIN = 0.freeze
|
43
|
+
|
44
|
+
# The maximum possible value of an Int256.
|
45
|
+
INT_MAX = (2 ** 255 - 1).freeze
|
46
|
+
|
47
|
+
# The minimum possible value of an Int256.
|
48
|
+
INT_MIN = (-2 ** 255).freeze
|
49
|
+
|
50
|
+
# A hash containing only zeros.
|
51
|
+
HASH_ZERO = ("\x00" * 32).freeze
|
52
|
+
|
53
|
+
# The RLP short length limit.
|
54
|
+
SHORT_LENGTH_LIMIT = 56.freeze
|
55
|
+
|
56
|
+
# The RLP long length limit.
|
57
|
+
LONG_LENGTH_LIMIT = (256 ** 8).freeze
|
58
|
+
|
59
|
+
# The RLP primitive type offset.
|
60
|
+
PRIMITIVE_PREFIX_OFFSET = 0x80.freeze
|
61
|
+
|
62
|
+
# The RLP array type offset.
|
63
|
+
LIST_PREFIX_OFFSET = 0xc0.freeze
|
64
|
+
|
65
|
+
# The binary encoding is ASCII (8-bit).
|
66
|
+
BINARY_ENCODING = "ASCII-8BIT".freeze
|
67
|
+
|
68
|
+
# Infinity as constant for convenience.
|
69
|
+
INFINITY = (1.0 / 0.0).freeze
|
70
|
+
end
|
71
|
+
end
|
data/lib/eth/eip712.rb
CHANGED
@@ -12,11 +12,11 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
# Provides the
|
15
|
+
# Provides the {Eth} module.
|
16
16
|
module Eth
|
17
17
|
|
18
18
|
# Defines handy tools for encoding typed structured data as per EIP-712.
|
19
|
-
#
|
19
|
+
# Ref: https://eips.ethereum.org/EIPS/eip-712
|
20
20
|
module Eip712
|
21
21
|
extend self
|
22
22
|
|
data/lib/eth/key/decrypter.rb
CHANGED
@@ -12,37 +12,40 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
# Provides the
|
15
|
+
# Provides the {Eth} module.
|
16
16
|
module Eth
|
17
17
|
|
18
|
-
# The Eth::Key::Decrypter class to handle PBKDF2-SHA-256 decryption.
|
18
|
+
# The {Eth::Key::Decrypter} class to handle PBKDF2-SHA-256 decryption.
|
19
19
|
class Key::Decrypter
|
20
20
|
|
21
21
|
# Provides a specific decrypter error if decryption fails.
|
22
22
|
class DecrypterError < StandardError; end
|
23
23
|
|
24
|
-
# Class method
|
24
|
+
# Class method {Eth::Key::Decrypter.perform} to perform an keystore
|
25
|
+
# decryption.
|
25
26
|
#
|
26
|
-
# @param data [JSON] encryption data including cypherkey
|
27
|
-
# @param password [String] password to decrypt the key
|
28
|
-
# @return [Eth::Key] decrypted key-pair
|
27
|
+
# @param data [JSON] encryption data including cypherkey.
|
28
|
+
# @param password [String] password to decrypt the key.
|
29
|
+
# @return [Eth::Key] decrypted key-pair.
|
29
30
|
def self.perform(data, password)
|
30
31
|
new(data, password).perform
|
31
32
|
end
|
32
33
|
|
33
|
-
# Constructor of the
|
34
|
-
#
|
34
|
+
# Constructor of the {Eth::Key::Decrypter} class for secret key
|
35
|
+
# decryption. Should not be used; use {Eth::Key::Decrypter.perform}
|
36
|
+
# instead.
|
35
37
|
#
|
36
|
-
# @param data [JSON] encryption data including cypherkey
|
37
|
-
# @param password [String] password to decrypt the key
|
38
|
+
# @param data [JSON] encryption data including cypherkey.
|
39
|
+
# @param password [String] password to decrypt the key.
|
38
40
|
def initialize(data, password)
|
39
|
-
|
41
|
+
data = JSON.parse(data) if data.is_a? String
|
42
|
+
@data = data
|
40
43
|
@password = password
|
41
44
|
end
|
42
45
|
|
43
|
-
# Method to decrypt key using password
|
46
|
+
# Method to decrypt key using password.
|
44
47
|
#
|
45
|
-
# @return [
|
48
|
+
# @return [Eth::Key] decrypted key.
|
46
49
|
def perform
|
47
50
|
derive_key password
|
48
51
|
check_macs
|
data/lib/eth/key/encrypter.rb
CHANGED
@@ -12,45 +12,47 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
# Provides the
|
15
|
+
# Provides the {Eth} module.
|
16
16
|
module Eth
|
17
17
|
|
18
|
-
# The Eth::Key::Encrypter class to handle PBKDF2-SHA-256 encryption.
|
18
|
+
# The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.
|
19
19
|
class Key::Encrypter
|
20
20
|
|
21
21
|
# Provides a specific encrypter error if decryption fails.
|
22
22
|
class EncrypterError < StandardError; end
|
23
23
|
|
24
|
-
# Class method
|
24
|
+
# Class method {Eth::Key::Encrypter.perform} to performa an key-store
|
25
25
|
# encryption.
|
26
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)
|
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
37
|
def self.perform(key, password, options = {})
|
38
38
|
new(key, options).perform(password)
|
39
39
|
end
|
40
40
|
|
41
|
-
# Constructor of the
|
42
|
-
# encryption.
|
41
|
+
# Constructor of the {Eth::Key::Encrypter} class for secret key
|
42
|
+
# encryption. Should not be used; use {Eth::Key::Encrypter.perform}
|
43
|
+
# instead.
|
43
44
|
#
|
44
|
-
# @param key [Eth::Key] representing a secret key-pair used for encryption
|
45
|
-
# @param options [Hash] the options to encrypt with
|
46
|
-
# @option options [String] :kdf key derivation function defaults to pbkdf2
|
47
|
-
# @option options [String] :id uuid given to the secret key
|
48
|
-
# @option options [String] :iterations number of iterations for the hash function
|
49
|
-
# @option options [String] :salt passed to PBKDF
|
50
|
-
# @option options [String] :iv 128-bit initialisation vector for the cipher
|
51
|
-
# @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8
|
52
|
-
# @option options [Integer] :block_size for scrypt, defaults to 1
|
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.
|
53
54
|
def initialize(key, options = {})
|
55
|
+
key = Key.new(priv: key) if key.is_a? String
|
54
56
|
@key = key
|
55
57
|
@options = options
|
56
58
|
|
@@ -64,7 +66,7 @@ module Eth
|
|
64
66
|
# Encrypt the key with a given password.
|
65
67
|
#
|
66
68
|
# @param password [String] a secret key used for encryption
|
67
|
-
# @return [String] a
|
69
|
+
# @return [String] a JSON-formatted keystore string.
|
68
70
|
def perform(password)
|
69
71
|
derive_key password
|
70
72
|
encrypt
|
data/lib/eth/key.rb
CHANGED
@@ -18,28 +18,28 @@ require "rbsecp256k1"
|
|
18
18
|
require "scrypt"
|
19
19
|
require "securerandom"
|
20
20
|
|
21
|
-
# Provides the
|
21
|
+
# Provides the {Eth} module.
|
22
22
|
module Eth
|
23
23
|
|
24
|
-
# The
|
24
|
+
# The {Eth::Key} class to handle Secp256k1 private/public key-pairs.
|
25
25
|
class Key
|
26
26
|
|
27
|
-
# The Eth::Key::Decrypter class to handle PBKDF2-SHA-256 decryption.
|
27
|
+
# The {Eth::Key::Decrypter} class to handle PBKDF2-SHA-256 decryption.
|
28
28
|
autoload :Decrypter, "eth/key/decrypter"
|
29
29
|
|
30
|
-
# The Eth::Key::Encrypter class to handle PBKDF2-SHA-256 encryption.
|
30
|
+
# The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.
|
31
31
|
autoload :Encrypter, "eth/key/encrypter"
|
32
32
|
|
33
|
-
# The `Secp256k1::PrivateKey` of the
|
33
|
+
# The `Secp256k1::PrivateKey` of the {Eth::Key} pair.
|
34
34
|
attr_reader :private_key
|
35
35
|
|
36
|
-
# The `Secp256k1::PublicKey` of the
|
36
|
+
# The `Secp256k1::PublicKey` of the {Eth::Key} pair.
|
37
37
|
attr_reader :public_key
|
38
38
|
|
39
|
-
# Constructor of the
|
39
|
+
# Constructor of the {Eth::Key} class. Creates a new random key-pair
|
40
40
|
# if no `priv` key is provided.
|
41
41
|
#
|
42
|
-
# @param priv [String] binary string of private key data
|
42
|
+
# @param priv [String] binary string of private key data.
|
43
43
|
def initialize(priv: nil)
|
44
44
|
|
45
45
|
# Creates a new, randomized libsecp256k1 context.
|
@@ -63,9 +63,10 @@ module Eth
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Signs arbitrary data without validation. Should not be used unless really
|
66
|
-
# desired. See also: personal_sign, sign_typed_data
|
66
|
+
# desired. See also: {Key.personal_sign}, {Key.sign_typed_data}, and
|
67
|
+
# {Signature.recover}.
|
67
68
|
#
|
68
|
-
# @param blob [
|
69
|
+
# @param blob [Object] that arbitrary data to be signed.
|
69
70
|
# @param chain_id [Integer] the chain id the signature should be generated on.
|
70
71
|
# @return [String] a hexa-decimal signature.
|
71
72
|
def sign(blob, chain_id = nil)
|
@@ -81,9 +82,11 @@ module Eth
|
|
81
82
|
Util.bin_to_hex signature.pack "c*"
|
82
83
|
end
|
83
84
|
|
84
|
-
# Prefixes a message with
|
85
|
+
# Prefixes a message with `\x19Ethereum Signed Message:` and signs
|
85
86
|
# it in the common way used by many web3 wallets. Complies with
|
86
|
-
# EIP-191 prefix 0x19 and version byte 0x45 (E).
|
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
|
87
90
|
#
|
88
91
|
# @param message [String] the message string to be prefixed and signed.
|
89
92
|
# @param chain_id [Integer] the chain id the signature should be generated on.
|
@@ -95,8 +98,10 @@ module Eth
|
|
95
98
|
end
|
96
99
|
|
97
100
|
# Prefixes, hashes, and signes a typed data structure in the common
|
98
|
-
# way used by many web3 wallets. Complies with EIP-191 prefix 0x19
|
99
|
-
# and EIP-712 version byte 0x01
|
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
|
100
105
|
#
|
101
106
|
# @param typed_data [Array] all the data in the typed data structure to be signed.
|
102
107
|
# @param chain_id [Integer] the chain id the signature should be generated on.
|
@@ -114,7 +119,7 @@ module Eth
|
|
114
119
|
end
|
115
120
|
|
116
121
|
# Exports the private key bytes in a wrapper function to maintain
|
117
|
-
# backward-compatibility with older versions of
|
122
|
+
# backward-compatibility with older versions of {Eth::Key}.
|
118
123
|
#
|
119
124
|
# @return [String] private key as packed byte-string.
|
120
125
|
def private_bytes
|
@@ -138,7 +143,7 @@ module Eth
|
|
138
143
|
end
|
139
144
|
|
140
145
|
# Exports the uncompressed public key bytes in a wrapper function to
|
141
|
-
# maintain backward-compatibility with older versions of
|
146
|
+
# maintain backward-compatibility with older versions of {Eth::Key}.
|
142
147
|
#
|
143
148
|
# @return [String] uncompressed public key as packed byte-string.
|
144
149
|
def public_bytes
|
@@ -0,0 +1,109 @@
|
|
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
|
+
raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == Constant::BYTE_ZERO
|
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
|
+
raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == Constant::BYTE_ZERO
|
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
|
+
# Consume an RLP payload at the given position of given type and size.
|
89
|
+
def consume_payload(rlp, start, type, length)
|
90
|
+
case type
|
91
|
+
when :str
|
92
|
+
[rlp[start...(start + length)], start + length]
|
93
|
+
when :list
|
94
|
+
items = []
|
95
|
+
next_item_start = start
|
96
|
+
payload_end = next_item_start + length
|
97
|
+
while next_item_start < payload_end
|
98
|
+
item, next_item_start = consume_item rlp, next_item_start
|
99
|
+
items.push item
|
100
|
+
end
|
101
|
+
raise DecodingError, "List length prefix announced a too small length" if next_item_start > payload_end
|
102
|
+
[items, next_item_start]
|
103
|
+
else
|
104
|
+
raise TypeError, "Type must be either :str or :list"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
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
|
@@ -0,0 +1,97 @@
|
|
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 sedes type for binary values.
|
27
|
+
class Binary
|
28
|
+
|
29
|
+
# A singleton class for binary values of fixed length.
|
30
|
+
class << self
|
31
|
+
|
32
|
+
# Create a serializable bianry of fixed size.
|
33
|
+
#
|
34
|
+
# @param l [Integer] the fixed size of the binary.
|
35
|
+
# @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
|
36
|
+
# @return [Eth::Rlp::Sedes::Binary] a serializable binary of fixed size.
|
37
|
+
def fixed_length(l, allow_empty: false)
|
38
|
+
new(min_length: l, max_length: l, allow_empty: allow_empty)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Checks wether the given object is of a valid binary type.
|
42
|
+
#
|
43
|
+
# @param obj [Object] the supposed binary item to check.
|
44
|
+
# @return [Boolean] true if valid.
|
45
|
+
def valid_type?(obj)
|
46
|
+
obj.instance_of? String
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create a serializable bianry of variable size.
|
51
|
+
#
|
52
|
+
# @param min_length [Integer] the minimum size of the binary.
|
53
|
+
# @param max_length [Integer] the maximum size of the binary.
|
54
|
+
# @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
|
55
|
+
def initialize(min_length: 0, max_length: Constant::INFINITY, allow_empty: false)
|
56
|
+
@min_length = min_length
|
57
|
+
@max_length = max_length
|
58
|
+
@allow_empty = allow_empty
|
59
|
+
end
|
60
|
+
|
61
|
+
# Serializes a binary.
|
62
|
+
#
|
63
|
+
# @param obj [String] the binary to serialize.
|
64
|
+
# @return [Object] a serialized binary.
|
65
|
+
# @raise [SerializationError] if provided object is of invalid type.
|
66
|
+
# @raise [SerializationError] if provided binary is of invalid length.
|
67
|
+
def serialize(obj)
|
68
|
+
raise SerializationError, "Object is not a serializable (#{obj.class})" unless self.class.valid_type? obj
|
69
|
+
serial = Util.str_to_bytes obj
|
70
|
+
raise SerializationError, "Object has invalid length" unless valid_length? serial.size
|
71
|
+
serial
|
72
|
+
end
|
73
|
+
|
74
|
+
# Deserializes a binary.
|
75
|
+
#
|
76
|
+
# @param serial [Object] the serialized binary.
|
77
|
+
# @return [String] a deserialized binary.
|
78
|
+
# @raise [DeserializationError] if provided serial is of wrong type.
|
79
|
+
# @raise [DeserializationError] if provided serial is of wrong length.
|
80
|
+
def deserialize(serial)
|
81
|
+
raise DeserializationError, "Objects of type #{serial.class} cannot be deserialized" unless Util.is_primitive? serial
|
82
|
+
raise DeserializationError, "#{serial.class} has invalid length" unless valid_length? serial.size
|
83
|
+
serial
|
84
|
+
end
|
85
|
+
|
86
|
+
# Checks wether the given length fits the defined size boundaries of the
|
87
|
+
# binary type.
|
88
|
+
#
|
89
|
+
# @param length [Integer] the supposed length of the binary item.
|
90
|
+
# @return [Boolean] true if valid.
|
91
|
+
def valid_length?(length)
|
92
|
+
(@min_length <= length && length <= @max_length) || (@allow_empty && length == 0)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|