ethereum.forks.berlin.transactions

Transactions are atomic units of work created externally to Ethereum and submitted to be executed. If Ethereum is viewed as a state machine, transactions are the events that move between states.

LegacyTransaction

Atomic operation performed on the block chain. This represents the original transaction format used before EIP-2930.

27
@slotted_freezable
28
@dataclass
class LegacyTransaction:

nonce

A scalar value equal to the number of transactions sent by the sender.

37
    nonce: U256

gas_price

The price of gas for this transaction, in wei.

42
    gas_price: Uint

gas

The maximum amount of gas that can be used by this transaction.

47
    gas: Uint

to

The address of the recipient. If empty, the transaction is a contract creation.

52
    to: Bytes0 | Address

value

The amount of ether (in wei) to send with this transaction.

58
    value: U256

data

The data payload of the transaction, which can be used to call functions on contracts or to create new contracts.

63
    data: Bytes

v

The recovery id of the signature.

69
    v: U256

r

The first part of the signature.

74
    r: U256

s

The second part of the signature.

79
    s: U256

Access

A mapping from account address to storage slots that are pre-warmed as part of a transaction.

85
@slotted_freezable
86
@dataclass
class Access:

account

The address of the account that is accessed.

93
    account: Address

slots

A tuple of storage slots that are accessed in the account.

98
    slots: Tuple[Bytes32, ...]

AccessListTransaction

The transaction type added in EIP-2930 to support access lists.

This transaction type extends the legacy transaction with an access list and chain ID. The access list specifies which addresses and storage slots the transaction will access.

104
@slotted_freezable
105
@dataclass
class AccessListTransaction:

chain_id

The ID of the chain on which this transaction is executed.

117
    chain_id: U64

nonce

A scalar value equal to the number of transactions sent by the sender.

122
    nonce: U256

gas_price

The price of gas for this transaction.

127
    gas_price: Uint

gas

The maximum amount of gas that can be used by this transaction.

132
    gas: Uint

to

The address of the recipient. If empty, the transaction is a contract creation.

137
    to: Bytes0 | Address

value

The amount of ether (in wei) to send with this transaction.

143
    value: U256

data

The data payload of the transaction, which can be used to call functions on contracts or to create new contracts.

148
    data: Bytes

access_list

A tuple of Access objects that specify which addresses and storage slots are accessed in the transaction.

154
    access_list: Tuple[Access, ...]

y_parity

The recovery id of the signature.

160
    y_parity: U256

r

The first part of the signature.

165
    r: U256

s

The second part of the signature.

170
    s: U256

Transaction

Union type representing any valid transaction type.

176
Transaction = LegacyTransaction | AccessListTransaction

encode_transaction

Encode a transaction into its RLP or typed transaction format. Needed because non-legacy transactions aren't RLP.

Legacy transactions are returned as-is, while other transaction types are prefixed with their type identifier and RLP encoded.

def encode_transaction(tx: Transaction) -> LegacyTransaction | Bytes:
183
    """
184
    Encode a transaction into its RLP or typed transaction format.
185
    Needed because non-legacy transactions aren't RLP.
186
187
    Legacy transactions are returned as-is, while other transaction types
188
    are prefixed with their type identifier and RLP encoded.
189
    """
190
    if isinstance(tx, LegacyTransaction):
191
        return tx
192
    elif isinstance(tx, AccessListTransaction):
193
        return b"\x01" + rlp.encode(tx)
194
    else:
195
        raise Exception(f"Unable to encode transaction of type {type(tx)}")

decode_transaction

Decode a transaction from its RLP or typed transaction format. Needed because non-legacy transactions aren't RLP.

Legacy transactions are returned as-is, while other transaction types are decoded based on their type identifier prefix.

def decode_transaction(tx: LegacyTransaction | Bytes) -> Transaction:
199
    """
200
    Decode a transaction from its RLP or typed transaction format.
201
    Needed because non-legacy transactions aren't RLP.
202
203
    Legacy transactions are returned as-is, while other transaction types
204
    are decoded based on their type identifier prefix.
205
    """
206
    if isinstance(tx, Bytes):
207
        if tx[0] != 1:
208
            raise TransactionTypeError(tx[0])
209
        return rlp.decode_to(AccessListTransaction, tx[1:])
210
    else:
211
        return tx

validate_transaction

Verifies a transaction.

The gas in a transaction gets used to pay for the intrinsic cost of operations, therefore if there is insufficient gas then it would not be possible to execute a transaction and it will be declared invalid.

Additionally, the nonce of a transaction must not equal or exceed the limit defined in EIP-2681. In practice, defining the limit as 2**64-1 has no impact because sending 2**64-1 transactions is improbable. It's not strictly impossible though, 2**64-1 transactions is the entire capacity of the Ethereum blockchain at 2022 gas limits for a little over 22 years.

This function takes a transaction as a parameter and returns the intrinsic gas cost of the transaction after validation. It throws an InsufficientTransactionGasError exception if the transaction does not provide enough gas to cover the intrinsic cost, and a NonceOverflowError exception if the nonce is greater than 2**64 - 2.

