Skip to content

Commit

Permalink
[evm] implement EIP-6780 SELFDESTRUCT only in same transaction (iotex…
Browse files Browse the repository at this point in the history
  • Loading branch information
dustinxie committed Apr 2, 2024
1 parent 582ee3b commit 24e6bd2
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 29 deletions.
2 changes: 2 additions & 0 deletions action/protocol/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ type (
DisableDelegateEndorsement bool
RefactorFreshAccountConversion bool
SuicideTxLogMismatchPanic bool
EnableCancunEVM bool
}

// FeatureWithHeightCtx provides feature check functions.
Expand Down Expand Up @@ -265,6 +266,7 @@ func WithFeatureCtx(ctx context.Context) context.Context {
DisableDelegateEndorsement: !g.IsTsunami(height),
RefactorFreshAccountConversion: g.IsTsunami(height),
SuicideTxLogMismatchPanic: g.IsToBeEnabled(height),
EnableCancunEVM: g.IsToBeEnabled(height),
},
)
}
Expand Down
3 changes: 3 additions & 0 deletions action/protocol/execution/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,9 @@ func prepareStateDB(ctx context.Context, sm protocol.StateManager) (*StateDBAdap
if featureCtx.SuicideTxLogMismatchPanic {
opts = append(opts, SuicideTxLogMismatchPanicOption())
}
if featureCtx.EnableCancunEVM {
opts = append(opts, EnableCancunEVMOption())
}

return NewStateDBAdapter(
sm,
Expand Down
118 changes: 89 additions & 29 deletions action/protocol/execution/evm/evmstatedbadapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type (
// deleteAccount records the account/contract to be deleted
deleteAccount map[hash.Hash160]struct{}

// createdAccount contains new accounts created in this tx
createdAccount map[common.Address]bool

// contractMap records the contracts being changed
contractMap map[hash.Hash160]Contract

Expand All @@ -61,6 +64,8 @@ type (
preimageSnapshot map[int]preimageMap
accessList *accessList // per-transaction access list
accessListSnapshot map[int]*accessList
createdAccount createdAccount
createdAccountSnapshot map[int]createdAccount
logsSnapshot map[int]int // logs is an array, save len(logs) at time of snapshot suffices
txLogsSnapshot map[int]int
notFixTopicCopyBug bool
Expand All @@ -73,6 +78,7 @@ type (
manualCorrectGasRefund bool
suicideTxLogMismatchPanic bool
zeroNonceForFreshAccount bool
enableCancun bool
}
)

Expand Down Expand Up @@ -162,6 +168,14 @@ func ZeroNonceForFreshAccountOption() StateDBAdapterOption {
}
}

// EnableCancunEVMOption indicates that Cancun EVM is activated
func EnableCancunEVMOption() StateDBAdapterOption {
return func(adapter *StateDBAdapter) error {
adapter.enableCancun = true
return nil
}
}

// NewStateDBAdapter creates a new state db with iotex blockchain
func NewStateDBAdapter(
sm protocol.StateManager,
Expand Down Expand Up @@ -197,6 +211,10 @@ func NewStateDBAdapter(
if !s.legacyNonceAccount && s.useConfirmedNonce {
return nil, errors.New("invalid parameter combination")
}
if s.enableCancun {
s.createdAccount = make(createdAccount)
s.createdAccountSnapshot = make(map[int]createdAccount)
}
return s, nil
}

Expand Down Expand Up @@ -225,12 +243,15 @@ func (stateDB *StateDBAdapter) CreateAccount(evmAddr common.Address) {
log.L().Error("Failed to convert evm address.", zap.Error(err))
return
}
_, _, err = accountutil.LoadOrCreateAccount(stateDB.sm, addr, stateDB.accountCreationOpts()...)
_, created, err := accountutil.LoadOrCreateAccount(stateDB.sm, addr, stateDB.accountCreationOpts()...)
if err != nil {
log.L().Error("Failed to create account.", zap.Error(err))
stateDB.logError(err)
return
}
if stateDB.enableCancun && created {
stateDB.createdAccount[evmAddr] = true
}
log.L().Debug("Called CreateAccount.", log.Hex("addrHash", evmAddr[:]))
}

Expand Down Expand Up @@ -433,6 +454,7 @@ func (stateDB *StateDBAdapter) SelfDestruct(evmAddr common.Address) {
}
// clears the account balance
actBalance := new(big.Int).Set(s.Balance)
log.L().Info("SelfDestruct contract", zap.String("Balance", actBalance.String()))
if err := s.SubBalance(s.Balance); err != nil {
log.L().Debug("failed to clear balance", zap.Error(err), zap.String("address", evmAddr.Hex()))
return
Expand All @@ -443,28 +465,10 @@ func (stateDB *StateDBAdapter) SelfDestruct(evmAddr common.Address) {
stateDB.logError(err)
return
}
// To ensure data consistency, generate this log after the hard-fork
// a separate patch file will be created later to provide missing logs before the hard-fork
// TODO: remove this gating once the hard-fork has passed
if stateDB.suicideTxLogMismatchPanic {
// before calling SelfDestruct, EVM will transfer the contract's balance to beneficiary
// need to create a transaction log on successful SelfDestruct
if stateDB.lastAddBalanceAmount.Cmp(actBalance) == 0 {
if stateDB.lastAddBalanceAmount.Cmp(big.NewInt(0)) > 0 {
from, _ := address.FromBytes(evmAddr[:])
stateDB.addTransactionLogs(&action.TransactionLog{
Type: iotextypes.TransactionLogType_IN_CONTRACT_TRANSFER,
Sender: from.String(),
Recipient: stateDB.lastAddBalanceAddr,
Amount: stateDB.lastAddBalanceAmount,
})
}
} else {
log.L().Panic("SelfDestruct contract's balance does not match",
zap.String("SelfDestruct", actBalance.String()),
zap.String("beneficiary", stateDB.lastAddBalanceAmount.String()))
}
}
// before calling SelfDestruct, EVM will transfer the contract's balance to beneficiary
// need to create a transaction log on successful SelfDestruct
from, _ := address.FromBytes(evmAddr[:])
stateDB.generateSelfDestructTransferLog(from.String(), stateDB.lastAddBalanceAmount.Cmp(actBalance) == 0)
// mark it as deleted
stateDB.selfDestructed[addrHash] = struct{}{}
}
Expand All @@ -476,6 +480,22 @@ func (stateDB *StateDBAdapter) HasSelfDestructed(evmAddr common.Address) bool {
return ok
}

// Selfdestruct6780 implements EIP-6780
func (stateDB *StateDBAdapter) Selfdestruct6780(evmAddr common.Address) {
if !stateDB.Exist(evmAddr) {
log.L().Debug("Account does not exist.", zap.String("address", evmAddr.Hex()))
return
}
// opSelfdestruct6780 has already subtracted the contract's balance
// so create a transaction log
from, _ := address.FromBytes(evmAddr[:])
stateDB.generateSelfDestructTransferLog(from.String(), true)
// per EIP-6780, delete the account only if it is created in the same transaction
if stateDB.createdAccount[evmAddr] {
stateDB.selfDestructed[hash.BytesToHash160(evmAddr.Bytes())] = struct{}{}
}
}

// SetTransientState sets transient storage for a given account
func (stateDB *StateDBAdapter) SetTransientState(addr common.Address, key, value common.Hash) {
log.S().Panic("SetTransientState not implemented")
Expand All @@ -487,12 +507,6 @@ func (stateDB *StateDBAdapter) GetTransientState(addr common.Address, key common
return common.Hash{}
}

// Selfdestruct6780 implements EIP-6780
func (stateDB *StateDBAdapter) Selfdestruct6780(evmAddr common.Address) {
//Todo: implement EIP-6780
log.S().Panic("Selfdestruct6780 not implemented")
}

// Exist checks the existence of an address
func (stateDB *StateDBAdapter) Exist(evmAddr common.Address) bool {
addr, err := address.FromBytes(evmAddr.Bytes())
Expand Down Expand Up @@ -623,6 +637,19 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) {
}
}
}
// restore created accounts
if stateDB.enableCancun {
stateDB.createdAccount = stateDB.createdAccountSnapshot[snapshot]
{
for i := snapshot; ; i++ {
if _, ok := stateDB.createdAccountSnapshot[i]; ok {
delete(stateDB.createdAccountSnapshot, i)
} else {
break
}
}
}
}
// restore logs and txLogs
if stateDB.revertLog {
stateDB.logs = stateDB.logs[:stateDB.logsSnapshot[snapshot]]
Expand Down Expand Up @@ -750,6 +777,14 @@ func (stateDB *StateDBAdapter) Snapshot() int {
stateDB.preimageSnapshot[sn] = p
// save a copy of access list
stateDB.accessListSnapshot[sn] = stateDB.accessList.Copy()
if stateDB.enableCancun {
// save a copy of created account map
ca := make(createdAccount)
for k, v := range stateDB.createdAccount {
ca[k] = v
}
stateDB.createdAccountSnapshot[sn] = ca
}
return sn
}

Expand Down Expand Up @@ -854,6 +889,27 @@ func (stateDB *StateDBAdapter) accountState(evmAddr common.Address) (*state.Acco
return accountutil.LoadAccountByHash160(stateDB.sm, addrHash, stateDB.accountCreationOpts()...)
}

func (stateDB *StateDBAdapter) generateSelfDestructTransferLog(sender string, amountMatch bool) {
// To ensure data consistency, generate this log after the hard-fork
// a separate patch file will be created later to provide missing logs before the hard-fork
// TODO: remove this gating once the hard-fork has passed
if stateDB.suicideTxLogMismatchPanic {
if amountMatch {
if stateDB.lastAddBalanceAmount.Cmp(big.NewInt(0)) > 0 {
stateDB.addTransactionLogs(&action.TransactionLog{
Type: iotextypes.TransactionLogType_IN_CONTRACT_TRANSFER,
Sender: sender,
Recipient: stateDB.lastAddBalanceAddr,
Amount: stateDB.lastAddBalanceAmount,
})
}
} else {
log.L().Panic("SelfDestruct contract's balance does not match",
zap.String("beneficiary", stateDB.lastAddBalanceAmount.String()))
}
}
}

func (stateDB *StateDBAdapter) addTransactionLogs(tlog *action.TransactionLog) {
stateDB.transactionLogs = append(stateDB.transactionLogs, tlog)
}
Expand Down Expand Up @@ -1093,4 +1149,8 @@ func (stateDB *StateDBAdapter) clear() {
stateDB.txLogsSnapshot = make(map[int]int)
stateDB.logs = []*action.Log{}
stateDB.transactionLogs = []*action.TransactionLog{}
if stateDB.enableCancun {
stateDB.createdAccount = make(createdAccount)
stateDB.createdAccountSnapshot = make(map[int]createdAccount)
}
}

0 comments on commit 24e6bd2

Please sign in to comment.