Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[evm] add transaction log for Suicide() #4171

Merged
merged 3 commits into from
Mar 12, 2024
Merged

Conversation

dustinxie
Copy link
Member

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.

  • 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

  • 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

@@ -406,6 +412,17 @@ 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 len(stateDB.lastAddBalanceAmount.Bytes()) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

should be check if stateDB.lastAddBalanceAmount != nil && stateDB.lastAddBalanceAmount.Cmp(big.NewInt(0))>0 to prevent nil or negative amount

Copy link
Member Author

Choose a reason for hiding this comment

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

in AddBalance(), nil or negative amount will be caught and return error, in other words, lastAddBalanceAmount must be valid positive number here

add initialization of the value lastAddBalanceAmount in new to prevent being nil

@@ -242,6 +245,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)
Copy link
Contributor

Choose a reason for hiding this comment

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

the change is only in AddBalance, does SubBalance also need it

@@ -242,6 +245,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)
Copy link
Collaborator

Choose a reason for hiding this comment

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

what's the purpose?

Copy link
Member Author

Choose a reason for hiding this comment

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

to clear the amount for each AddBalance(), consider the following:

  1. AddBalance(4000)
  2. AddBalance(0)
  3. Inside Suicide, it calls AddBalance(4000) to beneficiary but failed, if the amount is not cleared, it will mistakenly think the amount match and issue a tx log with amount=4000, but the 4000 is in step 1 and is mistaken

// need to create a transaction log on successful suicide
if len(stateDB.lastAddBalanceAmount.Bytes()) > 0 {
from, _ := address.FromBytes(evmAddr[:])
stateDB.addTransactionLogs(&action.TransactionLog{
Copy link
Collaborator

Choose a reason for hiding this comment

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

if lastAddBalanceAmount is not set or the amount is different from balance, panic?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed in latest commit

Copy link
Member

@envestcc envestcc left a comment

Choose a reason for hiding this comment

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

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.

@dustinxie
Copy link
Member Author

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

Copy link

codecov bot commented Mar 11, 2024

Codecov Report

Attention: Patch coverage is 84.00000% with 8 lines in your changes are missing coverage. Please review.

Project coverage is 76.18%. Comparing base (e1f0636) to head (461ef9f).
Report is 198 commits behind head on master.

Files Patch % Lines
action/protocol/execution/evm/evmstatedbadapter.go 87.23% 4 Missing and 2 partials ⚠️
action/protocol/execution/evm/evm.go 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4171      +/-   ##
==========================================
+ Coverage   75.38%   76.18%   +0.80%     
==========================================
  Files         303      340      +37     
  Lines       25923    29094    +3171     
==========================================
+ Hits        19541    22165    +2624     
- Misses       5360     5829     +469     
- Partials     1022     1100      +78     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

})
}
} else {
if stateDB.suicideTxLogMismatchPanic {
Copy link
Member Author

Choose a reason for hiding this comment

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

for safety, we gate the panic with a flag
after hard-fork, we can run a fullsync w/o the flag, and remove this flag if fullsync pass

Copy link
Collaborator

Choose a reason for hiding this comment

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

https://github.com/iotexproject/iotex-core/pull/4171/files#r1519122441 if we panic here, what's the purpose of resetting the mount?

Copy link
Member Author

Choose a reason for hiding this comment

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

I gave an example above, if we don't reset the amount, it is possible to generate a mistaken txLog for an earlier AddBalance() whose amount happens to equal to suicide address

@@ -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),
Copy link
Collaborator

Choose a reason for hiding this comment

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

default value should be the value after hard fork

Copy link
Member Author

Choose a reason for hiding this comment

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

is this a must? to me using a positive (enable after height) flag name will help improve code readability and reduce chance of programming mistake

// 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 len(stateDB.lastAddBalanceAmount.Bytes()) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

ignore efficiency and use stateDB.lastAddBalanceAmount.Cmp(big.NewInt(0)) > 0

Copy link
Member Author

Choose a reason for hiding this comment

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

updated

})
}
} else {
if stateDB.suicideTxLogMismatchPanic {
Copy link
Collaborator

Choose a reason for hiding this comment

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

https://github.com/iotexproject/iotex-core/pull/4171/files#r1519122441 if we panic here, what's the purpose of resetting the mount?

Copy link

sonarcloud bot commented Mar 11, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
5.5% Duplication on New Code (required ≤ 3%)

See analysis details on SonarCloud

@@ -48,6 +48,8 @@ type (
err error
blockHeight uint64
executionHash hash.Hash256
lastAddBalanceAddr string
Copy link
Member

@Liuhaai Liuhaai Mar 12, 2024

Choose a reason for hiding this comment

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

Retrieving the info from statedb/snapshot ( getStateObject(addr common.Address) in geth(https://github.com/ethereum/go-ethereum/blob/ebf9e11af2ff701d0961623e817d37b421b96802/core/state/statedb.go#L540) ) seems more reasonable than two new variables under StateDBAdapter for me

Copy link
Member Author

Choose a reason for hiding this comment

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

can you elaborate? we cannot get the beneficiary's address in StateDBAdapter, so capture it in AddBalance every time. also we need the transfer amount, not the beneficiary's entire balance

@dustinxie dustinxie merged commit 135d9c1 into iotexproject:master Mar 12, 2024
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants