From 0926de326e50a807a424b73122053ab6a58d4696 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 2 Dec 2024 12:58:32 +0100 Subject: [PATCH] core: track validation error for authorizations It's not strictly necessary, but can be nice for debugging. --- core/error.go | 11 ++++ core/state_transition.go | 107 +++++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 44 deletions(-) diff --git a/core/error.go b/core/error.go index 4cccc344cf..885acc8f24 100644 --- a/core/error.go +++ b/core/error.go @@ -103,6 +103,8 @@ var ( // ErrSenderNoEOA is returned if the sender of a transaction is a contract. ErrSenderNoEOA = errors.New("sender not an eoa") + // -- EIP-4844 errors -- + // ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the // blob gas fee of the block. ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee") @@ -113,10 +115,19 @@ var ( // ErrBlobTxCreate is returned if a blob transaction has no explicit to field. ErrBlobTxCreate = errors.New("blob transaction of type create") + // -- EIP-7702 errors -- + // ErrEmptyAuthList is returned if a set code transaction has an empty auth list. ErrEmptyAuthList = errors.New("set code transaction with empty auth list") // ErrAuthSignatureVeryHigh is returned if a set code transaction has a // signature with R or S larger than 2^256-1. ErrAuthSignatureVeryHigh = errors.New("set code transaction has authorization with R or S value greater than 2^256 - 1") + + // EIP-7702 state transition errors: + ErrAuthorizationWrongChainID = errors.New("EIP-7702 authorization chain ID mismatch") + ErrAuthorizationNonceOverflow = errors.New("EIP-7702 authorization nonce > 64 bit") + ErrAuthorizationInvalidSignature = errors.New("EIP-7702 authorization has invalid signature") + ErrAuthorizationDestinationHasCode = errors.New("EIP-7702 authorization destination is a contract") + ErrAuthorizationNonceMismatch = errors.New("EIP-7702 authorization nonce does not match current account nonce") ) diff --git a/core/state_transition.go b/core/state_transition.go index e9231a2c00..676ae22d43 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -466,52 +466,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1) } - // Check authorizations list validity. + // Apply EIP-7702 authorizations. if msg.AuthList != nil { for _, auth := range msg.AuthList { - // Verify chain ID is 0 or equal to current chain ID. - if auth.ChainID != 0 && st.evm.ChainConfig().ChainID.Uint64() != auth.ChainID { - continue - } - // Limit nonce to 2^64-1 per EIP-2681. - if auth.Nonce+1 < auth.Nonce { - continue - } - // Validate signature values and recover authority. - authority, err := auth.Authority() - if err != nil { - continue - } - // Check the authority account 1) doesn't have code or has exisiting - // delegation 2) matches the auth's nonce - st.state.AddAddressToAccessList(authority) - code := st.state.GetCode(authority) - if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok { - continue - } - if have := st.state.GetNonce(authority); have != auth.Nonce { - continue - } - // If the account already exists in state, refund the new account cost - // charged in the intrinsic calculation. - if exists := st.state.Exist(authority); exists { - st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas) - } - st.state.SetNonce(authority, auth.Nonce+1) - delegation := types.AddressToDelegation(auth.Address) - if auth.Address == (common.Address{}) { - // If the delegation is for the zero address, completely clear all - // delegations from the account. - delegation = []byte{} - } - st.state.SetCode(authority, delegation) - - // Usually the transaction destination and delegation target are added to - // the access list in statedb.Prepare(..), however if the delegation is in - // the same transaction we need add here as Prepare already happened. - if *msg.To == authority { - st.state.AddAddressToAccessList(auth.Address) - } + // Note errors are ignored, we simply skip invalid authorizations here. + err := st.applyAuthorization(msg, &auth) + fmt.Println("err:", err) } } @@ -565,6 +525,65 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { }, nil } +// validateAuthorization validates an EIP-7702 authorization against the state. +func (st *stateTransition) validateAuthorization(auth *types.Authorization) (authority common.Address, err error) { + // Verify chain ID is 0 or equal to current chain ID. + if auth.ChainID != 0 && st.evm.ChainConfig().ChainID.Uint64() != auth.ChainID { + return authority, ErrAuthorizationWrongChainID + } + // Limit nonce to 2^64-1 per EIP-2681. + if auth.Nonce+1 < auth.Nonce { + return authority, ErrAuthorizationNonceOverflow + } + // Validate signature values and recover authority. + authority, err = auth.Authority() + if err != nil { + return authority, fmt.Errorf("%w: %v", ErrAuthorizationInvalidSignature, err) + } + // Check the authority account 1) doesn't have code or has exisiting + // delegation 2) matches the auth's nonce + st.state.AddAddressToAccessList(authority) + code := st.state.GetCode(authority) + if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok { + return authority, ErrAuthorizationDestinationHasCode + } + if have := st.state.GetNonce(authority); have != auth.Nonce { + return authority, ErrAuthorizationNonceMismatch + } + return authority, nil +} + +// applyAuthorization applies an EIP-7702 code delegation to the state. +func (st *stateTransition) applyAuthorization(msg *Message, auth *types.Authorization) error { + authority, err := st.validateAuthorization(auth) + if err != nil { + return err + } + + // If the account already exists in state, refund the new account cost + // charged in the intrinsic calculation. + if exists := st.state.Exist(authority); exists { + st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas) + } + st.state.SetNonce(authority, auth.Nonce+1) + delegation := types.AddressToDelegation(auth.Address) + if auth.Address == (common.Address{}) { + // If the delegation is for the zero address, completely clear all + // delegations from the account. + delegation = []byte{} + } + st.state.SetCode(authority, delegation) + + // Usually the transaction destination and delegation target are added to + // the access list in statedb.Prepare(..), however if the delegation is in + // the same transaction we need add here as Prepare already happened. + if *msg.To == authority { + st.state.AddAddressToAccessList(auth.Address) + } + + return nil +} + func (st *stateTransition) refundGas(refundQuotient uint64) uint64 { // Apply refund counter, capped to a refund quotient refund := st.gasUsed() / refundQuotient