eth 0.4.18 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +6 -2
  3. data/.github/workflows/docs.yml +1 -1
  4. data/.github/workflows/spec.yml +52 -0
  5. data/.gitignore +24 -24
  6. data/.gitmodules +3 -3
  7. data/.yardopts +1 -0
  8. data/AUTHORS.txt +27 -0
  9. data/CHANGELOG.md +63 -13
  10. data/Gemfile +12 -4
  11. data/LICENSE.txt +202 -22
  12. data/README.md +231 -76
  13. data/bin/console +4 -4
  14. data/bin/setup +5 -4
  15. data/codecov.yml +6 -0
  16. data/eth.gemspec +23 -19
  17. data/lib/eth/abi/type.rb +178 -0
  18. data/lib/eth/abi.rb +396 -0
  19. data/lib/eth/address.rb +57 -10
  20. data/lib/eth/api.rb +223 -0
  21. data/lib/eth/chain.rb +151 -0
  22. data/lib/eth/client/http.rb +63 -0
  23. data/lib/eth/client/ipc.rb +50 -0
  24. data/lib/eth/client.rb +232 -0
  25. data/lib/eth/constant.rb +71 -0
  26. data/lib/eth/eip712.rb +184 -0
  27. data/lib/eth/key/decrypter.rb +121 -85
  28. data/lib/eth/key/encrypter.rb +180 -99
  29. data/lib/eth/key.rb +134 -45
  30. data/lib/eth/rlp/decoder.rb +114 -0
  31. data/lib/eth/rlp/encoder.rb +78 -0
  32. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  33. data/lib/eth/rlp/sedes/binary.rb +97 -0
  34. data/lib/eth/rlp/sedes/list.rb +84 -0
  35. data/lib/eth/rlp/sedes.rb +74 -0
  36. data/lib/eth/rlp.rb +63 -0
  37. data/lib/eth/signature.rb +163 -0
  38. data/lib/eth/solidity.rb +75 -0
  39. data/lib/eth/tx/eip1559.rb +337 -0
  40. data/lib/eth/tx/eip2930.rb +329 -0
  41. data/lib/eth/tx/legacy.rb +297 -0
  42. data/lib/eth/tx.rb +269 -146
  43. data/lib/eth/unit.rb +49 -0
  44. data/lib/eth/util.rb +235 -0
  45. data/lib/eth/version.rb +18 -1
  46. data/lib/eth.rb +34 -67
  47. metadata +47 -95
  48. data/.github/workflows/build.yml +0 -36
  49. data/lib/eth/gas.rb +0 -7
  50. data/lib/eth/open_ssl.rb +0 -395
  51. data/lib/eth/secp256k1.rb +0 -5
  52. data/lib/eth/sedes.rb +0 -39
  53. data/lib/eth/utils.rb +0 -126
