core/state: move state log mechanism to a separate layer #30569 #30732 (#1775)

This commit is contained in:
Daniel Liu 2025-12-16 11:33:19 +08:00 committed by GitHub
parent d9867ea87d
commit ebbbdf2bff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 674 additions and 179 deletions

View file

@ -43,7 +43,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/bloombits" "github.com/XinFinOrg/XDPoSChain/core/bloombits"
"github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
@ -738,7 +737,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
// Set infinite balance to the fake caller account. // Set infinite balance to the fake caller account.
from := stateDB.GetOrNewStateObject(call.From) from := stateDB.GetOrNewStateObject(call.From)
from.SetBalance(math.MaxBig256, tracing.BalanceChangeUnspecified) from.SetBalance(math.MaxBig256)
// Execute the call. // Execute the call.
msg := &core.Message{ msg := &core.Message{

View file

@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/clique" "github.com/XinFinOrg/XDPoSChain/consensus/clique"
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/event"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
@ -276,7 +277,7 @@ func (x *XDPoS) Prepare(chain consensus.ChainReader, header *types.Header) error
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block // Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given, and returns the final block. // rewards given, and returns the final block.
func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (x *XDPoS) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
switch x.config.BlockConsensusVersion(header.Number) { switch x.config.BlockConsensusVersion(header.Number) {
case params.ConsensusEngineVersion2: case params.ConsensusEngineVersion2:
return x.EngineV2.Finalize(chain, header, state, parentState, txs, uncles, receipts) return x.EngineV2.Finalize(chain, header, state, parentState, txs, uncles, receipts)

View file

@ -21,6 +21,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
@ -53,7 +54,7 @@ type XDPoS_v1 struct {
signFn clique.SignerFn // Signer function to authorize hashes with signFn clique.SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields lock sync.RWMutex // Protects the signer fields
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookReward func(chain consensus.ChainReader, state vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error)
HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error) HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error)
HookValidator func(header *types.Header, signers []common.Address) ([]byte, error) HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
@ -823,7 +824,7 @@ func (x *XDPoS_v1) UpdateMasternodes(chain consensus.ChainReader, header *types.
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block // Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given, and returns the final block. // rewards given, and returns the final block.
func (x *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (x *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// set block reward // set block reward
number := header.Number.Uint64() number := header.Number.Uint64()
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint rCheckpoint := chain.Config().XDPoS.RewardCheckpoint

View file

@ -23,6 +23,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
@ -74,7 +75,7 @@ type XDPoS_v2 struct {
latestReward map[string]interface{} latestReward map[string]interface{}
latestRewardBlocknum uint64 latestRewardBlocknum uint64
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookReward func(chain consensus.ChainReader, state vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error)
HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error) HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error)
ForensicsProcessor *Forensics ForensicsProcessor *Forensics
@ -410,7 +411,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block // Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given, and returns the final block. // rewards given, and returns the final block.
func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// set block reward // set block reward
isEpochSwitch, _, err := x.IsEpochSwitch(header) isEpochSwitch, _, err := x.IsEpochSwitch(header)

View file

@ -34,6 +34,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
@ -584,7 +585,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block // Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given, and returns the final block. // rewards given, and returns the final block.
func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped // No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil) header.UncleHash = types.CalcUncleHash(nil)

View file

@ -23,6 +23,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rpc" "github.com/XinFinOrg/XDPoSChain/rpc"
) )
@ -83,7 +84,7 @@ type Engine interface {
// and assembles the final block. // and assembles the final block.
// Note: The block header and state database might be updated to reflect any // Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards). // consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, Finalize(chain ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new block for the given input block with the local miner's // Seal generates a new block for the given input block with the local miner's

View file

@ -29,6 +29,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/trie" "github.com/XinFinOrg/XDPoSChain/trie"
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
@ -433,7 +434,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
// Finalize implements consensus.Engine, accumulating the block and uncle rewards, // Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state and assembling the block. // setting the final state and assembling the block.
func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state vm.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles) accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
@ -451,7 +452,7 @@ var (
// AccumulateRewards credits the coinbase of the given block with the mining // AccumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for // reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded. // included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) { func accumulateRewards(config *params.ChainConfig, stateDB vm.StateDB, header *types.Header, uncles []*types.Header) {
// Select the correct block reward based on chain progression // Select the correct block reward based on chain progression
blockReward := FrontierBlockReward blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) { if config.IsByzantium(header.Number) {

View file

@ -21,9 +21,9 @@ import (
"errors" "errors"
"math/big" "math/big"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
) )
@ -73,7 +73,7 @@ func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header)
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork // ApplyDAOHardFork modifies the state database according to the DAO hard-fork
// rules, transferring all balances of a set of DAO accounts to a single refund // rules, transferring all balances of a set of DAO accounts to a single refund
// contract. // contract.
func ApplyDAOHardFork(statedb *state.StateDB) { func ApplyDAOHardFork(statedb vm.StateDB) {
// Retrieve the contract to refund balances into // Retrieve the contract to refund balances into
if !statedb.Exist(params.DAORefundContract) { if !statedb.Exist(params.DAORefundContract) {
statedb.CreateAccount(params.DAORefundContract) statedb.CreateAccount(params.DAORefundContract)
@ -81,7 +81,8 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
// Move every DAO account and extra-balance account funds into the refund contract // Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList() { for _, addr := range params.DAODrainList() {
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract) balance := statedb.GetBalance(addr)
statedb.SetBalance(addr, new(big.Int), tracing.BalanceDecreaseDaoAccount) statedb.AddBalance(params.DAORefundContract, balance, tracing.BalanceIncreaseDaoContract)
statedb.SubBalance(addr, balance, tracing.BalanceDecreaseDaoAccount)
} }
} }

View file

@ -1552,7 +1552,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
if err != nil { if err != nil {
return it.index, events, coalescedLogs, err return it.index, events, coalescedLogs, err
} }
statedb.SetLogger(bc.logger)
// If we have a followup block, run that against the current state to pre-cache // If we have a followup block, run that against the current state to pre-cache
// transactions and probabilistically some of the account/storage trie nodes. // transactions and probabilistically some of the account/storage trie nodes.

View file

@ -117,7 +117,7 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
} }
} }
// CanTransfer checks wether there are enough funds in the address' account to make a transfer. // CanTransfer checks whether there are enough funds in the address' account to make a transfer.
// This does not take the necessary gas in to account to make the transfer valid. // This does not take the necessary gas in to account to make the transfer valid.
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
return db.GetBalance(addr).Cmp(amount) >= 0 return db.GetBalance(addr).Cmp(amount) >= 0

