Repository: iotexproject/iotex-core
Pull request #4171 (2024-03-07 00:43:14)

[evm] add transaction log for Suicide()
# Description
When `Suicide()` is called upon destruction, the contract's balance will be transferred to a beneficiary. This needs to be captured in a transaction log  

Fixes #(issue)

## Type of change
Please delete options that are not relevant.
- [x] Bug fix (non-breaking change which fixes an issue)
- [] New feature (non-breaking change which adds functionality)
- [] Code refactor or improvement
- [] Breaking change (fix or feature that would cause a new or changed behavior of existing functionality)
- [] This change requires a documentation update

# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [x] make test
- [] fullsync
- [] Other test (please specify)

**Test Configuration**:
- Firmware version:
- Hardware:
- Toolchain:
- SDK:

# Checklist:
- [] My code follows the style guidelines of this project
- [] I have performed a self-review of my code
- [] I have commented my code, particularly in hard-to-understand areas
- [] I have made corresponding changes to the documentation
- [] My changes generate no new warnings
- [] I have added tests that prove my fix is effective or that my feature works
- [] New and existing unit tests pass locally with my changes
- [] Any dependent changes have been merged and published in downstream modules


sonarcloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [4.5% Duplication on New Code](https://sonarcloud.io/component_measures?id=iotexproject_iotex-core&pullRequest=4171&metric=new_duplicated_lines_density&view=list) (required ≤ 3%)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171)


sonarcloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [4.5% Duplication on New Code](https://sonarcloud.io/component_measures?id=iotexproject_iotex-core&pullRequest=4171&metric=new_duplicated_lines_density&view=list) (required ≤ 3%)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171)


sonarcloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [5.2% Duplication on New Code](https://sonarcloud.io/component_measures?id=iotexproject_iotex-core&pullRequest=4171&metric=new_duplicated_lines_density&view=list) (required ≤ 3%)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171)


dustinxie: > I think it is not a safe enough way to handle EVM upgrade in the future. If EVM has unexpected calls to `AddBalance` and `SubBalance` in the future, it would be difficult for us to detect.
> 
> Here is my proposal:
> 
> 1. Basically, operations on `AddBalance` and `SubBalance` not triggered by `MakeTransfer` are unexpected behaviors in terms of recording transaction logs, and these operations should be append to a `UnexpectedOpList`.
> 2. Based on the current logic of the EVM code, when `Suicide` is called, we can remove unexpected `AddBalance` records with the same address and amount from the list, thereby generating a transaction log.
> 3. After the action is executed, we should check the `UnexpectedOpList`. If it is not empty, we should return an error or panic.

good suggestion, will take care in further PRs
sonarcloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [5.1% Duplication on New Code](https://sonarcloud.io/component_measures?id=iotexproject_iotex-core&pullRequest=4171&metric=new_duplicated_lines_density&view=list) (required ≤ 3%)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=iotexproject_iotex-core&pullRequest=4171)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject) Report
Attention: Patch coverage is `84.00000%` with `8 lines` in your changes are missing coverage. Please review.
> Project coverage is 76.38%. Comparing base [(`e1f0636`)](https://app.codecov.io/gh/iotexproject/iotex-core/commit/e1f0636b27ca54fa57da8cadb3034431143833fa?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject) to head [(`df763eb`)](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject).
> Report is 194 commits behind head on master.

| [Files](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject) | Patch % | Lines |
|---|---|---|
| [action/protocol/execution/evm/evmstatedbadapter.go](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject#diff-YWN0aW9uL3Byb3RvY29sL2V4ZWN1dGlvbi9ldm0vZXZtc3RhdGVkYmFkYXB0ZXIuZ28=) | 87.23% | [4 Missing and 2 partials :warning: ](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject) |
| [action/protocol/execution/evm/evm.go](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject#diff-YWN0aW9uL3Byb3RvY29sL2V4ZWN1dGlvbi9ldm0vZXZtLmdv) | 0.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #4171      +/-   ##
==========================================
+ Coverage   75.38%   76.38%   +1.00%     
==========================================
  Files         303      340      +37     
  Lines       25923    29053    +3130     
==========================================
+ Hits        19541    22192    +2651     
- Misses       5360     5754     +394     
- Partials     1022     1107      +85     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/iotexproject/iotex-core/pull/4171?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=iotexproject).


Patch:
diff --git a/action/protocol/context.go b/action/protocol/context.go
index 1ee44c4b4..470a9331b 100644
--- a/action/protocol/context.go
+++ b/action/protocol/context.go
@@ -118,6 +118,7 @@ type (
 		SharedGasWithDapp                       bool
 		CandidateRegisterMustWithStake          bool
 		DisableDelegateEndorsement              bool
+		SuicideTxLogMismatchPanic               bool
 	}
 
 	// FeatureWithHeightCtx provides feature check functions.
@@ -261,6 +262,7 @@ func WithFeatureCtx(ctx context.Context) context.Context {
 			SharedGasWithDapp:                       g.IsToBeEnabled(height),
 			CandidateRegisterMustWithStake:          !g.IsToBeEnabled(height),
 			DisableDelegateEndorsement:              !g.IsToBeEnabled(height),
+			SuicideTxLogMismatchPanic:               g.IsToBeEnabled(height),
 		},
 	)
 }
diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go
index 74482f401..6eccfa647 100644
--- a/action/protocol/execution/evm/evm.go
+++ b/action/protocol/execution/evm/evm.go
@@ -402,6 +402,9 @@ func prepareStateDB(ctx context.Context, sm protocol.StateManager) (*StateDBAdap
 	if !featureCtx.CorrectGasRefund {
 		opts = append(opts, ManualCorrectGasRefundOption())
 	}
+	if featureCtx.SuicideTxLogMismatchPanic {
+		opts = append(opts, SuicideTxLogMismatchPanicOption())
+	}
 
 	return NewStateDBAdapter(
 		sm,
diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go
index a898d5986..4cb0c5476 100644
--- a/action/protocol/execution/evm/evmstatedbadapter.go
+++ b/action/protocol/execution/evm/evmstatedbadapter.go
@@ -48,6 +48,8 @@ type (
 		err                        error
 		blockHeight                uint64
 		executionHash              hash.Hash256
+		lastAddBalanceAddr         string
+		lastAddBalanceAmount       *big.Int
 		refund                     uint64
 		refundSnapshot             map[int]uint64
 		cachedContract             contractMap
@@ -68,6 +70,7 @@ type (
 		fixSnapshotOrder           bool
 		revertLog                  bool
 		manualCorrectGasRefund     bool
+		suicideTxLogMismatchPanic  bool
 	}
 )
 
@@ -141,6 +144,14 @@ func ManualCorrectGasRefundOption() StateDBAdapterOption {
 	}
 }
 
+// SuicideTxLogMismatchPanicOption set suicideTxLogMismatchPanic as true
+func SuicideTxLogMismatchPanicOption() StateDBAdapterOption {
+	return func(adapter *StateDBAdapter) error {
+		adapter.suicideTxLogMismatchPanic = true
+		return nil
+	}
+}
+
 // NewStateDBAdapter creates a new state db with iotex blockchain
 func NewStateDBAdapter(
 	sm protocol.StateManager,
@@ -149,22 +160,23 @@ func NewStateDBAdapter(
 	opts ...StateDBAdapterOption,
 ) (*StateDBAdapter, error) {
 	s := &StateDBAdapter{
-		sm:                 sm,
-		logs:               []*action.Log{},
-		err:                nil,
-		blockHeight:        blockHeight,
-		executionHash:      executionHash,
-		refundSnapshot:     make(map[int]uint64),
-		cachedContract:     make(contractMap),
-		contractSnapshot:   make(map[int]contractMap),
-		suicided:           make(deleteAccount),
-		suicideSnapshot:    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),
+		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),
+		suicided:             make(deleteAccount),
+		suicideSnapshot:      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 {
@@ -242,6 +254,7 @@ func (stateDB *StateDBAdapter) SubBalance(evmAddr common.Address, amount *big.In
 
 // AddBalance adds balance to account
 func (stateDB *StateDBAdapter) AddBalance(evmAddr common.Address, amount *big.Int) {
+	stateDB.lastAddBalanceAmount.SetUint64(0)
 	if amount.Cmp(big.NewInt(int64(0))) == 0 {
 		return
 	}
@@ -253,8 +266,10 @@ func (stateDB *StateDBAdapter) AddBalance(evmAddr common.Address, amount *big.In
 		log.L().Error("Failed to convert evm address.", zap.Error(err))
 		return
 	}
-	var state *state.Account
-	addrHash := hash.BytesToHash160(evmAddr[:])
+	var (
+		state    *state.Account
+		addrHash = hash.BytesToHash160(evmAddr[:])
+	)
 	if contract, ok := stateDB.cachedContract[addrHash]; ok {
 		state = contract.SelfState()
 	} else {
@@ -273,6 +288,10 @@ func (stateDB *StateDBAdapter) AddBalance(evmAddr common.Address, amount *big.In
 	if err := accountutil.StoreAccount(stateDB.sm, addr, state); err != nil {
 		log.L().Error("Failed to update pending account changes to trie.", zap.Error(err))
 		stateDB.logError(err)
+	} else {
+		// keep a record of latest add balance
+		stateDB.lastAddBalanceAddr = addr.String()
+		stateDB.lastAddBalanceAmount.SetBytes(amount.Bytes())
 	}
 }
 
@@ -396,6 +415,7 @@ func (stateDB *StateDBAdapter) Suicide(evmAddr common.Address) bool {
 		return false
 	}
 	// clears the account balance
+	actBalance := new(big.Int).Set(s.Balance)
 	if err := s.SubBalance(s.Balance); err != nil {
 		log.L().Debug("failed to clear balance", zap.Error(err), zap.String("address", evmAddr.Hex()))
 		return false
@@ -406,6 +426,25 @@ func (stateDB *StateDBAdapter) Suicide(evmAddr common.Address) bool {
 		stateDB.logError(err)
 		return false
 	}
+	// before calling Suicide, EVM will transfer the contract's balance to beneficiary
+	// need to create a transaction log on successful suicide
+	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 {
+		if stateDB.suicideTxLogMismatchPanic {
+			log.L().Panic("suicide contract's balance does not match",
+				zap.String("suicide", actBalance.String()),
+				zap.String("beneficiary", stateDB.lastAddBalanceAmount.String()))
+		}
+	}
 	// mark it as deleted
 	stateDB.suicided[addrHash] = struct{}{}
 	return true
@@ -710,7 +749,7 @@ func (stateDB *StateDBAdapter) AddLog(evmLog *types.Log) {
 		if amount, zero := new(big.Int).SetBytes(evmLog.Data), big.NewInt(0); amount.Cmp(zero) == 1 {
 			from, _ := address.FromBytes(topics[1][12:])
 			to, _ := address.FromBytes(topics[2][12:])
-			stateDB.transactionLogs = append(stateDB.transactionLogs, &action.TransactionLog{
+			stateDB.addTransactionLogs(&action.TransactionLog{
 				Type:      iotextypes.TransactionLogType_IN_CONTRACT_TRANSFER,
 				Sender:    from.String(),
 				Recipient: to.String(),
@@ -790,6 +829,10 @@ func (stateDB *StateDBAdapter) accountState(evmAddr common.Address) (*state.Acco
 	return accountutil.LoadAccountByHash160(stateDB.sm, addrHash, stateDB.accountCreationOpts()...)
 }
 
+func (stateDB *StateDBAdapter) addTransactionLogs(tlog *action.TransactionLog) {
+	stateDB.transactionLogs = append(stateDB.transactionLogs, tlog)
+}
+
 //======================================
 // Contract functions
 //======================================


Test patch:
diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go
index efe632376..ead8d568f 100644
--- a/action/protocol/execution/evm/evmstatedbadapter_test.go
+++ b/action/protocol/execution/evm/evmstatedbadapter_test.go
@@ -15,11 +15,11 @@ import (
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/golang/mock/gomock"
 	"github.com/iotexproject/go-pkgs/hash"
+	"github.com/iotexproject/iotex-address/address"
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/iotexproject/iotex-core/action"
 	"github.com/iotexproject/iotex-core/action/protocol"
 	"github.com/iotexproject/iotex-core/blockchain/genesis"
 	"github.com/iotexproject/iotex-core/db/batch"
@@ -99,11 +99,16 @@ func TestAddBalance(t *testing.T) {
 	require.NoError(err)
 	addAmount := big.NewInt(40000)
 	stateDB.AddBalance(addr, addAmount)
+	require.Equal(addAmount, stateDB.lastAddBalanceAmount)
+	beneficiary, _ := address.FromBytes(addr[:])
+	require.Equal(beneficiary.String(), stateDB.lastAddBalanceAddr)
 	amount := stateDB.GetBalance(addr)
-	require.Equal(0, amount.Cmp(addAmount))
+	require.Equal(amount, addAmount)
 	stateDB.AddBalance(addr, addAmount)
 	amount = stateDB.GetBalance(addr)
-	require.Equal(0, amount.Cmp(big.NewInt(80000)))
+	require.Equal(amount, big.NewInt(80000))
+	stateDB.AddBalance(addr, new(big.Int))
+	require.Zero(len(stateDB.lastAddBalanceAmount.Bytes()))
 }
 
 func TestRefundAPIs(t *testing.T) {
@@ -312,8 +317,8 @@ var tests = []stateDBTest{
 		},
 		15000,
 		[]sui{
-			{_c2, false, false},
-			{_c4, false, false},
+			{nil, _c4, _c2, false, false},
+			{nil, _c2, _c4, false, false},
 		},
 		[]image{
 			{common.BytesToHash(_v1[:]), []byte("cat")},
@@ -325,11 +330,8 @@ var tests = []stateDBTest{
 		[]*types.Log{
 			newTestLog(_c3), newTestLog(_c2), newTestLog(_c1),
 		},
-		[]*action.TransactionLog{
-			newTestTxLog(_c3), newTestTxLog(_c1),
-		},
-		3, 2,
-		"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", "io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
+		3, 0,
+		"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", "io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", "",
 	},
 	{
 		[]bal{
@@ -346,8 +348,8 @@ var tests = []stateDBTest{
 		},
 		2000,
 		[]sui{
-			{_c1, true, true},
-			{_c3, true, true},
+			{nil, _c4, _c1, true, true},
+			{big.NewInt(1000), _c2, _c3, true, true},
 		},
 		[]image{
 			{common.BytesToHash(_v3[:]), []byte("hen")},
@@ -359,11 +361,10 @@ var tests = []stateDBTest{
 		[]*types.Log{
 			newTestLog(_c4),
 		},
-		[]*action.TransactionLog{
-			newTestTxLog(_c2), newTestTxLog(_c1), newTestTxLog(_c4),
-		},
-		4, 5,
-		"io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q", "io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
+		4, 1,
+		"io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
+		"io1j4kjr6x5s8p6dyqlcfrxxdrsea32u2hpvpl5us",
+		"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
 	},
 	{
 		nil,
@@ -374,7 +375,7 @@ var tests = []stateDBTest{
 		},
 		15000,
 		[]sui{
-			{_addr1, true, true},
+			{big.NewInt(0), _c1, _addr1, true, true},
 		},
 		[]image{
 			{common.BytesToHash(_v4[:]), []byte("fox")},
@@ -385,9 +386,10 @@ var tests = []stateDBTest{
 		[]*types.Log{
 			newTestLog(_c1), newTestLog(_c2),
 		},
-		nil,
-		6, 5,
-		"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe", "io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
+		6, 2,
+		"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
+		"io1q2hz49tdy85dfqwr560pge3ngux0vf0vmhanad",
+		"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
 	},
 }
 
@@ -400,6 +402,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 		require.NoError(err)
 		opt := []StateDBAdapterOption{
 			NotFixTopicCopyBugOption(),
+			SuicideTxLogMismatchPanicOption(),
 		}
 		if async {
 			opt = append(opt, AsyncContractTrieOption())
@@ -432,8 +435,13 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 			stateDB.refund = test.refund
 			// set suicide
 			for _, e := range test.suicide {
+				if e.amount != nil {
+					stateDB.AddBalance(e.addr, e.amount)
+				}
+				stateDB.AddBalance(e.beneficiary, stateDB.GetBalance(e.addr)) // simulate transfer to beneficiary inside Suicide()
 				require.Equal(e.suicide, stateDB.Suicide(e.addr))
 				require.Equal(e.exist, stateDB.Exist(e.addr))
+				require.Zero(new(big.Int).Cmp(stateDB.GetBalance(e.addr)))
 			}
 			// set preimage
 			for _, e := range test.preimage {
@@ -462,13 +470,14 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 			for _, l := range test.logs {
 				stateDB.AddLog(l)
 			}
-			for _, l := range test.txLogs {
-				stateDB.transactionLogs = append(stateDB.transactionLogs, l)
-			}
+
 			require.Equal(test.logSize, len(stateDB.logs))
 			require.Equal(test.txLogSize, len(stateDB.transactionLogs))
 			require.Equal(test.logAddr, stateDB.logs[test.logSize-1].Address)
-			require.Equal(test.txLogAddr, stateDB.transactionLogs[test.txLogSize-1].Sender)
+			if test.txLogSize > 0 {
+				require.Equal(test.txSender, stateDB.transactionLogs[test.txLogSize-1].Sender)
+				require.Equal(test.txReceiver, stateDB.transactionLogs[test.txLogSize-1].Recipient)
+			}
 			require.Equal(i, stateDB.Snapshot())
 		}
 
@@ -486,11 +495,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 				},
 				tests[2].refund,
 				[]sui{
-					{_c1, true, true},
-					{_c3, true, true},
-					{_c2, false, true},
-					{_c4, false, false},
-					{_addr1, true, true},
+					{nil, common.Address{}, _c1, true, true},
+					{nil, common.Address{}, _c3, true, true},
+					{nil, common.Address{}, _c2, false, true},
+					{nil, common.Address{}, _c4, false, false},
+					{nil, common.Address{}, _addr1, true, true},
 				},
 				[]image{
 					{common.BytesToHash(_v1[:]), []byte("cat")},
@@ -502,9 +511,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 					{_c1, []common.Hash{_k1, _k2, _k3, _k4}, nil, true},
 					{_c2, []common.Hash{_k1, _k2, _k3, _k4}, nil, true},
 				},
-				nil, nil,
-				6, 5,
-				"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe", "io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
+				nil,
+				6, 2,
+				"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
+				"io1q2hz49tdy85dfqwr560pge3ngux0vf0vmhanad",
+				"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
 			},
 			{
 				[]bal{
@@ -514,11 +525,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 				tests[1].states,
 				tests[1].refund,
 				[]sui{
-					{_c1, true, true},
-					{_c3, true, true},
-					{_c2, false, true},
-					{_c4, false, false},
-					{_addr1, false, true},
+					{nil, common.Address{}, _c1, true, true},
+					{nil, common.Address{}, _c3, true, true},
+					{nil, common.Address{}, _c2, false, true},
+					{nil, common.Address{}, _c4, false, false},
+					{nil, common.Address{}, _addr1, false, true},
 				},
 				[]image{
 					{common.BytesToHash(_v1[:]), []byte("cat")},
@@ -530,9 +541,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 					{_c1, []common.Hash{_k1, _k2, _k3, _k4}, nil, true},
 					{_c2, []common.Hash{_k1, _k3}, []common.Hash{_k2, _k4}, true},
 				},
-				nil, nil,
-				4, 5,
-				"io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q", "io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
+				nil,
+				4, 1,
+				"io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
+				"io1j4kjr6x5s8p6dyqlcfrxxdrsea32u2hpvpl5us",
+				"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
 			},
 			{
 				[]bal{
@@ -546,11 +559,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 				},
 				tests[0].refund,
 				[]sui{
-					{_c1, false, true},
-					{_c3, false, true},
-					{_c2, false, false},
-					{_c4, false, false},
-					{_addr1, false, true},
+					{nil, common.Address{}, _c1, false, true},
+					{nil, common.Address{}, _c3, false, true},
+					{nil, common.Address{}, _c2, false, false},
+					{nil, common.Address{}, _c4, false, false},
+					{nil, common.Address{}, _addr1, false, true},
 				},
 				[]image{
 					{common.BytesToHash(_v1[:]), []byte("cat")},
@@ -562,9 +575,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 					{_c1, []common.Hash{_k1, _k2}, []common.Hash{_k3, _k4}, true},
 					{_c2, nil, []common.Hash{_k1, _k2, _k3, _k4}, false},
 				},
-				nil, nil,
-				3, 2,
-				"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", "io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
+				nil,
+				3, 0,
+				"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
+				"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
+				"",
 			},
 		}
 
@@ -619,12 +634,16 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
 				require.Equal(test.logSize, len(stateDB.logs))
 				require.Equal(test.txLogSize, len(stateDB.transactionLogs))
 				require.Equal(test.logAddr, stateDB.logs[test.logSize-1].Address)
-				require.Equal(test.txLogAddr, stateDB.transactionLogs[test.txLogSize-1].Sender)
+				if test.txLogSize > 0 {
+					require.Equal(test.txSender, stateDB.transactionLogs[test.txLogSize-1].Sender)
+					require.Equal(test.txReceiver, stateDB.transactionLogs[test.txLogSize-1].Recipient)
+				}
 			} else {
 				require.Equal(6, len(stateDB.logs))
-				require.Equal(5, len(stateDB.transactionLogs))
+				require.Equal(2, len(stateDB.transactionLogs))
 				require.Equal("io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe", stateDB.logs[5].Address)
-				require.Equal("io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q", stateDB.transactionLogs[4].Sender)
+				require.Equal("io1q2hz49tdy85dfqwr560pge3ngux0vf0vmhanad", stateDB.transactionLogs[1].Sender)
+				require.Equal("io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", stateDB.transactionLogs[1].Recipient)
 			}
 		}
 
diff --git a/action/protocol/execution/evm/testdata_test.go b/action/protocol/execution/evm/testdata_test.go
index fe2b625f3..ad3a9b703 100644
--- a/action/protocol/execution/evm/testdata_test.go
+++ b/action/protocol/execution/evm/testdata_test.go
@@ -6,9 +6,6 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/iotexproject/go-pkgs/hash"
-	"github.com/iotexproject/iotex-address/address"
-
-	"github.com/iotexproject/iotex-core/action"
 )
 
 type (
@@ -38,9 +35,11 @@ type (
 		v    common.Hash
 	}
 	sui struct {
-		addr    common.Address
-		suicide bool
-		exist   bool
+		amount      *big.Int
+		beneficiary common.Address
+		addr        common.Address
+		suicide     bool
+		exist       bool
 	}
 	image struct {
 		hash common.Hash
@@ -53,17 +52,16 @@ type (
 		exist bool
 	}
 	stateDBTest struct {
-		balance            []bal
-		codes              []code
-		states             []evmSet
-		refund             uint64
-		suicide            []sui
-		preimage           []image
-		accessList         []access
-		logs               []*types.Log
-		txLogs             []*action.TransactionLog
-		logSize, txLogSize int
-		logAddr, txLogAddr string
+		balance                       []bal
+		codes                         []code
+		states                        []evmSet
+		refund                        uint64
+		suicide                       []sui
+		preimage                      []image
+		accessList                    []access
+		logs                          []*types.Log
+		logSize, txLogSize            int
+		logAddr, txSender, txReceiver string
 	}
 )
 
@@ -100,10 +98,3 @@ func newTestLog(addr common.Address) *types.Log {
 		Topics:  []common.Hash{_k1},
 	}
 }
-
-func newTestTxLog(addr common.Address) *action.TransactionLog {
-	a, _ := address.FromBytes(addr.Bytes())
-	return &action.TransactionLog{
-		Sender: a.String(),
-	}
-}