def validate_transaction(tx: Transaction) -> Uint:
215
    """
216
    Verifies a transaction.
217
218
    The gas in a transaction gets used to pay for the intrinsic cost of
219
    operations, therefore if there is insufficient gas then it would not
220
    be possible to execute a transaction and it will be declared invalid.
221
222
    Additionally, the nonce of a transaction must not equal or exceed the
223
    limit defined in [EIP-2681].
224
    In practice, defining the limit as ``2**64-1`` has no impact because
225
    sending ``2**64-1`` transactions is improbable. It's not strictly
226
    impossible though, ``2**64-1`` transactions is the entire capacity of the
227
    Ethereum blockchain at 2022 gas limits for a little over 22 years.
228
229
    This function takes a transaction as a parameter and returns the intrinsic
230
    gas cost of the transaction after validation. It throws an
231
    `InsufficientTransactionGasError` exception if the transaction does not
232
    provide enough gas to cover the intrinsic cost, and a `NonceOverflowError`
233
    exception if the nonce is greater than `2**64 - 2`.
234
235
    [EIP-2681]: https://eips.ethereum.org/EIPS/eip-2681
236
    """
237
    intrinsic_gas = calculate_intrinsic_cost(tx)
238
    if intrinsic_gas > tx.gas:
239
        raise InsufficientTransactionGasError("Insufficient gas")
240
    if U256(tx.nonce) >= U256(U64.MAX_VALUE):
241
        raise NonceOverflowError("Nonce too high")
242
    return intrinsic_gas

calculate_intrinsic_cost

Calculates the gas that is charged before execution is started.

The intrinsic cost of the transaction is charged before execution has begun. Functions/operations in the EVM cost money to execute so this intrinsic cost is for the operations that need to be paid for as part of the transaction. Data transfer, for example, is part of this intrinsic cost. It costs ether to send data over the wire and that ether is accounted for in the intrinsic cost calculated in this function. This intrinsic cost must be calculated and paid for before execution in order for all operations to be implemented.

The intrinsic cost includes:

  1. Base cost (TX_BASE)

  2. Cost for data (zero and non-zero bytes)

  3. Cost for contract creation (if applicable)

  4. Cost for access list entries (if applicable)

This function takes a transaction as a parameter and returns the intrinsic gas cost of the transaction.

def calculate_intrinsic_cost(tx: Transaction) -> Uint:
246
    """
247
    Calculates the gas that is charged before execution is started.
248
249
    The intrinsic cost of the transaction is charged before execution has
250
    begun. Functions/operations in the EVM cost money to execute so this
251
    intrinsic cost is for the operations that need to be paid for as part of
252
    the transaction. Data transfer, for example, is part of this intrinsic
253
    cost. It costs ether to send data over the wire and that ether is
254
    accounted for in the intrinsic cost calculated in this function. This
255
    intrinsic cost must be calculated and paid for before execution in order
256
    for all operations to be implemented.
257
258
    The intrinsic cost includes:
259
    1. Base cost (`TX_BASE`)
260
    2. Cost for data (zero and non-zero bytes)
261
    3. Cost for contract creation (if applicable)
262
    4. Cost for access list entries (if applicable)
263
264
    This function takes a transaction as a parameter and returns the intrinsic
265
    gas cost of the transaction.
266
    """
267
    from .vm.gas import GasCosts
268
269
    num_zeros = Uint(tx.data.count(0))
270
    num_non_zeros = ulen(tx.data) - num_zeros
271
    data_cost = (
272
        num_zeros * GasCosts.TX_DATA_PER_ZERO
273
        + num_non_zeros * GasCosts.TX_DATA_PER_NON_ZERO
274
    )
275
276
    if tx.to == Bytes0(b""):
277
        create_cost = GasCosts.TX_CREATE
278
    else:
279
        create_cost = Uint(0)
280
281
    access_list_cost = Uint(0)
282
    if isinstance(tx, AccessListTransaction):
283
        for access in tx.access_list:
284
            access_list_cost += GasCosts.TX_ACCESS_LIST_ADDRESS
285
            access_list_cost += (
286
                ulen(access.slots) * GasCosts.TX_ACCESS_LIST_STORAGE_KEY
287
            )
288
289
    return GasCosts.TX_BASE + data_cost + create_cost + access_list_cost

recover_sender

Extracts the sender address from a transaction.

The v, r, and s values are the three parts that make up the signature of a transaction. In order to recover the sender of a transaction the two components needed are the signature (v, r, and s) and the signing hash of the transaction. The sender's public key can be obtained with these two values and therefore the sender address can be retrieved.

This function takes chain_id and a transaction as parameters and returns the address of the sender of the transaction. It raises an InvalidSignatureError if the signature values (r, s, v) are invalid.

