eth 0.5.0 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +4 -0
  3. data/.github/workflows/spec.yml +14 -3
  4. data/.yardopts +1 -0
  5. data/AUTHORS.txt +14 -1
  6. data/CHANGELOG.md +63 -13
  7. data/README.md +121 -18
  8. data/bin/console +2 -1
  9. data/bin/setup +3 -4
  10. data/codecov.yml +6 -0
  11. data/eth.gemspec +5 -7
  12. data/lib/eth/abi/event.rb +137 -0
  13. data/lib/eth/abi/type.rb +8 -7
  14. data/lib/eth/abi.rb +29 -11
  15. data/lib/eth/address.rb +12 -5
  16. data/lib/eth/api.rb +223 -0
  17. data/lib/eth/chain.rb +31 -28
  18. data/lib/eth/client/http.rb +63 -0
  19. data/lib/eth/client/ipc.rb +50 -0
  20. data/lib/eth/client.rb +468 -0
  21. data/lib/eth/constant.rb +71 -0
  22. data/lib/eth/contract/event.rb +41 -0
  23. data/lib/eth/contract/function.rb +56 -0
  24. data/lib/eth/contract/function_input.rb +36 -0
  25. data/lib/eth/contract/function_output.rb +32 -0
  26. data/lib/eth/contract/initializer.rb +46 -0
  27. data/lib/eth/contract.rb +120 -0
  28. data/lib/eth/eip712.rb +2 -2
  29. data/lib/eth/key/decrypter.rb +16 -13
  30. data/lib/eth/key/encrypter.rb +27 -25
  31. data/lib/eth/key.rb +21 -16
  32. data/lib/eth/rlp/decoder.rb +114 -0
  33. data/lib/eth/rlp/encoder.rb +78 -0
  34. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  35. data/lib/eth/rlp/sedes/binary.rb +97 -0
  36. data/lib/eth/rlp/sedes/list.rb +84 -0
  37. data/lib/eth/rlp/sedes.rb +74 -0
  38. data/lib/eth/rlp.rb +63 -0
  39. data/lib/eth/signature.rb +11 -8
  40. data/lib/eth/solidity.rb +75 -0
  41. data/lib/eth/tx/eip1559.rb +22 -14
  42. data/lib/eth/tx/eip2930.rb +23 -15
  43. data/lib/eth/tx/legacy.rb +14 -10
  44. data/lib/eth/tx.rb +28 -34
  45. data/lib/eth/unit.rb +1 -1
  46. data/lib/eth/util.rb +68 -11
  47. data/lib/eth/version.rb +3 -3
  48. data/lib/eth.rb +15 -2
  49. metadata +31 -23
  50. data/lib/eth/abi/constant.rb +0 -63