@@ -0,0 +1,297 @@
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
+ # Provides the `Tx` module supporting various transaction types.
19
+ module Tx
20
+
21
+ # Provides legacy support for transactions on blockchains that do not
22
+ # implement EIP-1559, EIP-2718, or EIP-2930.
23
+ class Legacy
24
+
25
+ # The transaction nonce provided by the signer.
26
+ attr_reader :signer_nonce
27
+
28
+ # The gas price for the transaction in Wei.
29
+ attr_reader :gas_price
30
+
31
+ # The gas limit for the transaction.
32
+ attr_reader :gas_limit
33
+
34
+ # The recipient address.
35
+ attr_reader :destination
36
+
37
+ # The transaction amount in Wei.
38
+ attr_reader :amount
39
+
40
+ # The transaction data payload.
41
+ attr_reader :payload
42
+
43
+ # The signature `v` byte.
44
+ attr_reader :signature_v
45
+
46
+ # The signature `r` value.
47
+ attr_reader :signature_r
48
+
49
+ # The signature `s` value.
50
+ attr_reader :signature_s
51
+
52
+ # The EIP-155 chain ID field.
53
+ # Ref: https://eips.ethereum.org/EIPS/eip-155
54
+ attr_reader :chain_id
55
+
56
+ # The sender address.
57
+ attr_reader :sender
58
+
59
+ # The transaction type.
60
+ attr_reader :type
61
+
62
+ # Create a legacy transaction object that can be prepared for
63
+ # signature and broadcast. Should not be used unless there is
64
+ # no EIP-1559 support.
65
+ #
66
+ # @param params [Hash] all necessary transaction fields.
67
+ # @option params [Integer] :nonce the signer nonce.
68
+ # @option params [Integer] :gas_price the gas price.
69
+ # @option params [Integer] :gas_limit the gas limit.
70
+ # @option params [Eth::Address] :from the sender address.
71
+ # @option params [Eth::Address] :to the reciever address.
72
+ # @option params [Integer] :value the transaction value.
73
+ # @option params [String] :data the transaction data payload.
74
+ # @param chain_id [Integer] the EIP-155 Chain ID.
75
+ # @raise [ParameterError] if gas limit is too low.
76
+ def initialize(params, chain_id = Chain::ETHEREUM)
77
+ fields = { v: chain_id, r: 0, s: 0 }.merge params
78
+
79
+ # populate optional fields with serializable empty values
80
+ fields[:value] = Tx.sanitize_amount fields[:value]
81
+ fields[:from] = Tx.sanitize_address fields[:from]
82
+ fields[:to] = Tx.sanitize_address fields[:to]
83
+ fields[:data] = Tx.sanitize_data fields[:data]
84
+
85
+ # ensure sane values for all mandatory fields
86
+ fields = Tx.validate_params fields
87
+ fields = Tx.validate_legacy_params fields
88
+
89
+ # ensure gas limit is not too low
90
+ minimum_cost = Tx.estimate_intrinsic_gas fields[:data]
91
+ raise ParameterError, "Transaction gas limit is too low, try #{minimum_cost}!" if fields[:gas_limit].to_i < minimum_cost
92
+
93
+ # populate class attributes
94
+ @signer_nonce = fields[:nonce].to_i
95
+ @gas_price = fields[:gas_price].to_i
96
+ @gas_limit = fields[:gas_limit].to_i
97
+ @sender = fields[:from].to_s
98
+ @destination = fields[:to].to_s
99
+ @amount = fields[:value].to_i
100
+ @payload = fields[:data]
101
+
102
+ # the signature v is set to the chain id for unsigned transactions
103
+ @signature_v = fields[:v]
104
+ @chain_id = chain_id
105
+
106
+ # the signature fields are empty for unsigned transactions.
107
+ @signature_r = fields[:r]
108
+ @signature_s = fields[:s]
109
+
110
+ # last but not least, set the type.
111
+ @type = TYPE_LEGACY
112
+ end
113
+
114
+ # overloads the constructor for decoding raw transactions and creating unsigned copies
115
+ konstructor :decode, :unsigned_copy
116
+
117
+ # Decodes a raw transaction hex into an {Eth::Tx::Legacy}
118
+ # transaction object.
119
+ #
120
+ # @param hex [String] the raw transaction hex-string.
121
+ # @return [Eth::Tx::Legacy] transaction object.
122
+ # @raise [ParameterError] if transaction misses fields.
123
+ def decode(hex)
124
+ bin = Util.hex_to_bin hex
125
+ tx = Rlp.decode bin
126
+
127
+ # decoded transactions always have 9 fields, even if they are empty or zero
128
+ raise ParameterError, "Transaction missing fields!" if tx.size < 9
129
+
130
+ # populate the 9 fields
131
+ nonce = Util.deserialize_big_endian_to_int tx[0]
132
+ gas_price = Util.deserialize_big_endian_to_int tx[1]
133
+ gas_limit = Util.deserialize_big_endian_to_int tx[2]
134
+ to = Util.bin_to_hex tx[3]
135
+ value = Util.deserialize_big_endian_to_int tx[4]
136
+ data = tx[5]
137
+ v = Util.bin_to_hex tx[6]
138
+ r = Util.bin_to_hex tx[7]
139
+ s = Util.bin_to_hex tx[8]
140
+
141
+ # try to recover the chain id from v
142
+ chain_id = Chain.to_chain_id Util.deserialize_big_endian_to_int tx[6]
143
+
144
+ # populate class attributes
145
+ @signer_nonce = nonce.to_i
146
+ @gas_price = gas_price.to_i
147
+ @gas_limit = gas_limit.to_i
148
+ @destination = to.to_s
149
+ @amount = value.to_i
150
+ @payload = data
151
+ @chain_id = chain_id
152
+
153
+ # allows us to force-setting a signature if the transaction is signed already
154
+ _set_signature(v, r, s)
155
+
156
+ unless chain_id.nil?
157
+
158
+ # recover sender address
159
+ public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v}", chain_id)
160
+ address = Util.public_key_to_address(public_key).to_s
161
+ @sender = Tx.sanitize_address address
162
+ else
163
+
164
+ # keep the 'from' field blank
165
+ @sender = Tx.sanitize_address nil
166
+ end
167
+
168
+ # last but not least, set the type.
169
+ @type = TYPE_LEGACY
170
+ end
171
+
172
+ # Creates an unsigned copy of a transaction.
173
+ #
174
+ # @param tx [Eth::Tx::Legacy] an legacy transaction object.
175
+ # @return [Eth::Tx::Legacy] an unsigned transaction object.
176
+ # @raise [TransactionTypeError] if transaction type does not match.
177
+ def unsigned_copy(tx)
178
+
179
+ # not checking transaction validity unless it's of a different class
180
+ raise TransactionTypeError, "Cannot copy transaction of different type!" unless tx.instance_of? Tx::Legacy
181
+
182
+ # populate class attributes
183
+ @signer_nonce = tx.signer_nonce
184
+ @gas_price = tx.gas_price
185
+ @gas_limit = tx.gas_limit
186
+ @destination = tx.destination
187
+ @amount = tx.amount
188
+ @payload = tx.payload
189
+ @chain_id = tx.chain_id
190
+
191
+ # force-set signature to unsigned
192
+ _set_signature(tx.chain_id, 0, 0)
193
+
194
+ # keep the 'from' field blank
195
+ @sender = Tx.sanitize_address nil
196
+
197
+ # last but not least, set the type.
198
+ @type = TYPE_LEGACY
199
+ end
200
+
201
+ # Sign the transaction with a given key.
202
+ #
203
+ # @param key [Eth::Key] the key-pair to use for signing.
204
+ # @return [String] a transaction hash.
205
+ # @raise [Signature::SignatureError] if transaction is already signed.
206
+ # @raise [Signature::SignatureError] if sender address does not match signing key.
207
+ def sign(key)
208
+ if Tx.is_signed? self
209
+ raise Signature::SignatureError, "Transaction is already signed!"
210
+ end
211
+
212
+ # ensure the sender address matches the given key
213
+ unless @sender.nil? or sender.empty?
214
+ signer_address = Tx.sanitize_address key.address.to_s
215
+ from_address = Tx.sanitize_address @sender
216
+ raise Signature::SignatureError, "Signer does not match sender" unless signer_address == from_address
217
+ end
218
+
219
+ # sign a keccak hash of the unsigned, encoded transaction
220
+ signature = key.sign(unsigned_hash, @chain_id)
221
+ r, s, v = Signature.dissect signature
222
+ @signature_v = v
223
+ @signature_r = r
224
+ @signature_s = s
225
+ return hash
226
+ end
227
+
228
+ # Encodes a raw transaction object.
229
+ #
230
+ # @return [String] a raw, RLP-encoded legacy transaction.
231
+ # @raise [Signature::SignatureError] if the transaction is not yet signed.
232
+ def encoded
233
+ unless Tx.is_signed? self
234
+ raise Signature::SignatureError, "Transaction is not signed!"
235
+ end
236
+ tx_data = []
237
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
238
+ tx_data.push Util.serialize_int_to_big_endian @gas_price
239
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
240
+ tx_data.push Util.hex_to_bin @destination
241
+ tx_data.push Util.serialize_int_to_big_endian @amount
242
+ tx_data.push Rlp::Sedes.binary.serialize @payload
243
+ tx_data.push Util.serialize_int_to_big_endian @signature_v
244
+ tx_data.push Util.serialize_int_to_big_endian @signature_r
245
+ tx_data.push Util.serialize_int_to_big_endian @signature_s
246
+ Rlp.encode tx_data
247
+ end
248
+
249
+ # Gets the encoded, raw transaction hex.
250
+ #
251
+ # @return [String] the raw transaction hex.
252
+ def hex
253
+ Util.bin_to_hex encoded
254
+ end
255
+
256
+ # Gets the transaction hash.
257
+ #
258
+ # @return [String] the transaction hash.
259
+ def hash
260
+ Util.bin_to_hex Util.keccak256 encoded
261
+ end
262
+
263
+ # Encodes the unsigned transaction object, required for signing.
264
+ #
265
+ # @return [String] an RLP-encoded, unsigned transaction.
266
+ def unsigned_encoded
267
+ tx_data = []
268
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
269
+ tx_data.push Util.serialize_int_to_big_endian @gas_price
270
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
271
+ tx_data.push Util.hex_to_bin @destination
272
+ tx_data.push Util.serialize_int_to_big_endian @amount
273
+ tx_data.push Rlp::Sedes.binary.serialize @payload
274
+ tx_data.push Util.serialize_int_to_big_endian @chain_id
275
+ tx_data.push Util.serialize_int_to_big_endian 0
276
+ tx_data.push Util.serialize_int_to_big_endian 0
277
+ Rlp.encode tx_data
278
+ end
279
+
280
+ # Gets the sign-hash required to sign a raw transaction.
281
+ #
282
+ # @return [String] a Keccak-256 hash of an unsigned transaction.
283
+ def unsigned_hash
284
+ Util.keccak256 unsigned_encoded
285
+ end
286
+
287
+ private
288
+
289
+ # Force-sets an existing signature of a decoded transaction.
290
+ def _set_signature(v, r, s)
291
+ @signature_v = v
292
+ @signature_r = r
293
+ @signature_s = s
294
+ end
295
+ end
296
+ end
297
+ end