Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion action/actctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type AbstractAction struct {
gasFeeCap *big.Int
accessList types.AccessList
blobData *BlobTxData
authList []types.SetCodeAuthorization
}

// ChainID returns the chainID
Expand Down Expand Up @@ -207,14 +208,26 @@ func (act *AbstractAction) convertToTx() TxCommonInternal {
accessList: act.accessList,
blob: act.blobData,
}
case SetCodeTxType:
return &SetCodeTx{
DynamicFeeTx: DynamicFeeTx{
chainID: act.chainID,
nonce: act.nonce,
gasLimit: act.gasLimit,
gasTipCap: act.gasTipCap,
gasFeeCap: act.gasFeeCap,
accessList: act.accessList,
},
authList: act.authList,
}
default:
panic(fmt.Sprintf("unsupported action type = %d", act.txType))
}
}

func (act *AbstractAction) validateTx() error {
switch act.txType {
case LegacyTxType, AccessListTxType, DynamicFeeTxType, BlobTxType:
case LegacyTxType, AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType:
// these are allowed tx types
default:
return errors.Wrapf(ErrInvalidAct, "unsupported tx type = %d", act.txType)
Expand Down
1 change: 1 addition & 0 deletions action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
AccessListTxType = 1
DynamicFeeTxType = 2
BlobTxType = 3
SetCodeTxType = 4
)

type (
Expand Down
1 change: 1 addition & 0 deletions action/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,5 @@ func TestNewActionType(t *testing.T) {
r.Equal(types.AccessListTxType, AccessListTxType)
r.Equal(types.DynamicFeeTxType, DynamicFeeTxType)
r.Equal(types.BlobTxType, BlobTxType)
r.Equal(types.SetCodeTxType, SetCodeTxType)
}
9 changes: 9 additions & 0 deletions action/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ func (b *EnvelopeBuilder) SetBlobTxData(
return b
}

// SetAuthList sets the set code authorization list.
func (b *EnvelopeBuilder) SetAuthList(auths []types.SetCodeAuthorization) *EnvelopeBuilder {
b.ab.authList = auths
return b
}

// Build builds a new action.
func (b *EnvelopeBuilder) Build() Envelope {
return b.build()
Expand Down Expand Up @@ -216,6 +222,7 @@ func (b *EnvelopeBuilder) setEnvelopeCommonFields(tx *types.Transaction) error {
sidecar: tx.BlobTxSidecar(),
}
}
b.ab.authList = tx.SetCodeAuthorizations()
return nil
}

Expand All @@ -229,6 +236,8 @@ func convertEthTxType(typ uint8) (int, error) {
return DynamicFeeTxType, nil
case types.BlobTxType:
return BlobTxType, nil
case types.SetCodeTxType:
return SetCodeTxType, nil
default:
return 0, errors.Wrapf(ErrInvalidAct, "unsupported eth tx type %d", typ)
}
Expand Down
13 changes: 13 additions & 0 deletions action/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type (
TxDynamicGas
AccessList() types.AccessList
TxBlob
SetCodeAuthorizations() []types.SetCodeAuthorization
}

TxCommonInternal interface {
Expand Down Expand Up @@ -146,6 +147,10 @@ func (elp *envelope) BlobTxSidecar() *types.BlobTxSidecar {
return elp.common.BlobTxSidecar()
}

func (elp *envelope) SetCodeAuthorizations() []types.SetCodeAuthorization {
return elp.common.SetCodeAuthorizations()
}

func (elp *envelope) Value() *big.Int {
if exec, ok := elp.Action().(*Execution); ok {
return exec.Value()
Expand Down Expand Up @@ -214,6 +219,9 @@ func (elp *envelope) IntrinsicGas() (uint64, error) {
gas += uint64(len(acl)) * TxAccessListAddressGas
gas += uint64(acl.StorageKeys()) * TxAccessListStorageKeyGas
}
if auths := elp.SetCodeAuthorizations(); len(auths) > 0 {
gas += uint64(len(auths)) * CallNewAccountGas
}
return gas, nil
}

Expand Down Expand Up @@ -306,6 +314,11 @@ func (elp *envelope) loadProtoTxCommon(pbAct *iotextypes.ActionCore) error {
if err = tx.fromProto(pbAct); err == nil {
elp.common = &tx
}
case SetCodeTxType:
tx := SetCodeTx{}
if err = tx.fromProto(pbAct); err == nil {
elp.common = &tx
}
default:
panic(fmt.Sprintf("unsupported action type = %d", pbAct.TxType))
}
Expand Down
2 changes: 2 additions & 0 deletions action/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const (
ExecutionBaseIntrinsicGas uint64 = 10000 // base intrinsic gas for execution
TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
)

var (
Expand Down
94 changes: 91 additions & 3 deletions action/protocol/execution/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type (
gas uint64
data []byte
accessList types.AccessList
authList []types.SetCodeAuthorization
evmConfig vm.Config
chainConfig *params.ChainConfig
genesis genesis.Blockchain
Expand Down Expand Up @@ -214,6 +215,7 @@ func newParams(
gasLimit,
execution.Data(),
execution.AccessList(),
execution.SetCodeAuthorizations(),
vmConfig,
chainConfig,
g.Blockchain,
Expand Down Expand Up @@ -496,6 +498,14 @@ func getChainConfig(g genesis.Blockchain, height uint64, id uint32, getBlockTime
cancunTimestamp := (uint64)(cancunTime.Unix())
chainConfig.CancunTime = &cancunTimestamp
}
// enable Prague
pragueTime, err := getBlockTime(g.ToBeEnabledBlockHeight)
if err != nil {
return nil, err
} else if pragueTime != nil {
pragueTimestamp := (uint64)(pragueTime.Unix())
chainConfig.PragueTime = &pragueTimestamp
}
return &chainConfig, nil
}

Expand Down Expand Up @@ -536,7 +546,7 @@ func executeInEVM(ctx context.Context, evmParams *Params, stateDB stateDB) ([]by
if g.IsOkhotsk(blockHeight) {
accessList = evmParams.accessList
}
intriGas, err := intrinsicGas(uint64(len(evmParams.data)), accessList)
intriGas, err := intrinsicGas(uint64(len(evmParams.data)), accessList, evmParams.authList)
if err != nil {
return nil, evmParams.gas, remainingGas, action.EmptyAddress, iotextypes.ReceiptStatus_Failure, err
}
Expand Down Expand Up @@ -576,6 +586,24 @@ func executeInEVM(ctx context.Context, evmParams *Params, stateDB stateDB) ([]by
}
} else {
stateDB.SetNonce(evmParams.txCtx.Origin, stateDB.GetNonce(evmParams.txCtx.Origin)+1, tracing.NonceChangeUnspecified)

// Apply EIP-7702 authorizations.
for _, auth := range evmParams.authList {
// Note errors are ignored, we simply skip invalid authorizations here.
if err := applyAuthorization(evm, stateDB, &auth); err != nil {
log.T(ctx).Debug("failed to apply authorization", zap.Error(err), zap.String("auth", auth.Address.String()))
Comment on lines +593 to +594
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we know which authorization succeed?

}
}

// Perform convenience warming of sender's delegation target. Although the
// sender is already warmed in Prepare(..), it's possible a delegation to
// the account was deployed during this transaction. To handle correctly,
// simply wait until the final state of delegations is determined before
// performing the resolution and warming.
if addr, ok := types.ParseDelegation(stateDB.GetCode(*evmParams.contract)); ok {
stateDB.AddAddressToAccessList(addr)
}

// process contract
ret, remainingGas, evmErr = evm.Call(executor, *evmParams.contract, evmParams.data, remainingGas, amount)
}
Expand Down Expand Up @@ -692,7 +720,7 @@ func evmErrToErrStatusCode(evmErr error, g genesis.Blockchain, height uint64) io
}

// intrinsicGas returns the intrinsic gas of an execution
func intrinsicGas(size uint64, list types.AccessList) (uint64, error) {
func intrinsicGas(size uint64, list types.AccessList, authList []types.SetCodeAuthorization) (uint64, error) {
if action.ExecutionDataGas == 0 {
panic("payload gas price cannot be zero")
}
Expand All @@ -705,7 +733,11 @@ func intrinsicGas(size uint64, list types.AccessList) (uint64, error) {
if (math.MaxInt64-action.ExecutionBaseIntrinsicGas-accessListGas)/action.ExecutionDataGas < size {
return 0, action.ErrInsufficientFunds
}
return size*action.ExecutionDataGas + action.ExecutionBaseIntrinsicGas + accessListGas, nil
var authListGas uint64
if len(authList) > 0 {
authListGas = uint64(len(authList)) * action.CallNewAccountGas
}
return size*action.ExecutionDataGas + action.ExecutionBaseIntrinsicGas + accessListGas + authListGas, nil
}

// SimulateExecution simulates the execution in evm
Expand Down Expand Up @@ -788,3 +820,59 @@ func ExtractRevertMessage(ret []byte) string {
revertMsg := string(data[64 : 64+msgLength])
return revertMsg
}

func validateAuthorization(evm *vm.EVM, sdb stateDB, auth *types.SetCodeAuthorization) (authority common.Address, err error) {
chainID := evm.ChainConfig().ChainID.Uint64()
// Verify chain ID is 0 or equal to current chain ID.
if !auth.ChainID.IsZero() && chainID != (auth.ChainID.Uint64()) {
return authority, errors.Errorf("authorization chain ID %v does not match current chain ID %v", auth.ChainID, chainID)
}
// Limit nonce to 2^64-1 per EIP-2681.
if auth.Nonce+1 < auth.Nonce {
return authority, errors.Errorf("authorization nonce %d exceeds maximum value", auth.Nonce)
}
// Validate signature values and recover authority.
authority, err = auth.Authority()
if err != nil {
return authority, errors.Wrap(err, "failed to recover authority from authorization")
}
// Check the authority account
// 1) doesn't have code or has exisiting delegation
// 2) matches the auth's nonce
//
// Note it is added to the access list even if the authorization is invalid.
sdb.AddAddressToAccessList(authority)
code := sdb.GetCode(authority)
if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok {
return authority, errors.Errorf("authorization destination %s has code", authority.String())
}
if have := sdb.GetNonce(authority); have != auth.Nonce {
return authority, errors.Errorf("authorization nonce %d does not match account %s nonce %d", auth.Nonce, authority.String(), have)
}
return authority, nil
}

func applyAuthorization(evm *vm.EVM, sdb stateDB, auth *types.SetCodeAuthorization) error {
authority, err := validateAuthorization(evm, sdb, auth)
if err != nil {
return err
}
// If the account already exists in state, refund the new account cost
// charged in the intrinsic calculation.
if sdb.Exist(authority) {
sdb.AddRefund(action.CallNewAccountGas - action.TxAuthTupleGas)
}

// Update nonce and account code.
sdb.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
if auth.Address == (common.Address{}) {
// Delegation to zero address means clear.
sdb.SetCode(authority, nil)
return nil
}

// Otherwise install delegation to auth.Address.
sdb.SetCode(authority, types.AddressToDelegation(auth.Address))

return nil
}
2 changes: 1 addition & 1 deletion action/protocol/execution/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ func TestGasEstimate(t *testing.T) {
func gasExecuteInEVM(gas, consume, refund, size uint64) (uint64, uint64, error) {
remainingGas := gas

intriGas, err := intrinsicGas(size, nil)
intriGas, err := intrinsicGas(size, nil, nil)
if err != nil {
return 0, 0, err
}
Expand Down
4 changes: 4 additions & 0 deletions action/protocol/execution/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ func (p *Protocol) Validate(ctx context.Context, elp action.Envelope, _ protocol
if len(elp.BlobHashes()) > 0 && elp.To() == nil {
return errors.New("cannot create contract in blob tx")
}
// Reject SetCodeTxType before Pectra EVM upgrade
if fCtx.PrePectraEVM && elp.TxType() == action.SetCodeTxType {
return errors.Wrapf(action.ErrInvalidAct, "SetCodeTxType is not allowed before Pectra EVM upgrade")
}
return nil
}

Expand Down
11 changes: 11 additions & 0 deletions action/protocol/generic_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ func (v *GenericValidator) Validate(ctx context.Context, selp *action.SealedEnve
return errors.Wrapf(action.ErrUnderpriced, "tip cap is too low: %s, min tip cap: %s", selp.GasTipCap().String(), MinTipCap.String())
}
}
if selp.TxType() == action.SetCodeTxType {
if featureCtx.PrePectraEVM {
return errors.Wrapf(action.ErrInvalidAct, "SetCodeTxType is not allowed before Pectra EVM upgrade")
}
if selp.To() == nil {
return errors.Wrapf(action.ErrSetCodeTxCreate, "sender %v", caller.String())
}
if len(selp.SetCodeAuthorizations()) == 0 {
return errors.Wrapf(action.ErrEmptyAuthList, "sender %v", caller.String())
}
}
if featureCtx.EnableBlobTransaction && len(selp.BlobHashes()) > 0 {
// validate sidecar
if !MustGetBlockCtx(ctx).SkipSidecarValidation || selp.BlobTxSidecar() != nil {
Expand Down
2 changes: 1 addition & 1 deletion action/rlp_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func ExtractTypeSigPubkey(tx *types.Transaction) (iotextypes.Encoding, []byte, c
encoding = iotextypes.Encoding_ETHEREUM_UNPROTECTED
signer = types.HomesteadSigner{}
}
case types.AccessListTxType, types.DynamicFeeTxType, types.BlobTxType:
case types.AccessListTxType, types.DynamicFeeTxType, types.BlobTxType, types.SetCodeTxType:
// AL txs are defined to use 0 and 1 as their recovery id,
// DynamicFee txs are defined to use 0 and 1 as their recovery id,
// Blob txs are defined to use 0 and 1 as their recovery id,
Expand Down
Loading