@@ -0,0 +1,36 @@
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
+ # Provide classes for contract function input.
20
+ class Contract::FunctionInput
21
+ attr_accessor :type, :name
22
+
23
+ # Constructor of the {Eth::Contract::FunctionInput} class.
24
+ #
25
+ # @param data [Hash] contract abi data.
26
+ def initialize(data)
27
+ @type = Eth::Abi::Type.parse(data["type"])
28
+ @name = data["name"]
29
+ end
30
+
31
+ # Returns types like uint256
32
+ def type
33
+ @type.base_type + @type.sub_type
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
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
+ # Provide classes for contract function output.
20
+ class Contract::FunctionOutput
21
+ attr_accessor :type, :name
22
+
23
+ def initialize(data)
24
+ @type = Eth::Abi::Type.parse(data["type"])
25
+ @name = data["name"]
26
+ end
27
+
28
+ def type
29
+ @type.base_type + @type.sub_type
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
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
+ # Provide classes for contract initializer.
20
+ class Contract::Initializer
21
+ attr_accessor :contracts, :file
22
+
23
+ # Constructor of the {Eth::Contract::Initializer} class.
24
+ #
25
+ # @param file [String] file path to solidity code.
26
+ def initialize(file)
27
+ sol_output = Eth::Solidity.new.compile(file)
28
+ contracts = sol_output.keys
29
+
30
+ @contracts = []
31
+ contracts.each do |contract|
32
+ abi = sol_output[contract]["abi"]
33
+ name = contract
34
+ code = sol_output[contract]["bin"]
35
+ @contracts << Contract.new(name, code, abi)
36
+ end
37
+ end
38
+
39
+ # Build and return all contracts.
40
+ def build_all
41
+ @contracts.each do |contract|
42
+ contract.build
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,120 @@
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 classes to access smart contracts
21
+ class Contract
22
+ attr_reader :address
23
+ attr_accessor :key
24
+ attr_accessor :gas_limit, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :nonce
25
+ attr_accessor :bin, :name, :abi, :class_object
26
+ attr_accessor :events, :functions, :constructor_inputs
27
+
28
+ # Constructor of the {Eth::Contract} class.
29
+ #
30
+ # @param name [String] contract name.
31
+ # @param bin [String] contract bin string.
32
+ # @param abi [String] contract abi string.
33
+ def initialize(name, bin, abi)
34
+ @name = name
35
+ @bin = bin
36
+ @abi = abi
37
+ @constructor_inputs, @functions, @events = parse_abi(abi)
38
+ end
39
+
40
+ # Creates a contract wrapper.
41
+ #
42
+ # @param file [String] solidity file path.
43
+ # @param bin [String] contract bin string.
44
+ # @param abi [String] contract abi string.
45
+ # @param address [String] contract address.
46
+ # @param name [String] name of contract.
47
+ # @param contract_index [Number] specify contract.
48
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
49
+ # @raise [JSON::ParserError] if the json format is wrong.
50
+ # @raise [ArgumentError] if argument is incorrect.
51
+ def self.create(file: nil, bin: nil, abi: nil, address: nil, name: nil, contract_index: nil)
52
+ if File.exist?(file.to_s)
53
+ contracts = Eth::Contract::Initializer.new(file).build_all
54
+ raise "No contracts compiled" if contracts.empty?
55
+ if contract_index
56
+ contract = contracts[contract_index].class_object.new
57
+ else
58
+ contract = contracts.first.class_object.new
59
+ end
60
+ elsif ![name, bin, abi].include? nil
61
+ begin
62
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
63
+ rescue JSON::ParserError => e
64
+ raise e
65
+ end
66
+ contract = Eth::Contract.new(name, bin, abi)
67
+ contract.build
68
+ contract = contract.class_object.new
69
+ else
70
+ raise ArgumentError, "The argument is incorrect."
71
+ end
72
+ contract.address = address
73
+ contract
74
+ end
75
+
76
+ # Set the address of the smart contract
77
+ def address=(addr)
78
+ @address = addr.nil? ? nil : Eth::Address.new(addr).address
79
+ @events.each do |event|
80
+ event.set_address(@address)
81
+ end
82
+ end
83
+
84
+ # Create classes for smart contracts
85
+ def build
86
+ class_name = @name
87
+ parent = self
88
+ class_methods = Class.new do
89
+ extend Forwardable
90
+ def_delegators :parent, :key, :key=
91
+ def_delegators :parent, :name, :abi, :bin
92
+ def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
93
+ def_delegators :parent, :max_fee_per_gas, :max_fee_per_gas=, :max_priority_fee_per_gas, :max_priority_fee_per_gas=
94
+ def_delegators :parent, :events
95
+ def_delegators :parent, :address, :address=
96
+ def_delegator :parent, :functions
97
+ define_method :parent do
98
+ parent
99
+ end
100
+ end
101
+ Eth::Contract.send(:remove_const, class_name) if Eth::Contract.const_defined?(class_name, false)
102
+ Eth::Contract.const_set(class_name, class_methods)
103
+ @class_object = class_methods
104
+ end
105
+
106
+ private
107
+
108
+ def parse_abi(abi)
109
+ constructor = abi.detect { |x| x["type"] == "constructor" }
110
+ if !constructor.nil?
111
+ constructor_inputs = constructor["inputs"].map { |input| Eth::Contract::FunctionInput.new(input) }
112
+ else
113
+ constructor_inputs = []
114
+ end
115
+ functions = abi.select { |x| x["type"] == "function" }.map { |fun| Eth::Contract::Function.new(fun) }
116
+ events = abi.select { |x| x["type"] == "event" }.map { |evt| Eth::Contract::Event.new(evt) }
117
+ [constructor_inputs, functions, events]
118
+ end
119
+ end
120
+ 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 `Eth` module.
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
- # ref: https://eips.ethereum.org/EIPS/eip-712
19
+ # Ref: https://eips.ethereum.org/EIPS/eip-712
20
20
  module Eip712
21
21
  extend self
22
22
 
@@ -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 `Eth` module.
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 `Eth::Key::Decrypter.perform`
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 `Eth::Key::Decrypter` class for secret key
34
- # encryption.
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
- @data = JSON.parse(data)
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 [String] decrypted key
48
+ # @return [Eth::Key] decrypted key.
46
49
  def perform
47
50
  derive_key password
48
51
  check_macs
@@ -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 `Eth` module.
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 `Eth::Key::Encrypter.perform` to performa an key-store
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 `Eth::Key::Encrypter` class for secret key
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 json-formatted keystore string.
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 `Eth` module.
21
+ # Provides the {Eth} module.
22
22
  module Eth
23
23
 
24
- # The `Eth::Key` class to handle Secp256k1 private/public key-pairs.
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 `Eth::Key` pair.
33
+ # The `Secp256k1::PrivateKey` of the {Eth::Key} pair.
34
34
  attr_reader :private_key
35
35
 
36
- # The `Secp256k1::PublicKey` of the `Eth::Key` pair.
36
+ # The `Secp256k1::PublicKey` of the {Eth::Key} pair.
37
37
  attr_reader :public_key
38
38
 
39
- # Constructor of the `Eth::Key` class. Creates a new random key-pair
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 (optional).
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 [String] that arbitrary data to be signed.
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 "\x19Ethereum Signed Message:" and signs
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. Supports `V3`, `V4`.
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 `Eth::Key`.
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 `Eth::Key`.
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,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