View file

@ -85,11 +85,21 @@ func (j *journal) length() int {
return len(j.entries) return len(j.entries)
} }
func (j *journal) createContract(addr common.Address) {
j.append(createContractChange{account: addr})
}
type ( type (
// Changes to the account trie. // Changes to the account trie.
createObjectChange struct { createObjectChange struct {
account common.Address account common.Address
} }
// createContractChange represents an account becoming a contract-account.
// This event happens prior to executing initcode. The journal-event simply
// manages the created-flag, in order to allow same-tx destruction.
createContractChange struct {
account common.Address
}
resetObjectChange struct { resetObjectChange struct {
account common.Address account common.Address
prev *stateObject prev *stateObject
@ -156,6 +166,14 @@ func (ch createObjectChange) dirtied() *common.Address {
return &ch.account return &ch.account
} }
func (ch createContractChange) revert(s *StateDB) {
s.getStateObject(ch.account).created = false
}
func (ch createContractChange) dirtied() *common.Address {
return nil
}
func (ch resetObjectChange) revert(s *StateDB) { func (ch resetObjectChange) revert(s *StateDB) {
s.setStateObject(ch.prev) s.setStateObject(ch.prev)
if !ch.prevdestruct { if !ch.prevdestruct {

View file

@ -25,7 +25,6 @@ import (
"time" "time"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
@ -218,11 +217,12 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
} }
// SetState updates a value in account storage. // SetState updates a value in account storage.
func (s *stateObject) SetState(db Database, key, value common.Hash) { func (s *stateObject) SetState(db Database, key, value common.Hash) common.Hash {
// If the new value is the same as old, don't set // If the new value is the same as old, don't set. Otherwise, track only the
// dirty changes, supporting reverting all of it back to no change.
prev := s.GetState(db, key) prev := s.GetState(db, key)
if prev == value { if prev == value {
return return prev
} }
// New value is different, update and journal the change // New value is different, update and journal the change
s.db.journal.append(storageChange{ s.db.journal.append(storageChange{
@ -230,10 +230,8 @@ func (s *stateObject) SetState(db Database, key, value common.Hash) {
key: key, key: key,
prevalue: prev, prevalue: prev,
}) })
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
s.db.logger.OnStorageChange(s.address, key, prev, value)
}
s.setState(key, value) s.setState(key, value)
return prev
} }
func (s *stateObject) setState(key, value common.Hash) { func (s *stateObject) setState(key, value common.Hash) {
@ -338,36 +336,28 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
// AddBalance adds amount to s's balance. // AddBalance adds amount to s's balance.
// It is used to add funds to the destination account of a transfer. // It is used to add funds to the destination account of a transfer.
func (s *stateObject) AddBalance(amount *big.Int, reason tracing.BalanceChangeReason) { // returns the previous balance
func (s *stateObject) AddBalance(amount *big.Int) *big.Int {
// EIP161: We must check emptiness for the objects such that the account // EIP161: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect. // clearing (0,0,0 objects) can take effect.
if amount.Sign() == 0 { if amount.Sign() == 0 {
if s.empty() { if s.empty() {
s.touch() s.touch()
} }
return return new(big.Int).Set(s.Balance())
} }
s.SetBalance(new(big.Int).Add(s.Balance(), amount), reason) return s.SetBalance(new(big.Int).Add(s.Balance(), amount))
} }
// SubBalance removes amount from s's balance. // SetBalance sets the balance for the object, and returns the previous balance.
// It is used to remove funds from the origin account of a transfer. func (s *stateObject) SetBalance(amount *big.Int) *big.Int {
func (s *stateObject) SubBalance(amount *big.Int, reason tracing.BalanceChangeReason) { prev := new(big.Int).Set(s.data.Balance)
if amount.Sign() == 0 {
return
}
s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason)
}
func (s *stateObject) SetBalance(amount *big.Int, reason tracing.BalanceChangeReason) {
s.db.journal.append(balanceChange{ s.db.journal.append(balanceChange{
account: s.address, account: s.address,
prev: new(big.Int).Set(s.data.Balance), prev: new(big.Int).Set(s.data.Balance),
}) })
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
s.db.logger.OnBalanceChange(s.address, s.Balance(), amount, reason)
}
s.setBalance(amount) s.setBalance(amount)
return prev
} }
func (s *stateObject) setBalance(amount *big.Int) { func (s *stateObject) setBalance(amount *big.Int) {
@ -437,9 +427,6 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
account: s.address, account: s.address,
prevCode: prevCode, prevCode: prevCode,
}) })
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevCode, codeHash, code)
}
s.setCode(codeHash, code) s.setCode(codeHash, code)
} }
@ -454,9 +441,6 @@ func (s *stateObject) SetNonce(nonce uint64) {
account: s.address, account: s.address,
prev: s.data.Nonce, prev: s.data.Nonce,
}) })
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
}
s.setNonce(nonce) s.setNonce(nonce)
} }

View file

