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 Sep 30, 2024
1 parent ac1b69f commit 4373a89
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 79 deletions.
2 changes: 2 additions & 0 deletions action/protocol/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ type (
EnableDynamicFeeTx bool
EnableBlobTransaction bool
SufficentBalanceGuarantee bool
EnableCancunEVM bool
}

// FeatureWithHeightCtx provides feature check functions.
Expand Down Expand Up @@ -295,6 +296,7 @@ func WithFeatureCtx(ctx context.Context) context.Context {
EnableDynamicFeeTx: g.IsVanuatu(height),
EnableBlobTransaction: g.IsVanuatu(height),
SufficentBalanceGuarantee: g.IsVanuatu(height),
EnableCancunEVM: g.IsVanuatu(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 @@ -373,6 +373,9 @@ func prepareStateDB(ctx context.Context, sm protocol.StateManager) (*StateDBAdap
if featureCtx.PanicUnrecoverableError {
opts = append(opts, PanicUnrecoverableErrorOption())
}
if featureCtx.EnableCancunEVM {
opts = append(opts, EnableCancunEVMOption())
}

return NewStateDBAdapter(
sm,
Expand Down
217 changes: 138 additions & 79 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[common.Address]struct{}

// createdAccount contains new accounts created in this tx
createdAccount map[common.Address]struct{}

// contractMap records the contracts being changed
contractMap map[common.Address]Contract

Expand All @@ -43,27 +46,28 @@ type (

// StateDBAdapter represents the state db adapter for evm to access iotx blockchain
StateDBAdapter struct {
sm protocol.StateManager
logs []*action.Log
transactionLogs []*action.TransactionLog
err error
blockHeight uint64
executionHash hash.Hash256
lastAddBalanceAddr string
lastAddBalanceAmount *big.Int
refund uint64
refundSnapshot map[int]uint64
cachedContract contractMap
contractSnapshot map[int]contractMap // snapshots of contracts
selfDestructed deleteAccount // account/contract calling SelfDestruct
selfDestructedSnapshot map[int]deleteAccount // snapshots of SelfDestruct accounts
preimages preimageMap
preimageSnapshot map[int]preimageMap
accessList *accessList // per-transaction access list
accessListSnapshot map[int]*accessList
// Transient storage
transientStorage transientStorage
sm protocol.StateManager
logs []*action.Log
transactionLogs []*action.TransactionLog
err error
blockHeight uint64
executionHash hash.Hash256
lastAddBalanceAddr string
lastAddBalanceAmount *big.Int
refund uint64
refundSnapshot map[int]uint64
cachedContract contractMap
contractSnapshot map[int]contractMap // snapshots of contracts
selfDestructed deleteAccount // account/contract calling SelfDestruct
selfDestructedSnapshot map[int]deleteAccount // snapshots of SelfDestruct accounts
preimages preimageMap
preimageSnapshot map[int]preimageMap
accessList *accessList // per-transaction access list
accessListSnapshot map[int]*accessList
transientStorage transientStorage // Transient storage
transientStorageSnapshot map[int]transientStorage
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 @@ -77,6 +81,7 @@ type (
suicideTxLogMismatchPanic bool
zeroNonceForFreshAccount bool
panicUnrecoverableError bool
enableCancun bool
}
)

Expand Down Expand Up @@ -174,6 +179,14 @@ func PanicUnrecoverableErrorOption() 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 All @@ -182,25 +195,23 @@ func NewStateDBAdapter(
opts ...StateDBAdapterOption,
) (*StateDBAdapter, error) {
s := &StateDBAdapter{
sm: sm,
logs: []*action.Log{},
err: nil,
blockHeight: blockHeight,
executionHash: executionHash,
lastAddBalanceAmount: new(big.Int),
refundSnapshot: make(map[int]uint64),
cachedContract: make(contractMap),
contractSnapshot: make(map[int]contractMap),
selfDestructed: make(deleteAccount),
selfDestructedSnapshot: make(map[int]deleteAccount),
preimages: make(preimageMap),
preimageSnapshot: make(map[int]preimageMap),
accessList: newAccessList(),
accessListSnapshot: make(map[int]*accessList),
transientStorage: newTransientStorage(),
transientStorageSnapshot: make(map[int]transientStorage),
logsSnapshot: make(map[int]int),
txLogsSnapshot: make(map[int]int),
sm: sm,
logs: []*action.Log{},
err: nil,
blockHeight: blockHeight,
executionHash: executionHash,
lastAddBalanceAmount: new(big.Int),
refundSnapshot: make(map[int]uint64),
cachedContract: make(contractMap),
contractSnapshot: make(map[int]contractMap),
selfDestructed: make(deleteAccount),
selfDestructedSnapshot: make(map[int]deleteAccount),
preimages: make(preimageMap),
preimageSnapshot: make(map[int]preimageMap),
accessList: newAccessList(),
accessListSnapshot: make(map[int]*accessList),
logsSnapshot: make(map[int]int),
txLogsSnapshot: make(map[int]int),
}
for _, opt := range opts {
if err := opt(s); err != nil {
Expand All @@ -211,6 +222,12 @@ func NewStateDBAdapter(
if !s.legacyNonceAccount && s.useConfirmedNonce {
return nil, errors.New("invalid parameter combination")
}
if s.enableCancun {
s.transientStorage = newTransientStorage()
s.transientStorageSnapshot = make(map[int]transientStorage)
s.createdAccount = make(createdAccount)
s.createdAccountSnapshot = make(map[int]createdAccount)
}
return s, nil
}

Expand Down Expand Up @@ -254,6 +271,9 @@ func (stateDB *StateDBAdapter) CreateAccount(evmAddr common.Address) {
if stateDB.assertError(err, "Failed to create account.", zap.Error(err), zap.String("address", evmAddr.Hex())) {
return
}
if stateDB.enableCancun {
stateDB.createdAccount[evmAddr] = struct{}{}
}
log.L().Debug("Called CreateAccount.", log.Hex("addrHash", evmAddr[:]))
}

Expand Down Expand Up @@ -443,6 +463,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()))
err = s.SubBalance(s.Balance)
if stateDB.assertError(err, "Failed to clear balance.", zap.Error(err), zap.String("address", evmAddr.Hex())) {
return
Expand All @@ -451,28 +472,10 @@ func (stateDB *StateDBAdapter) SelfDestruct(evmAddr common.Address) {
if stateDB.assertError(err, "Failed to kill contract.", zap.Error(err), zap.String("address", evmAddr.Hex())) {
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[evmAddr] = struct{}{}
}
Expand All @@ -483,6 +486,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 _, ok := stateDB.createdAccount[evmAddr]; ok {
stateDB.selfDestructed[evmAddr] = struct{}{}
}
}

// SetTransientState sets transient storage for a given account
func (stateDB *StateDBAdapter) SetTransientState(addr common.Address, key, value common.Hash) {
prev := stateDB.transientStorage.Get(addr, key)
Expand All @@ -497,12 +516,6 @@ func (stateDB *StateDBAdapter) GetTransientState(addr common.Address, key common
return stateDB.transientStorage.Get(addr, key)
}

// 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 @@ -632,14 +645,27 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) {
}
}
}
//restore transientStorage
stateDB.transientStorage = stateDB.transientStorageSnapshot[snapshot]
{
for i := snapshot; ; i++ {
if _, ok := stateDB.transientStorageSnapshot[i]; ok {
delete(stateDB.transientStorageSnapshot, i)
} else {
break
if stateDB.enableCancun {
//restore transientStorage
stateDB.transientStorage = stateDB.transientStorageSnapshot[snapshot]
{
for i := snapshot; ; i++ {
if _, ok := stateDB.transientStorageSnapshot[i]; ok {
delete(stateDB.transientStorageSnapshot, i)
} else {
break
}
}
}
// restore created accounts
stateDB.createdAccount = stateDB.createdAccountSnapshot[snapshot]
{
for i := snapshot; ; i++ {
if _, ok := stateDB.createdAccountSnapshot[i]; ok {
delete(stateDB.createdAccountSnapshot, i)
} else {
break
}
}
}
}
Expand Down Expand Up @@ -762,8 +788,16 @@ func (stateDB *StateDBAdapter) Snapshot() int {
stateDB.preimageSnapshot[sn] = p
// save a copy of access list
stateDB.accessListSnapshot[sn] = stateDB.accessList.Copy()
// save a copy of transient storage
stateDB.transientStorageSnapshot[sn] = stateDB.transientStorage.Copy()
if stateDB.enableCancun {
// save a copy of transient storage
stateDB.transientStorageSnapshot[sn] = stateDB.transientStorage.Copy()
// 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 @@ -864,6 +898,27 @@ func (stateDB *StateDBAdapter) accountState(evmAddr common.Address) (*state.Acco
return accountutil.LoadAccountByHash160(stateDB.sm, hash.BytesToHash160(evmAddr[:]), 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 @@ -1068,10 +1123,14 @@ func (stateDB *StateDBAdapter) clear() {
stateDB.preimageSnapshot = make(map[int]preimageMap)
stateDB.accessList = newAccessList()
stateDB.accessListSnapshot = make(map[int]*accessList)
stateDB.transientStorage = newTransientStorage()
stateDB.transientStorageSnapshot = make(map[int]transientStorage)
stateDB.logsSnapshot = make(map[int]int)
stateDB.txLogsSnapshot = make(map[int]int)
stateDB.logs = []*action.Log{}
stateDB.transactionLogs = []*action.TransactionLog{}
if stateDB.enableCancun {
stateDB.transientStorage = newTransientStorage()
stateDB.transientStorageSnapshot = make(map[int]transientStorage)
stateDB.createdAccount = make(createdAccount)
stateDB.createdAccountSnapshot = make(map[int]createdAccount)
}
}

0 comments on commit 4373a89

Please sign in to comment.