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.
- checksums.yaml +4 -4
- data/.github/workflows/codeql.yml +15 -5
- data/.github/workflows/docs.yml +26 -0
- data/.github/workflows/spec.yml +52 -0
- data/.gitignore +24 -24
- data/.gitmodules +3 -3
- data/.yardopts +1 -0
- data/AUTHORS.txt +21 -0
- data/CHANGELOG.md +65 -10
- data/Gemfile +13 -2
- data/LICENSE.txt +202 -22
- data/README.md +199 -74
- data/bin/console +4 -4
- data/bin/setup +5 -4
- data/eth.gemspec +34 -29
- data/lib/eth/abi/type.rb +178 -0
- data/lib/eth/abi.rb +396 -0
- data/lib/eth/address.rb +55 -11
- data/lib/eth/api.rb +223 -0
- data/lib/eth/chain.rb +151 -0
- 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 +184 -0
- data/lib/eth/key/decrypter.rb +121 -88
- data/lib/eth/key/encrypter.rb +178 -99
- data/lib/eth/key.rb +136 -48
- 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 +163 -0
- data/lib/eth/tx/eip1559.rb +336 -0
- data/lib/eth/tx/eip2930.rb +328 -0
- data/lib/eth/tx/legacy.rb +296 -0
- data/lib/eth/tx.rb +273 -143
- data/lib/eth/unit.rb +49 -0
- data/lib/eth/util.rb +235 -0
- data/lib/eth/version.rb +18 -1
- data/lib/eth.rb +33 -67
- metadata +50 -85
- data/.github/workflows/build.yml +0 -26
- data/lib/eth/gas.rb +0 -9
- data/lib/eth/open_ssl.rb +0 -264
- data/lib/eth/secp256k1.rb +0 -7
- data/lib/eth/sedes.rb +0 -40
- data/lib/eth/utils.rb +0 -130
data/lib/eth/key.rb
CHANGED
@@ -1,79 +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.
|
1
22
|
module Eth
|
23
|
+
|
24
|
+
# The {Eth::Key} class to handle Secp256k1 private/public key-pairs.
|
2
25
|
class Key
|
3
|
-
autoload :Decrypter, 'eth/key/decrypter'
|
4
|
-
autoload :Encrypter, 'eth/key/encrypter'
|
5
26
|
|
6
|
-
|
27
|
+
# The {Eth::Key::Decrypter} class to handle PBKDF2-SHA-256 decryption.
|
28
|
+
autoload :Decrypter, "eth/key/decrypter"
|
7
29
|
|
8
|
-
|
9
|
-
|
30
|
+
# The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.
|
31
|
+
autoload :Encrypter, "eth/key/encrypter"
|
10
32
|
|
11
|
-
|
12
|
-
|
33
|
+
# The `Secp256k1::PrivateKey` of the {Eth::Key} pair.
|
34
|
+
attr_reader :private_key
|
13
35
|
|
14
|
-
|
15
|
-
|
16
|
-
new priv: priv
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.personal_recover(message, signature)
|
20
|
-
bin_signature = Utils.hex_to_bin(signature).bytes.rotate(-1).pack('c*')
|
21
|
-
OpenSsl.recover_compact(Utils.keccak256(Utils.prefix_message(message)), bin_signature)
|
22
|
-
end
|
36
|
+
# The `Secp256k1::PublicKey` of the {Eth::Key} pair.
|
37
|
+
attr_reader :public_key
|
23
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.
|
24
43
|
def initialize(priv: nil)
|
25
|
-
@private_key = MoneyTree::PrivateKey.new key: priv
|
26
|
-
@public_key = MoneyTree::PublicKey.new private_key, compressed: false
|
27
|
-
end
|
28
44
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
45
|
+
# Creates a new, randomized libsecp256k1 context.
|
46
|
+
ctx = Secp256k1::Context.new context_randomization_bytes: SecureRandom.random_bytes(32)
|
32
47
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
48
|
+
# Creates a new random key pair (public, private).
|
49
|
+
key = ctx.generate_key_pair
|
36
50
|
|
37
|
-
|
38
|
-
|
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
|
39
63
|
end
|
40
64
|
|
41
|
-
|
42
|
-
|
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*"
|
43
83
|
end
|
44
|
-
alias_method :to_address, :address
|
45
84
|
|
46
|
-
|
47
|
-
|
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
|
48
98
|
end
|
49
99
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
55
112
|
end
|
56
113
|
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
60
119
|
end
|
61
120
|
|
62
|
-
|
63
|
-
|
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
|
64
127
|
end
|
65
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
|
66
136
|
|
67
|
-
|
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
|
68
144
|
|
69
|
-
|
70
|
-
|
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
|
71
151
|
end
|
72
152
|
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
76
158
|
end
|
77
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
|
78
166
|
end
|
79
167
|
end
|
@@ -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
|
@@ -0,0 +1,84 @@
|
|
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 lists of fixed length.
|
27
|
+
class List < Array
|
28
|
+
|
29
|
+
# Create a serializable list of fixed size.
|
30
|
+
#
|
31
|
+
# @param elements [Array] an array indicating the structure of the list.
|
32
|
+
# @param strict [Boolean] an option to enforce the given structure.
|
33
|
+
def initialize(elements: [], strict: true)
|
34
|
+
super()
|
35
|
+
@strict = strict
|
36
|
+
elements.each do |e|
|
37
|
+
if Sedes.is_sedes?(e)
|
38
|
+
push e
|
39
|
+
elsif Util.is_list?(e)
|
40
|
+
push List.new(elements: e)
|
41
|
+
else
|
42
|
+
raise TypeError, "Instances of List must only contain sedes objects or nested sequences thereof."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Serialize an array.
|
48
|
+
#
|
49
|
+
# @param obj [Array] the array to be serialized.
|
50
|
+
# @return [Array] a serialized list.
|
51
|
+
# @raise [SerializationError] if provided array is not a sequence.
|
52
|
+
# @raise [SerializationError] if provided array is of wrong length.
|
53
|
+
def serialize(obj)
|
54
|
+
raise SerializationError, "Can only serialize sequences" unless Util.is_list?(obj)
|
55
|
+
raise SerializationError, "List has wrong length" if (@strict && self.size != obj.size) || self.size < obj.size
|
56
|
+
result = []
|
57
|
+
obj.zip(self).each_with_index do |(element, sedes), i|
|
58
|
+
result.push sedes.serialize(element)
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# Deserializes a list.
|
64
|
+
#
|
65
|
+
# @param serial [Array] the serialized list.
|
66
|
+
# @return [Array] a deserialized list.
|
67
|
+
# @raise [DeserializationError] if provided serial is not a sequence.
|
68
|
+
# @raise [DeserializationError] if provided serial is of wrong length.
|
69
|
+
def deserialize(serial)
|
70
|
+
raise DeserializationError, "Can only deserialize sequences" unless Util.is_list?(serial)
|
71
|
+
raise DeserializationError, "List has wrong length" if @strict && serial.size != self.size
|
72
|
+
result = []
|
73
|
+
len = [serial.size, self.size].min
|
74
|
+
len.times do |i|
|
75
|
+
sedes = self[i]
|
76
|
+
element = serial[i]
|
77
|
+
result.push sedes.deserialize(element)
|
78
|
+
end
|
79
|
+
result.freeze
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|