def recover_sender(chain_id: U64, ​​tx: Transaction) -> Address:
293
    """
294
    Extracts the sender address from a transaction.
295
296
    The v, r, and s values are the three parts that make up the signature
297
    of a transaction. In order to recover the sender of a transaction the two
298
    components needed are the signature (``v``, ``r``, and ``s``) and the
299
    signing hash of the transaction. The sender's public key can be obtained
300
    with these two values and therefore the sender address can be retrieved.
301
302
    This function takes chain_id and a transaction as parameters and returns
303
    the address of the sender of the transaction. It raises an
304
    `InvalidSignatureError` if the signature values (r, s, v) are invalid.
305
    """
306
    r, s = tx.r, tx.s
307
    if U256(0) >= r or r >= SECP256K1N:
308
        raise InvalidSignatureError("bad r")
309
    if U256(0) >= s or s > SECP256K1N // U256(2):
310
        raise InvalidSignatureError("bad s")
311
312
    if isinstance(tx, LegacyTransaction):
313
        v = tx.v
314
        if v == 27 or v == 28:
315
            public_key = secp256k1_recover(
316
                r, s, v - U256(27), signing_hash_pre155(tx)
317
            )
318
        else:
319
            chain_id_x2 = U256(chain_id) * U256(2)
320
            if v != U256(35) + chain_id_x2 and v != U256(36) + chain_id_x2:
321
                raise InvalidSignatureError("bad v")
322
            public_key = secp256k1_recover(
323
                r,
324
                s,
325
                v - U256(35) - chain_id_x2,
326
                signing_hash_155(tx, chain_id),
327
            )
328
    elif isinstance(tx, AccessListTransaction):
329
        if tx.y_parity not in (U256(0), U256(1)):
330
            raise InvalidSignatureError("bad y_parity")
331
        public_key = secp256k1_recover(
332
            r, s, tx.y_parity, signing_hash_2930(tx)
333
        )
334
335
    return Address(keccak256(public_key)[12:32])

signing_hash_pre155

Compute the hash of a transaction used in a legacy (pre EIP-155) signature.

This function takes a transaction as a parameter and returns the hash of the transaction used in a legacy signature.

def signing_hash_pre155(tx: Transaction) -> Hash32:
339
    """
340
    Compute the hash of a transaction used in a legacy (pre [EIP-155])
341
    signature.
342
343
    This function takes a transaction as a parameter and returns the
344
    hash of the transaction used in a legacy signature.
345
346
    [EIP-155]: https://eips.ethereum.org/EIPS/eip-155
347
    """
348
    return keccak256(
349
        rlp.encode(
350
            (
351
                tx.nonce,
352
                tx.gas_price,
353
                tx.gas,
354
                tx.to,
355
                tx.value,
356
                tx.data,
357
            )
358
        )
359
    )

signing_hash_155

Compute the hash of a transaction used in a EIP-155 signature.

This function takes a transaction and chain ID as parameters and returns the hash of the transaction used in a EIP-155 signature.

def signing_hash_155(tx: Transaction, ​​chain_id: U64) -> Hash32:
363
    """
364
    Compute the hash of a transaction used in a [EIP-155] signature.
365
366
    This function takes a transaction and chain ID as parameters and returns
367
    the hash of the transaction used in a [EIP-155] signature.
368
369
    [EIP-155]: https://eips.ethereum.org/EIPS/eip-155
370
    """
371
    return keccak256(
372
        rlp.encode(
373
            (
374
                tx.nonce,
375
                tx.gas_price,
376
                tx.gas,
377
                tx.to,
378
                tx.value,
379
                tx.data,
380
                chain_id,
381
                Uint(0),
382
                Uint(0),
383
            )
384
        )
385
    )

signing_hash_2930

Compute the hash of a transaction used in a EIP-2930 signature.

This function takes an access list transaction as a parameter and returns the hash of the transaction used in an EIP-2930 signature.

def signing_hash_2930(tx: AccessListTransaction) -> Hash32:
389
    """
390
    Compute the hash of a transaction used in a [EIP-2930] signature.
391
392
    This function takes an access list transaction as a parameter
393
    and returns the hash of the transaction used in an [EIP-2930] signature.
394
395
    [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930
396
    """
397
    return keccak256(
398
        b"\x01"
399
        + rlp.encode(
400
            (
401
                tx.chain_id,
402
                tx.nonce,
403
                tx.gas_price,
404
                tx.gas,
405
                tx.to,
406
                tx.value,
407
                tx.data,
408
                tx.access_list,
409
            )
410
        )
411
    )

get_transaction_hash

Compute the hash of a transaction.

This function takes a transaction as a parameter and returns the keccak256 hash of the transaction. It can handle both legacy transactions and typed transactions (eg. AccessListTransaction).

def get_transaction_hash(tx: Bytes | LegacyTransaction) -> Hash32:
415
    """
416
    Compute the hash of a transaction.
417
418
    This function takes a transaction as a parameter and returns the
419
    keccak256 hash of the transaction. It can handle both legacy transactions
420
    and typed transactions (eg. `AccessListTransaction`).
421
    """
422
    assert isinstance(tx, (LegacyTransaction, Bytes))
423
    if isinstance(tx, LegacyTransaction):
424
        return keccak256(rlp.encode(tx))
425
    else:
426
        return keccak256(tx)