@ -24,7 +24,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
@ -49,11 +48,11 @@ func TestDump(t *testing.T) {
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified) obj1.AddBalance(big.NewInt(22))
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified) obj3.SetBalance(big.NewInt(44))
// write some of them to the trie // write some of them to the trie
s.state.updateStateObject(obj1) s.state.updateStateObject(obj1)
@ -101,13 +100,13 @@ func TestIterativeDump(t *testing.T) {
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified) obj1.AddBalance(big.NewInt(22))
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified) obj3.SetBalance(big.NewInt(44))
obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00}))
obj4.AddBalance(big.NewInt(1337), tracing.BalanceChangeUnspecified) obj4.AddBalance(big.NewInt(1337))
// write some of them to the trie // write some of them to the trie
s.state.updateStateObject(obj1) s.state.updateStateObject(obj1)
@ -203,7 +202,7 @@ func TestSnapshot2(t *testing.T) {
// db, trie are already non-empty values // db, trie are already non-empty values
so0 := state.getStateObject(stateobjaddr0) so0 := state.getStateObject(stateobjaddr0)
so0.SetBalance(big.NewInt(42), tracing.BalanceChangeUnspecified) so0.SetBalance(big.NewInt(42))
so0.SetNonce(43) so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
so0.selfDestructed = false so0.selfDestructed = false
@ -215,7 +214,7 @@ func TestSnapshot2(t *testing.T) {
// and one with deleted == true // and one with deleted == true
so1 := state.getStateObject(stateobjaddr1) so1 := state.getStateObject(stateobjaddr1)
so1.SetBalance(big.NewInt(52), tracing.BalanceChangeUnspecified) so1.SetBalance(big.NewInt(52))
so1.SetNonce(53) so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
so1.selfDestructed = true so1.selfDestructed = true

View file

@ -47,9 +47,8 @@ type revision struct {
// * Contracts // * Contracts
// * Accounts // * Accounts
type StateDB struct { type StateDB struct {
db Database db Database
trie Trie trie Trie
logger *tracing.Hooks
// originalRoot is the pre-state root, before any changes were made. // originalRoot is the pre-state root, before any changes were made.
// It will be updated when the Commit is called. // It will be updated when the Commit is called.
@ -139,11 +138,6 @@ func New(root common.Hash, db Database) (*StateDB, error) {
}, nil }, nil
} }
// SetLogger sets the logger for account update hooks.
func (s *StateDB) SetLogger(l *tracing.Hooks) {
s.logger = l
}
// setError remembers the first non-nil error it is called with. // setError remembers the first non-nil error it is called with.
func (s *StateDB) setError(err error) { func (s *StateDB) setError(err error) {
if s.dbErr == nil { if s.dbErr == nil {
@ -183,9 +177,6 @@ func (s *StateDB) AddLog(log *types.Log) {
log.TxHash = s.thash log.TxHash = s.thash
log.TxIndex = uint(s.txIndex) log.TxIndex = uint(s.txIndex)
log.Index = s.logSize log.Index = s.logSize
if s.logger != nil && s.logger.OnLog != nil {
s.logger.OnLog(log)
}
s.logs[s.thash] = append(s.logs[s.thash], log) s.logs[s.thash] = append(s.logs[s.thash], log)
s.logSize++ s.logSize++
} }
@ -390,25 +381,31 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
*/ */
// AddBalance adds amount to the account associated with addr. // AddBalance adds amount to the account associated with addr.
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, _ tracing.BalanceChangeReason) *big.Int {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject == nil {
stateObject.AddBalance(amount, reason) return new(big.Int)
} }
return stateObject.AddBalance(amount)
} }
// SubBalance subtracts amount from the account associated with addr. // SubBalance subtracts amount from the account associated with addr.
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, _ tracing.BalanceChangeReason) *big.Int {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject == nil {
stateObject.SubBalance(amount, reason) return new(big.Int)
} }
prev := stateObject.Balance()
if amount.Sign() == 0 {
return new(big.Int).Set(prev)
}
return stateObject.SetBalance(new(big.Int).Sub(prev, amount))
} }
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, _ tracing.BalanceChangeReason) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetBalance(amount, reason) stateObject.SetBalance(amount)
} }
} }
@ -426,11 +423,11 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) {
} }
} }
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash {
stateObject := s.GetOrNewStateObject(addr) if stateObject := s.GetOrNewStateObject(addr); stateObject != nil {
if stateObject != nil { return stateObject.SetState(s.db, key, value)
stateObject.SetState(s.db, key, value)
} }
return common.Hash{}
} }
// SetStorage replaces the entire storage for the specified account with given // SetStorage replaces the entire storage for the specified account with given
@ -458,7 +455,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
if obj != nil { if obj != nil {
newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code) newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code)
newObj.SetNonce(obj.Nonce()) newObj.SetNonce(obj.Nonce())
newObj.SetBalance(obj.Balance(), tracing.BalanceChangeUnspecified) newObj.SetBalance(obj.Balance())
} }
} }
@ -467,36 +464,40 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
// //
// The account's state object is still available until the state is committed, // The account's state object is still available until the state is committed,
// getStateObject will return a non-nil account after SelfDestruct. // getStateObject will return a non-nil account after SelfDestruct.
func (s *StateDB) SelfDestruct(addr common.Address) { func (s *StateDB) SelfDestruct(addr common.Address) *big.Int {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
prevBalance := new(big.Int)
if stateObject == nil { if stateObject == nil {
return return prevBalance
} }
var ( prevBalance.Set(stateObject.Balance())
prev = new(big.Int).Set(stateObject.Balance()) // Regardless of whether it is already destructed or not, we do have to
n = new(big.Int) // journal the balance-change, if we set it to zero here.
) if prevBalance.Sign() != 0 {
s.journal.append(selfDestructChange{ stateObject.SetBalance(new(big.Int))
account: addr,
prev: stateObject.selfDestructed,
prevbalance: prev,
})
if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 {
s.logger.OnBalanceChange(addr, prev, n, tracing.BalanceDecreaseSelfdestruct)
} }
stateObject.markSelfdestructed() // If it is already marked as self-destructed, we do not need to add it
stateObject.data.Balance = n // for journalling a second time.
if !stateObject.selfDestructed {
s.journal.append(selfDestructChange{
account: addr,
prev: stateObject.selfDestructed,
prevbalance: prevBalance,
})
stateObject.markSelfdestructed()
}
return prevBalance
} }
func (s *StateDB) Selfdestruct6780(addr common.Address) { func (s *StateDB) SelfDestruct6780(addr common.Address) (*big.Int, bool) {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
if stateObject == nil { if stateObject == nil {
return return new(big.Int), false
} }
if stateObject.created { if stateObject.created {
s.SelfDestruct(addr) return s.SelfDestruct(addr), true
} }
return new(big.Int).Set(stateObject.Balance()), false
} }
// SetTransientState sets transient storage for a given account. It // SetTransientState sets transient storage for a given account. It
@ -653,6 +654,19 @@ func (s *StateDB) CreateAccount(addr common.Address) {
} }
} }
// CreateContract is used whenever a contract is created. This may be preceded
// by CreateAccount, but that is not required if it already existed in the
// state due to funds sent beforehand.
// This operation sets the 'newContract'-flag, which is required in order to
// correctly handle EIP-6780 'delete-in-same-transaction' logic.
func (s *StateDB) CreateContract(addr common.Address) {
obj := s.getStateObject(addr)
if obj != nil && !obj.created {
obj.created = true
s.journal.createContract(addr)
}
}
func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
so := s.getStateObject(addr) so := s.getStateObject(addr)
if so == nil { if so == nil {
@ -801,14 +815,11 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
obj.deleted = true obj.deleted = true
// We need to maintain account deletions explicitly (will remain // We need to maintain account deletions explicitly (will remain
// set indefinitely). // set indefinitely). Note only the first occurred self-destruct
s.stateObjectsDestruct[obj.address] = struct{}{} // event is tracked.
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
// If ether was sent to account post-selfdestruct it is burnt. s.stateObjectsDestruct[obj.address] = struct{}{}
if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 {
s.logger.OnBalanceChange(obj.address, bal, new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
} }
} else { } else {
obj.finalise() obj.finalise()

View file

@ -0,0 +1,277 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package state
import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
)
// hookedStateDB represents a statedb which emits calls to tracing-hooks
// on state operations.
type hookedStateDB struct {
inner *StateDB
hooks *tracing.Hooks
}
// NewHookedState wraps the given stateDb with the given hooks
func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB {
s := &hookedStateDB{stateDb, hooks}
if s.hooks == nil {
s.hooks = new(tracing.Hooks)
}
return s
}
func (s *hookedStateDB) CreateAccount(addr common.Address) {
s.inner.CreateAccount(addr)
}
func (s *hookedStateDB) CreateContract(addr common.Address) {
s.inner.CreateContract(addr)
}
func (s *hookedStateDB) GetBalance(addr common.Address) *big.Int {
return s.inner.GetBalance(addr)
}
func (s *hookedStateDB) GetNonce(addr common.Address) uint64 {
return s.inner.GetNonce(addr)
}
func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash {
return s.inner.GetCodeHash(addr)
}
func (s *hookedStateDB) GetCode(addr common.Address) []byte {
return s.inner.GetCode(addr)
}
func (s *hookedStateDB) GetCodeSize(addr common.Address) int {
return s.inner.GetCodeSize(addr)
}
func (s *hookedStateDB) AddRefund(u uint64) {
s.inner.AddRefund(u)
}
func (s *hookedStateDB) SubRefund(u uint64) {
s.inner.SubRefund(u)
}
func (s *hookedStateDB) GetRefund() uint64 {
return s.inner.GetRefund()
}
func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
return s.inner.GetCommittedState(addr, hash)
}
func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
return s.inner.GetState(addr, hash)
}
func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash {
return s.inner.GetStorageRoot(addr)
}
func (s *hookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
return s.inner.GetTransientState(addr, key)
}
func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) {
s.inner.SetTransientState(addr, key, value)
}
func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool {
return s.inner.HasSelfDestructed(addr)
}
func (s *hookedStateDB) Exist(addr common.Address) bool {
return s.inner.Exist(addr)
}
func (s *hookedStateDB) Empty(addr common.Address) bool {
return s.inner.Empty(addr)
}
func (s *hookedStateDB) AddressInAccessList(addr common.Address) bool {
return s.inner.AddressInAccessList(addr)
}
func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
return s.inner.SlotInAccessList(addr, slot)
}
func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) {
s.inner.AddAddressToAccessList(addr)
}
func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
s.inner.AddSlotToAccessList(addr, slot)
}
func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) {
s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses)
}
func (s *hookedStateDB) RevertToSnapshot(i int) {
s.inner.RevertToSnapshot(i)
}
func (s *hookedStateDB) Snapshot() int {
return s.inner.Snapshot()
}
func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
s.inner.AddPreimage(hash, bytes)
}
func (s *hookedStateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int {
prev := s.inner.SubBalance(addr, amount, reason)
if s.hooks.OnBalanceChange != nil && amount.Sign() != 0 {
newBalance := new(big.Int).Sub(prev, amount)
s.hooks.OnBalanceChange(addr, prev, newBalance, reason)
}
return prev
}
func (s *hookedStateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int {
prev := s.inner.AddBalance(addr, amount, reason)
if s.hooks.OnBalanceChange != nil && amount.Sign() != 0 {
newBalance := new(big.Int).Add(prev, amount)
s.hooks.OnBalanceChange(addr, prev, newBalance, reason)
}
return prev
}
func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) {
prev := s.inner.GetNonce(address)
s.inner.SetNonce(address, nonce)
if s.hooks.OnNonceChange != nil {
s.hooks.OnNonceChange(address, prev, nonce)
}
}
func (s *hookedStateDB) SetCode(address common.Address, code []byte) {
prevCode := s.inner.GetCode(address)
s.inner.SetCode(address, code)
if s.hooks.OnCodeChange != nil {
prevHash := crypto.Keccak256Hash(prevCode)
codeHash := crypto.Keccak256Hash(code)
// Invoke the hooks only if the contract code is changed
if prevHash != codeHash {
s.hooks.OnCodeChange(address, prevHash, prevCode, codeHash, code)
}
}
}
func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash {
prev := s.inner.SetState(address, key, value)
if s.hooks.OnStorageChange != nil && prev != value {
s.hooks.OnStorageChange(address, key, prev, value)
}
return prev
}
func (s *hookedStateDB) SelfDestruct(address common.Address) *big.Int {
prev := s.inner.SelfDestruct(address)
if s.hooks.OnBalanceChange != nil {
s.hooks.OnBalanceChange(address, prev, new(big.Int), tracing.BalanceDecreaseSelfdestruct)
}
return prev
}
func (s *hookedStateDB) SelfDestruct6780(address common.Address) (*big.Int, bool) {
prev, changed := s.inner.SelfDestruct6780(address)
if changed {
if s.hooks.OnBalanceChange != nil {
s.hooks.OnBalanceChange(address, prev, new(big.Int), tracing.BalanceDecreaseSelfdestruct)
}
}
return prev, changed
}
func (s *hookedStateDB) AddLog(log *types.Log) {
// The inner will modify the log (add fields), so invoke that first
s.inner.AddLog(log)
if s.hooks.OnLog != nil {
s.hooks.OnLog(log)
}
}
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
defer s.inner.Finalise(deleteEmptyObjects)
if s.hooks.OnBalanceChange == nil {
return
}
for addr := range s.inner.journal.dirties {
obj := s.inner.stateObjects[addr]
if obj != nil && obj.selfDestructed {
// If ether was sent to account post-selfdestruct it is burnt.
if bal := obj.Balance(); bal.Sign() != 0 {
s.hooks.OnBalanceChange(addr, bal, new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
}
}
}
}
func (s *hookedStateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
return s.inner.IntermediateRoot(deleteEmptyObjects)
}
func (s *hookedStateDB) UpdateTRC21Fee(newBalance map[common.Address]*big.Int, totalFeeUsed *big.Int) {
s.inner.UpdateTRC21Fee(newBalance, totalFeeUsed)
}
func (s *hookedStateDB) PutMintedRecordOnsetBlock(value common.Hash) {
s.inner.PutMintedRecordOnsetBlock(value)
}
func (s *hookedStateDB) PutMintedRecordOnsetEpoch(value common.Hash) {
s.inner.PutMintedRecordOnsetEpoch(value)
}
func (s *hookedStateDB) GetPostBurned(epoch uint64) common.Hash {
return s.inner.GetPostBurned(epoch)
}
func (s *hookedStateDB) PutPostBurned(epoch uint64, value common.Hash) {
s.inner.PutPostBurned(epoch, value)
}
func (s *hookedStateDB) GetPostMinted(epoch uint64) common.Hash {
return s.inner.GetPostMinted(epoch)
}
func (s *hookedStateDB) PutPostMinted(epoch uint64, value common.Hash) {
s.inner.PutPostMinted(epoch, value)
}
func (s *hookedStateDB) PutPostRewardBlock(epoch uint64, value common.Hash) {
s.inner.PutPostRewardBlock(epoch, value)
}
func (s *hookedStateDB) IncrementMintedRecordNonce() {
s.inner.IncrementMintedRecordNonce()
}

View file

@ -0,0 +1,129 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package state
import (
"fmt"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types"
)
// This method tests that the 'burn' from sending-to-selfdestructed accounts
// is accounted for.
// (There is also a higher-level test in eth/tracers: TestSupplySelfDestruct )
func TestBurn(t *testing.T) {
// Note: burn can happen even after EIP-6780, if within one single transaction,
// the following occur:
// 1. contract B creates contract A
// 2. contract A is destructed
// 3. contract B sends ether to A
var burned = new(big.Int)
s, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
hooked := NewHookedState(s, &tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
if reason == tracing.BalanceDecreaseSelfdestructBurn {
burned.Add(burned, prev)
}
},
})
createAndDestroy := func(addr common.Address) {
hooked.AddBalance(addr, big.NewInt(100), tracing.BalanceChangeUnspecified)
hooked.CreateContract(addr)
hooked.SelfDestruct(addr)
// sanity-check that balance is now 0
if have, want := hooked.GetBalance(addr), new(big.Int); have.Cmp(want) != 0 {
t.Fatalf("post-destruct balance wrong: have %v want %v", have, want)
}
}
addA := common.Address{0xaa}
addB := common.Address{0xbb}
addC := common.Address{0xcc}
// Tx 1: create and destroy address A and B in one tx
createAndDestroy(addA)
createAndDestroy(addB)
hooked.AddBalance(addA, big.NewInt(200), tracing.BalanceChangeUnspecified)
hooked.AddBalance(addB, big.NewInt(200), tracing.BalanceChangeUnspecified)
hooked.Finalise(true)
// Tx 2: create and destroy address C, then commit
createAndDestroy(addC)
hooked.AddBalance(addC, big.NewInt(200), tracing.BalanceChangeUnspecified)
hooked.Finalise(true)
s.Commit(false)
if have, want := burned, big.NewInt(600); have.Cmp(want) != 0 {
t.Fatalf("burn-count wrong, have %v want %v", have, want)
}
}
// TestHooks is a basic sanity-check of all hooks
func TestHooks(t *testing.T) {
inner, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
inner.SetTxContext(common.Hash{0x11}, 100) // For the log
var result []string
var wants = []string{
"0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)",
"0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)",
"0xaa00000000000000000000000000000000000000.nonce: 0->1337",
"0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)",
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011",
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022",
"log 100",
}
emitF := func(format string, a ...any) {
result = append(result, fmt.Sprintf(format, a...))
}
sdb := NewHookedState(inner, &tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
emitF("%v.balance: %v->%v (%v)", addr, prev, new, reason)
},
OnNonceChange: func(addr common.Address, prev, new uint64) {
emitF("%v.nonce: %v->%v", addr, prev, new)
},
OnCodeChange: func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) {
emitF("%v.code: %#x (%v) ->%#x (%v)", addr, prevCode, prevCodeHash, code, codeHash)
},
OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) {
emitF("%v.storage slot %v: %v ->%v", addr, slot, prev, new)
},
OnLog: func(log *types.Log) {
emitF("log %v", log.TxIndex)
},
})
sdb.AddBalance(common.Address{0xaa}, big.NewInt(100), tracing.BalanceChangeUnspecified)
sdb.SubBalance(common.Address{0xaa}, big.NewInt(50), tracing.BalanceChangeTransfer)
sdb.SetNonce(common.Address{0xaa}, 1337)
sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37})
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11"))
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22"))
sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01"))
sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x02"))
sdb.AddLog(&types.Log{
Address: common.Address{0xbb},
})
for i, want := range wants {
if have := result[i]; have != want {
t.Fatalf("error event %d\nhave: %v\nwant: %v", i, have, want)
}
}
}

View file

@ -155,7 +155,7 @@ func TestCopy(t *testing.T) {
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(big.NewInt(int64(i)), tracing.BalanceChangeUnspecified) obj.AddBalance(big.NewInt(int64(i)))
orig.updateStateObject(obj) orig.updateStateObject(obj)
} }
orig.Finalise(false) orig.Finalise(false)
@ -172,9 +172,9 @@ func TestCopy(t *testing.T) {
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
origObj.AddBalance(big.NewInt(2*int64(i)), tracing.BalanceChangeUnspecified) origObj.AddBalance(big.NewInt(2 * int64(i)))
copyObj.AddBalance(big.NewInt(3*int64(i)), tracing.BalanceChangeUnspecified) copyObj.AddBalance(big.NewInt(3 * int64(i)))
ccopyObj.AddBalance(big.NewInt(4*int64(i)), tracing.BalanceChangeUnspecified) ccopyObj.AddBalance(big.NewInt(4 * int64(i)))
orig.updateStateObject(origObj) orig.updateStateObject(origObj)
copy.updateStateObject(copyObj) copy.updateStateObject(copyObj)
@ -292,6 +292,27 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
s.CreateAccount(addr) s.CreateAccount(addr)
}, },
}, },
{
name: "CreateContract",
fn: func(a testAction, s *StateDB) {
if !s.Exist(addr) {
s.CreateAccount(addr)
}
contractHash := s.GetCodeHash(addr)
emptyCode := contractHash == (common.Hash{}) || contractHash == types.EmptyCodeHash
storageRoot := s.GetStorageRoot(addr)
emptyStorage := storageRoot == (common.Hash{}) || storageRoot == types.EmptyRootHash
if s.GetNonce(addr) == 0 && emptyCode && emptyStorage {
s.CreateContract(addr)
// We also set some code here, to prevent the
// CreateContract action from being performed twice in a row,
// which would cause a difference in state when unrolling
// the journal. (CreateContract assumes created was false prior to
// invocation, and the journal rollback sets it to false).
s.SetCode(addr, []byte{1})
}
},
},
{ {
name: "SelfDestruct", name: "SelfDestruct",
fn: func(a testAction, s *StateDB) { fn: func(a testAction, s *StateDB) {

View file

@ -23,7 +23,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
@ -52,7 +51,7 @@ func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) {
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
acc := &testAccount{address: common.BytesToAddress([]byte{i})} acc := &testAccount{address: common.BytesToAddress([]byte{i})}
obj.AddBalance(big.NewInt(11*int64(i)), tracing.BalanceChangeUnspecified) obj.AddBalance(big.NewInt(11 * int64(i)))
acc.balance = big.NewInt(11 * int64(i)) acc.balance = big.NewInt(11 * int64(i))
obj.SetNonce(uint64(42 * i)) obj.SetNonce(uint64(42 * i))

View file

@ -76,9 +76,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
allLogs []*types.Log allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
) )
var tracingStateDB = vm.StateDB(statedb)
if hooks := cfg.Tracer; hooks != nil {
tracingStateDB = state.NewHookedState(statedb, hooks)
}
// Mutate the block and state according to any hard-fork specs // Mutate the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(tracingStateDB)
} }
if common.TIPSigning.Cmp(blockNumber) == 0 { if common.TIPSigning.Cmp(blockNumber) == 0 {
statedb.DeleteAddress(common.BlockSignersBinary) statedb.DeleteAddress(common.BlockSignersBinary)
@ -87,10 +93,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
InitSignerInTransactions(p.config, header, block.Transactions()) InitSignerInTransactions(p.config, header, block.Transactions())
balanceUpdated := map[common.Address]*big.Int{} balanceUpdated := map[common.Address]*big.Int{}
totalFeeUsed := big.NewInt(0) totalFeeUsed := big.NewInt(0)
// Apply pre-execution system calls.
blockContext := NewEVMBlockContext(header, p.bc, nil) blockContext := NewEVMBlockContext(header, p.bc, nil)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, tracingStateDB, tradingState, p.config, cfg)
signer := types.MakeSigner(p.config, blockNumber) signer := types.MakeSigner(p.config, blockNumber)
coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
// check black-list txs after hf // check black-list txs after hf
@ -144,9 +153,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
} }
} }
statedb.UpdateTRC21Fee(balanceUpdated, totalFeeUsed) tracingStateDB.UpdateTRC21Fee(balanceUpdated, totalFeeUsed)
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, parentState, block.Transactions(), block.Uncles(), receipts) p.engine.Finalize(p.bc, header, tracingStateDB, parentState, block.Transactions(), block.Uncles(), receipts)
return receipts, allLogs, *usedGas, nil return receipts, allLogs, *usedGas, nil
} }
@ -161,9 +172,15 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
allLogs []*types.Log allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
) )
var tracingStateDB = vm.StateDB(statedb)
if hooks := cfg.Tracer; hooks != nil {
tracingStateDB = state.NewHookedState(statedb, hooks)
}
// Mutate the block and state according to any hard-fork specs // Mutate the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(tracingStateDB)
} }
if common.TIPSigning.Cmp(blockNumber) == 0 { if common.TIPSigning.Cmp(blockNumber) == 0 {
statedb.DeleteAddress(common.BlockSignersBinary) statedb.DeleteAddress(common.BlockSignersBinary)
@ -179,10 +196,13 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
if cBlock.stop { if cBlock.stop {
return nil, nil, 0, ErrStopPreparingBlock return nil, nil, 0, ErrStopPreparingBlock
} }
// Apply pre-execution system calls.
blockContext := NewEVMBlockContext(header, p.bc, nil) blockContext := NewEVMBlockContext(header, p.bc, nil)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, tracingStateDB, tradingState, p.config, cfg)
signer := types.MakeSigner(p.config, blockNumber) signer := types.MakeSigner(p.config, blockNumber)
coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
receipts = make([]*types.Receipt, block.Transactions().Len()) receipts = make([]*types.Receipt, block.Transactions().Len())
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
@ -222,6 +242,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
return nil, nil, 0, err return nil, nil, 0, err
} }
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner) receipt, gas, tokenFeeUsed, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, 0, err
@ -238,9 +259,10 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
} }
} }
statedb.UpdateTRC21Fee(balanceUpdated, totalFeeUsed) tracingStateDB.UpdateTRC21Fee(balanceUpdated, totalFeeUsed)
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, parentState, block.Transactions(), block.Uncles(), receipts) p.engine.Finalize(p.bc, header, tracingStateDB, parentState, block.Transactions(), block.Uncles(), receipts)
return receipts, allLogs, *usedGas, nil return receipts, allLogs, *usedGas, nil
} }
@ -250,39 +272,39 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, balanceFee *big.Int, coinbaseOwner common.Address) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) { func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, balanceFee *big.Int, coinbaseOwner common.Address) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) {
// Initialize tracer at the beginning to ensure all transaction types // Initialize tracer at the beginning to ensure all transaction types
// (including non-EVM special transactions) are properly traced. // (including non-EVM special transactions) are properly traced.
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { var tracingStateDB = vm.StateDB(statedb)
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) if hooks := evm.Config.Tracer; hooks != nil {
if evm.Config.Tracer.OnTxEnd != nil { tracingStateDB = state.NewHookedState(statedb, hooks)
defer func() { if hooks.OnTxStart != nil {
evm.Config.Tracer.OnTxEnd(receipt, err) hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
}() }
if hooks.OnTxEnd != nil {
defer func() { hooks.OnTxEnd(receipt, err) }()
} }
} }
to := tx.To() to := tx.To()
if to != nil { if to != nil {
if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) { if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
return ApplySignTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) return ApplySignTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm)
} }
if *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(blockNumber) { if *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(blockNumber) {
return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm)
} }
if *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(blockNumber) { if *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(blockNumber) {
return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm)
} }
} }
if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(blockNumber) { if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(blockNumber) {
return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm)
} }
if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(blockNumber) { if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(blockNumber) {
return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) return ApplyEmptyTransaction(msg, config, statedb, blockNumber, blockHash, tx, usedGas, evm)
} }
// Create a new context to be used in the EVM environment // Create a new context to be used in the EVM environment
txContext := NewEVMTxContext(msg) txContext := NewEVMTxContext(msg)
evm.Reset(txContext, tracingStateDB)
// Update the evm with the new transaction context.
evm.Reset(txContext, statedb)
// Bypass blacklist address // Bypass blacklist address
maxBlockNumber := new(big.Int).SetInt64(9147459) maxBlockNumber := new(big.Int).SetInt64(9147459)
@ -437,7 +459,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
// Update the state with pending changes. // Update the state with pending changes.
var root []byte var root []byte
if config.IsByzantium(blockNumber) { if config.IsByzantium(blockNumber) {
statedb.Finalise(true) tracingStateDB.Finalise(true)
} else { } else {
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
} }
@ -512,7 +534,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, balanceFee, coinbaseOwner) return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, balanceFee, coinbaseOwner)
} }
func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, bool, error) { func ApplySignTransaction(msg *Message, config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) {
// Update the state with pending changes // Update the state with pending changes
var root []byte var root []byte
if config.IsByzantium(blockNumber) { if config.IsByzantium(blockNumber) {
@ -532,8 +554,8 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, bl
} }
statedb.SetNonce(from, nonce+1) statedb.SetNonce(from, nonce+1)
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts. // based on the eip phase, we're passing whether the root touch-delete accounts.
receipt := types.NewReceipt(root, false, *usedGas) receipt = types.NewReceipt(root, false, *usedGas)
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
receipt.GasUsed = 0 receipt.GasUsed = 0
// if the transaction created a contract, store the creation address in the receipt. // if the transaction created a contract, store the creation address in the receipt.
@ -550,7 +572,7 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, bl
return receipt, 0, false, nil return receipt, 0, false, nil
} }
func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, bool, error) { func ApplyEmptyTransaction(msg *Message, config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, gasUsed uint64, tokenFeeUsed bool, err error) {
// Update the state with pending changes // Update the state with pending changes
var root []byte var root []byte
if config.IsByzantium(blockNumber) { if config.IsByzantium(blockNumber) {
@ -559,8 +581,8 @@ func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, b
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
} }
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts. // based on the eip phase, we're passing whether the root touch-delete accounts.
receipt := types.NewReceipt(root, false, *usedGas) receipt = types.NewReceipt(root, false, *usedGas)
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
receipt.GasUsed = 0 receipt.GasUsed = 0
// if the transaction created a contract, store the creation address in the receipt. // if the transaction created a contract, store the creation address in the receipt.

View file

@ -859,7 +859,7 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
balance := evm.StateDB.GetBalance(scope.Contract.Address()) balance := evm.StateDB.GetBalance(scope.Contract.Address())
evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct)
evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
evm.StateDB.Selfdestruct6780(scope.Contract.Address()) evm.StateDB.SelfDestruct6780(scope.Contract.Address())
if tracer := evm.Config.Tracer; tracer != nil { if tracer := evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil { if tracer.OnEnter != nil {
tracer.OnEnter(evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) tracer.OnEnter(evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)

View file

@ -29,8 +29,8 @@ import (
type StateDB interface { type StateDB interface {
CreateAccount(common.Address) CreateAccount(common.Address)
SubBalance(common.Address, *big.Int, tracing.BalanceChangeReason) SubBalance(common.Address, *big.Int, tracing.BalanceChangeReason) *big.Int
AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason) AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason) *big.Int
GetBalance(common.Address) *big.Int GetBalance(common.Address) *big.Int
GetNonce(common.Address) uint64 GetNonce(common.Address) uint64
@ -47,16 +47,21 @@ type StateDB interface {
GetCommittedState(common.Address, common.Hash) common.Hash GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash) SetState(common.Address, common.Hash, common.Hash) common.Hash
GetStorageRoot(addr common.Address) common.Hash GetStorageRoot(addr common.Address) common.Hash
GetTransientState(addr common.Address, key common.Hash) common.Hash GetTransientState(addr common.Address, key common.Hash) common.Hash
SetTransientState(addr common.Address, key, value common.Hash) SetTransientState(addr common.Address, key, value common.Hash)
SelfDestruct(common.Address) SelfDestruct(common.Address) *big.Int
HasSelfDestructed(common.Address) bool HasSelfDestructed(common.Address) bool
Selfdestruct6780(common.Address) // SelfDestruct6780 is post-EIP6780 selfdestruct, which means that it's a
// send-all-to-beneficiary, unless the contract was created in this same
// transaction, in which case it will be destructed.
// This method returns the prior balance, along with a boolean which is
// true iff the object was indeed destructed.
SelfDestruct6780(common.Address) (*big.Int, bool)
// Exist reports whether the given account exists in state. // Exist reports whether the given account exists in state.
// Notably this should also return true for self-destructed accounts. // Notably this should also return true for self-destructed accounts.
@ -80,6 +85,26 @@ type StateDB interface {
AddLog(*types.Log) AddLog(*types.Log)
AddPreimage(common.Hash, []byte) AddPreimage(common.Hash, []byte)
// Finalise must be invoked at the end of a transaction
Finalise(bool)
IntermediateRoot(deleteEmptyObjects bool) common.Hash
UpdateTRC21Fee(newBalance map[common.Address]*big.Int, totalFeeUsed *big.Int)
PutMintedRecordOnsetBlock(value common.Hash)
PutMintedRecordOnsetEpoch(value common.Hash)
GetPostBurned(epoch uint64) common.Hash
PutPostBurned(epoch uint64, value common.Hash)
GetPostMinted(epoch uint64) common.Hash
PutPostMinted(epoch uint64, value common.Hash)
PutPostRewardBlock(epoch uint64, value common.Hash)
IncrementMintedRecordNonce()
} }
// CallContext provides a basic interface for the EVM calling conventions. The EVM // CallContext provides a basic interface for the EVM calling conventions. The EVM

View file

@ -18,6 +18,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/eth/util" "github.com/XinFinOrg/XDPoSChain/eth/util"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
@ -255,7 +256,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
} }
// Hook calculates reward for masternodes // Hook calculates reward for masternodes
adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) {
number := header.Number.Uint64() number := header.Number.Uint64()
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr

View file

@ -17,6 +17,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/eth/util" "github.com/XinFinOrg/XDPoSChain/eth/util"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
@ -265,7 +266,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
} }
// Hook calculates reward for masternodes // Hook calculates reward for masternodes
adaptor.EngineV2.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { adaptor.EngineV2.HookReward = func(chain consensus.ChainReader, stateBlock vm.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) {
number := header.Number.Uint64() number := header.Number.Uint64()
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
if foundationWalletAddr == (common.Address{}) { if foundationWalletAddr == (common.Address{}) {

View file

@ -881,7 +881,6 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
} }
// The actual TxContext will be created as part of ApplyTransactionWithEVM. // The actual TxContext will be created as part of ApplyTransactionWithEVM.
vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice}, statedb, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice}, statedb, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
statedb.SetLogger(tracer.Hooks)
// Define a meaningful timeout of a single transaction trace // Define a meaningful timeout of a single transaction trace
if config.Timeout != nil { if config.Timeout != nil {

View file

@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
@ -126,20 +127,22 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
GasLimit: uint64(test.Context.GasLimit), GasLimit: uint64(test.Context.GasLimit),
BaseFee: test.Genesis.BaseFee, BaseFee: test.Genesis.BaseFee,
} }
state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) st = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
) )
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
logState := vm.StateDB(st)
state.SetLogger(tracer.Hooks) if tracer.Hooks != nil {
logState = state.NewHookedState(st, tracer.Hooks)
}
msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee) msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee)
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, core.NewEVMTxContext(msg), logState, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{}) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{})
if err != nil { if err != nil {
@ -348,7 +351,7 @@ func TestInternals(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
state := tests.MakePreState(rawdb.NewMemoryDatabase(), st := tests.MakePreState(rawdb.NewMemoryDatabase(),
types.GenesisAlloc{ types.GenesisAlloc{
to: types.Account{ to: types.Account{
Code: tc.code, Code: tc.code,
@ -357,7 +360,12 @@ func TestInternals(t *testing.T) {
Balance: big.NewInt(500000000000000), Balance: big.NewInt(500000000000000),
}, },
}) })
state.SetLogger(tc.tracer.Hooks)
logState := vm.StateDB(st)
if hooks := tc.tracer.Hooks; hooks != nil {
logState = state.NewHookedState(st, hooks)
}
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
To: &to, To: &to,
Value: big.NewInt(0), Value: big.NewInt(0),
@ -371,7 +379,7 @@ func TestInternals(t *testing.T) {
Origin: origin, Origin: origin,
GasPrice: tx.GasPrice(), GasPrice: tx.GasPrice(),
} }
evm := vm.NewEVM(context, txContext, state, nil, config, vm.Config{Tracer: tc.tracer.Hooks}) evm := vm.NewEVM(context, txContext, logState, nil, config, vm.Config{Tracer: tc.tracer.Hooks})
msg, err := core.TransactionToMessage(tx, signer, nil, nil, big.NewInt(0)) msg, err := core.TransactionToMessage(tx, signer, nil, nil, big.NewInt(0))
if err != nil { if err != nil {
t.Fatalf("test %v: failed to create message: %v", tc.name, err) t.Fatalf("test %v: failed to create message: %v", tc.name, err)

View file

@ -101,7 +101,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
return fmt.Errorf("failed to create call tracer: %v", err) return fmt.Errorf("failed to create call tracer: %v", err)
} }
state.SetLogger(tracer.Hooks)
msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee) msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
if err != nil { if err != nil {
return fmt.Errorf("failed to prepare transaction for tracing: %v", err) return fmt.Errorf("failed to prepare transaction for tracing: %v", err)

View file

@ -109,18 +109,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
// msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
// if err != nil {
// t.Fatalf("failed to prepare transaction for tracing: %v", err)
// }
// evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
// st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
// if _, err = st.TransitionDb(common.Address{}); err != nil {
// t.Fatalf("failed to execute transaction: %v", err)
// }
// // Retrieve the trace result and compare against the expected.
state.SetLogger(tracer.Hooks)
msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee) msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)

View file

@ -48,9 +48,11 @@ type dummyStatedb struct {
state.StateDB state.StateDB
} }
func (*dummyStatedb) GetRefund() uint64 { return 1337 } func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} }
func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) common.Hash {
return common.Hash{}
}
func TestStoreCapture(t *testing.T) { func TestStoreCapture(t *testing.T) {
var ( var (

View file

@ -1292,10 +1292,16 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
evm.SetPrecompiles(precompiles) evm.SetPrecompiles(precompiles)
} }
return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp) res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp)
// If an internal state error occurred, let that have precedence. Otherwise,
// a "trie root missing" type of error will masquerade as e.g. "insufficient gas"
if err := state.Error(); err != nil {
return nil, err
}
return res, err
} }
func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) {
// Wait for the context to be done and cancel the evm. Even if the // Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly) // EVM has finished, cancelling may be done (repeatedly)
go func() { go func() {
@ -1305,9 +1311,6 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, st
// Execute the message. // Execute the message.
result, err := core.ApplyMessage(evm, msg, gp, common.Address{}) result, err := core.ApplyMessage(evm, msg, gp, common.Address{})
if err := state.Error(); err != nil {
return nil, err
}
// If the timer caused an abort, return an appropriate error message // If the timer caused an abort, return an appropriate error message
if evm.Cancelled() { if evm.Cancelled() {

View file

@ -177,7 +177,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
} }
evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, nil, sim.chainConfig, *vmConfig) evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, nil, sim.chainConfig, *vmConfig)
) )
sim.state.SetLogger(tracer.Hooks()) var tracingStateDB = vm.StateDB(sim.state)
if hooks := tracer.Hooks(); hooks != nil {
tracingStateDB = state.NewHookedState(sim.state, hooks)
}
// It is possible to override precompiles with EVM bytecode, or // It is possible to override precompiles with EVM bytecode, or
// move them to another address. // move them to another address.
if precompiles != nil { if precompiles != nil {
@ -195,8 +198,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
tracer.reset(tx.Hash(), uint(i)) tracer.reset(tx.Hash(), uint(i))
// EoA check is always skipped, even in validation mode. // EoA check is always skipped, even in validation mode.
msg := call.ToMessage(sim.b, header.BaseFee, !sim.validate, true) msg := call.ToMessage(sim.b, header.BaseFee, !sim.validate, true)
evm.Reset(core.NewEVMTxContext(msg), sim.state) evm.Reset(core.NewEVMTxContext(msg), tracingStateDB)
result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, sim.gp) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)
if err != nil { if err != nil {
txErr := txValidationError(err) txErr := txValidationError(err)
return nil, nil, txErr return nil, nil, txErr
@ -204,7 +207,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
// Update the state with pending changes. // Update the state with pending changes.
var root []byte var root []byte
if sim.chainConfig.IsByzantium(blockContext.BlockNumber) { if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
sim.state.Finalise(true) tracingStateDB.Finalise(true)
} else { } else {
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
} }