diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 12e6ef9f26..994b744df3 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -369,10 +369,10 @@ func (beacon *Beacon) Delay(_ consensus.ChainReader, _ *types.Header, _ *time.Du } // Finalize implements consensus.Engine, setting the final state on the header -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, txs *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) error { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, txs *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64, tracer *tracing.Hooks) error { // Finalize is different with Prepare, it can be used in both block verification. if !beacon.IsPoSHeader(header) { - beacon.ethone.Finalize(chain, header, state, txs, uncles, nil, nil, nil, nil) + beacon.ethone.Finalize(chain, header, state, txs, uncles, nil, nil, nil, nil, tracer) return nil } // Withdrawals processing. @@ -388,10 +388,10 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, setting the final state and // assembling the block. -func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { +func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { // FinalizeAndAssemble is different with Prepare, it can be used in both block generation. if !beacon.IsPoSHeader(header) { - return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts) + return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts, tracer) } shanghai := chain.Config().IsShanghai(header.Number, header.Time) if shanghai { @@ -405,7 +405,7 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea } } // Finalize and assemble the block. - beacon.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil) + beacon.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil, tracer) // Assign the final state root to header. header.Root = state.IntermediateRoot(true) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 9bd36687b7..4591642305 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -592,19 +593,19 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, - _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) { + _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64, tracer *tracing.Hooks) (err error) { // No block rewards in PoA, so the state remains as is return } // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. -func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { +func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { if len(body.Withdrawals) > 0 { return nil, nil, errors.New("clique does not support withdrawals") } // Finalize block - c.Finalize(chain, header, state, &body.Transactions, body.Uncles, nil, nil, nil, nil) + c.Finalize(chain, header, state, &body.Transactions, body.Uncles, nil, nil, nil, nil, tracer) // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) diff --git a/consensus/consensus.go b/consensus/consensus.go index 54aef4e82b..9da6f6df1a 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" @@ -117,14 +118,14 @@ type Engine interface { // Note: The state database might be updated to reflect any consensus rules // that happen at finalization (e.g. block rewards). Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, txs *[]*types.Transaction, - uncles []*types.Header, withdrawals []*types.Withdrawal, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error + uncles []*types.Header, withdrawals []*types.Withdrawal, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64, tracer *tracing.Hooks) error // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards or process withdrawals) and assembles the final block. // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) + FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 8a62152ddd..de28a1d451 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -515,7 +515,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H // Finalize implements consensus.Engine, accumulating the block and uncle rewards, // setting the final state on the header func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, - _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) { + _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64, tracer *tracing.Hooks) (err error) { // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(chain.Config(), state, header, uncles) return @@ -523,12 +523,12 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, accumulating the block and // uncle rewards, setting the final state and assembling the block. -func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { +func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { if len(body.Withdrawals) > 0 { return nil, nil, errors.New("ethash does not support withdrawals") } // Finalize block - ethash.Finalize(chain, header, state, &body.Transactions, body.Uncles, nil, nil, nil, nil) + ethash.Finalize(chain, header, state, &body.Transactions, body.Uncles, nil, nil, nil, nil, tracer) // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) diff --git a/consensus/parlia/feynmanfork.go b/consensus/parlia/feynmanfork.go index 72d122ebc8..8a824f74e0 100644 --- a/consensus/parlia/feynmanfork.go +++ b/consensus/parlia/feynmanfork.go @@ -30,7 +30,7 @@ func isBreatheBlock(lastBlockTime, blockTime uint64) bool { // initializeFeynmanContract initialize new contracts of Feynman fork func (p *Parlia) initializeFeynmanContract(state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config, ) error { // method method := "initialize" @@ -53,7 +53,7 @@ func (p *Parlia) initializeFeynmanContract(state vm.StateDB, header *types.Heade msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(c), data, common.Big0) // apply message log.Info("initialize feynman contract", "block number", header.Number.Uint64(), "contract", c) - err = p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + err = p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) if err != nil { return err } @@ -96,7 +96,7 @@ func (h *ValidatorHeap) Pop() interface{} { } func (p *Parlia) updateValidatorSetV2(state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config, ) error { // 1. get all validators and its voting power blockNr := rpc.BlockNumberOrHashWithHash(header.ParentHash, false) @@ -123,7 +123,7 @@ func (p *Parlia) updateValidatorSetV2(state vm.StateDB, header *types.Header, ch // get system message msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.ValidatorContract), data, common.Big0) // apply message - return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) } func (p *Parlia) getValidatorElectionInfo(blockNr rpc.BlockNumberOrHash) ([]ValidatorItem, error) { diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 0800bbad22..58aaddc9c6 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1186,7 +1186,7 @@ func (p *Parlia) verifyTurnLength(chain consensus.ChainHeaderReader, header *typ func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, state vm.StateDB, header *types.Header, cx core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, - usedGas *uint64, mining bool) error { + usedGas *uint64, mining bool, vmConfig vm.Config) error { currentHeight := header.Number.Uint64() epoch := p.config.Epoch chainConfig := chain.Config() @@ -1255,13 +1255,13 @@ func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, sta return err } msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.ValidatorContract), data, common.Big0) - return p.applyTransaction(msg, state, header, cx, txs, receipts, systemTxs, usedGas, mining) + return p.applyTransaction(msg, state, header, cx, txs, receipts, systemTxs, usedGas, mining, vmConfig) } // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, txs *[]*types.Transaction, - uncles []*types.Header, _ []*types.Withdrawal, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error { + uncles []*types.Header, _ []*types.Withdrawal, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64, tracer *tracing.Hooks) error { // warn if not in majority fork number := header.Number.Uint64() snap, err := p.snapshot(chain, number-1, header.ParentHash, nil) @@ -1283,6 +1283,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade } cx := chainContext{Chain: chain, parlia: p} + vmConfig := vm.Config{Tracer: tracer} parent := chain.GetHeaderByHash(header.ParentHash) if parent == nil { @@ -1292,7 +1293,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade systemcontracts.TryUpdateBuildInSystemContract(p.chainConfig, header.Number, parent.Time, header.Time, state, false) if p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) { - err := p.initializeFeynmanContract(state, header, cx, txs, receipts, systemTxs, usedGas, false) + err := p.initializeFeynmanContract(state, header, cx, txs, receipts, systemTxs, usedGas, false, vmConfig) if err != nil { log.Error("init feynman contract failed", "error", err) } @@ -1300,7 +1301,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // No block rewards in PoA, so the state remains as is and uncles are dropped if header.Number.Cmp(common.Big1) == 0 { - err := p.initContract(state, header, cx, txs, receipts, systemTxs, usedGas, false) + err := p.initContract(state, header, cx, txs, receipts, systemTxs, usedGas, false, vmConfig) if err != nil { log.Error("init contract failed") } @@ -1321,7 +1322,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade if !signedRecently { log.Trace("slash validator", "block hash", header.Hash(), "address", spoiledVal) - err = p.slash(spoiledVal, state, header, cx, txs, receipts, systemTxs, usedGas, false) + err = p.slash(spoiledVal, state, header, cx, txs, receipts, systemTxs, usedGas, false, vmConfig) if err != nil { // it is possible that slash validator failed because of the slash channel is disabled. log.Error("slash validator failed", "block hash", header.Hash(), "address", spoiledVal, "err", err) @@ -1329,13 +1330,13 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade } } val := header.Coinbase - err = p.distributeIncoming(val, state, header, cx, txs, receipts, systemTxs, usedGas, false) + err = p.distributeIncoming(val, state, header, cx, txs, receipts, systemTxs, usedGas, false, vmConfig) if err != nil { return err } if p.chainConfig.IsPlato(header.Number) { - if err := p.distributeFinalityReward(chain, state, header, cx, txs, receipts, systemTxs, usedGas, false); err != nil { + if err := p.distributeFinalityReward(chain, state, header, cx, txs, receipts, systemTxs, usedGas, false, vmConfig); err != nil { return err } } @@ -1344,7 +1345,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade if p.chainConfig.IsFeynman(header.Number, header.Time) && isBreatheBlock(parent.Time, header.Time) { // we should avoid update validators in the Feynman upgrade block if !p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) { - if err := p.updateValidatorSetV2(state, header, cx, txs, receipts, systemTxs, usedGas, false); err != nil { + if err := p.updateValidatorSetV2(state, header, cx, txs, receipts, systemTxs, usedGas, false, vmConfig); err != nil { return err } } @@ -1359,9 +1360,11 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, - body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { + body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { // No block rewards in PoA, so the state remains as is and uncles are dropped cx := chainContext{Chain: chain, parlia: p} + vmConfig := vm.Config{Tracer: tracer} + if body.Transactions == nil { body.Transactions = make([]*types.Transaction, 0) } @@ -1377,14 +1380,14 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * systemcontracts.TryUpdateBuildInSystemContract(p.chainConfig, header.Number, parent.Time, header.Time, state, false) if p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) { - err := p.initializeFeynmanContract(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true) + err := p.initializeFeynmanContract(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, vmConfig) if err != nil { log.Error("init feynman contract failed", "error", err) } } if header.Number.Cmp(common.Big1) == 0 { - err := p.initContract(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true) + err := p.initContract(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, vmConfig) if err != nil { log.Error("init contract failed") } @@ -1408,7 +1411,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * } } if !signedRecently { - err = p.slash(spoiledVal, state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true) + err = p.slash(spoiledVal, state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, vmConfig) if err != nil { // it is possible that slash validator failed because of the slash channel is disabled. log.Error("slash validator failed", "block hash", header.Hash(), "address", spoiledVal) @@ -1416,13 +1419,13 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * } } - err := p.distributeIncoming(p.val, state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true) + err := p.distributeIncoming(p.val, state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, vmConfig) if err != nil { return nil, nil, err } if p.chainConfig.IsPlato(header.Number) { - if err := p.distributeFinalityReward(chain, state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true); err != nil { + if err := p.distributeFinalityReward(chain, state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, vmConfig); err != nil { return nil, nil, err } } @@ -1431,7 +1434,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * if p.chainConfig.IsFeynman(header.Number, header.Time) && isBreatheBlock(parent.Time, header.Time) { // we should avoid update validators in the Feynman upgrade block if !p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) { - if err := p.updateValidatorSetV2(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true); err != nil { + if err := p.updateValidatorSetV2(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, vmConfig); err != nil { return nil, nil, err } } @@ -1814,7 +1817,7 @@ func (p *Parlia) getCurrentValidators(blockHash common.Hash, blockNum *big.Int) // distributeIncoming distributes system incoming of the block func (p *Parlia) distributeIncoming(val common.Address, state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool) error { + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config) error { coinbase := header.Coinbase doDistributeSysReward := !p.chainConfig.IsKepler(header.Number, header.Time) && @@ -1826,7 +1829,7 @@ func (p *Parlia) distributeIncoming(val common.Address, state vm.StateDB, header if rewards.Cmp(common.U2560) > 0 { state.SetBalance(consensus.SystemAddress, balance.Sub(balance, rewards), tracing.BalanceChangeUnspecified) state.AddBalance(coinbase, rewards, tracing.BalanceChangeUnspecified) - err := p.distributeToSystem(rewards.ToBig(), state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + err := p.distributeToSystem(rewards.ToBig(), state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) if err != nil { return err } @@ -1838,15 +1841,16 @@ func (p *Parlia) distributeIncoming(val common.Address, state vm.StateDB, header if balance.Cmp(common.U2560) <= 0 { return nil } - state.SetBalance(consensus.SystemAddress, common.U2560, tracing.BalanceChangeUnspecified) - state.AddBalance(coinbase, balance, tracing.BalanceChangeUnspecified) + + state.SetBalance(consensus.SystemAddress, common.U2560, tracing.BalanceDecreaseBSCDistributeReward) + state.AddBalance(coinbase, balance, tracing.BalanceIncreaseBSCDistributeReward) log.Trace("distribute to validator contract", "block hash", header.Hash(), "amount", balance) - return p.distributeToValidator(balance.ToBig(), val, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + return p.distributeToValidator(balance.ToBig(), val, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) } // slash spoiled validators func (p *Parlia) slash(spoiledVal common.Address, state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool) error { + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config) error { // method method := "slash" @@ -1861,12 +1865,12 @@ func (p *Parlia) slash(spoiledVal common.Address, state vm.StateDB, header *type // get system message msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.SlashContract), data, common.Big0) // apply message - return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) } // init contract func (p *Parlia) initContract(state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool) error { + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config) error { // method method := "init" // contracts @@ -1889,7 +1893,7 @@ func (p *Parlia) initContract(state vm.StateDB, header *types.Header, chain core msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(c), data, common.Big0) // apply message log.Trace("init contract", "block hash", header.Hash(), "contract", c) - err = p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + err = p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) if err != nil { return err } @@ -1898,17 +1902,17 @@ func (p *Parlia) initContract(state vm.StateDB, header *types.Header, chain core } func (p *Parlia) distributeToSystem(amount *big.Int, state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool) error { + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config) error { // get system message msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.SystemRewardContract), nil, amount) // apply message - return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) } // distributeToValidator deposits validator reward to validator contract func (p *Parlia) distributeToValidator(amount *big.Int, validator common.Address, state vm.StateDB, header *types.Header, chain core.ChainContext, - txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool) error { + txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, vmConfig vm.Config) error { // method method := "deposit" @@ -1923,7 +1927,7 @@ func (p *Parlia) distributeToValidator(amount *big.Int, validator common.Address // get system message msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.ValidatorContract), data, amount) // apply message - return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) + return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining, vmConfig) } // get system message @@ -1945,12 +1949,14 @@ func (p *Parlia) applyTransaction( chainContext core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, -) (err error) { + vmConfig vm.Config, +) (applyErr error) { nonce := state.GetNonce(msg.From) expectedTx := types.NewTransaction(nonce, *msg.To, msg.Value, msg.GasLimit, msg.GasPrice, msg.Data) expectedHash := p.signer.Hash(expectedTx) if msg.From == p.val && mining { + var err error expectedTx, err = p.signTxFn(accounts.Account{Address: msg.From}, expectedTx, p.chainConfig.ChainID) if err != nil { return err @@ -1975,7 +1981,39 @@ func (p *Parlia) applyTransaction( *receivedTxs = (*receivedTxs)[1:] } state.SetTxContext(expectedTx.Hash(), len(*txs)) - gasUsed, err := applyMessage(msg, state, header, p.chainConfig, chainContext) + + // Create a new context to be used in the EVM environment + context := core.NewEVMBlockContext(header, chainContext, nil) + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + evm := vm.NewEVM(context, state, p.chainConfig, vmConfig) + evm.SetTxContext(core.NewEVMTxContext(msg)) + + // Tracing receipt will be set if there is no error and will be used to trace the transaction + var tracingReceipt *types.Receipt + if tracer := vmConfig.Tracer; tracer != nil { + if vmConfig.Tracer.OnSystemTxStart != nil { + vmConfig.Tracer.OnSystemTxStart() + } + if tracer.OnTxStart != nil { + tracer.OnTxStart(evm.GetVMContext(), expectedTx, msg.From) + } + + // Defers are last in first out, so OnTxEnd will run before OnSystemTxEnd in this transaction, + // which is what we want. + if vmConfig.Tracer.OnSystemTxEnd != nil { + defer func() { + vmConfig.Tracer.OnSystemTxEnd() + }() + } + if tracer.OnTxEnd != nil { + defer func() { + tracer.OnTxEnd(tracingReceipt, applyErr) + }() + } + } + + gasUsed, err := applyMessage(msg, evm, state, header, p.chainConfig, chainContext) if err != nil { return err } @@ -1987,17 +2025,17 @@ func (p *Parlia) applyTransaction( root = state.IntermediateRoot(p.chainConfig.IsEIP158(header.Number)).Bytes() } *usedGas += gasUsed - receipt := types.NewReceipt(root, false, *usedGas) - receipt.TxHash = expectedTx.Hash() - receipt.GasUsed = gasUsed + tracingReceipt = types.NewReceipt(root, false, *usedGas) + tracingReceipt.TxHash = expectedTx.Hash() + tracingReceipt.GasUsed = gasUsed // Set the receipt logs and create a bloom for filtering - receipt.Logs = state.GetLogs(expectedTx.Hash(), header.Number.Uint64(), header.Hash()) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - receipt.BlockHash = header.Hash() - receipt.BlockNumber = header.Number - receipt.TransactionIndex = uint(state.TxIndex()) - *receipts = append(*receipts, receipt) + tracingReceipt.Logs = state.GetLogs(expectedTx.Hash(), header.Number.Uint64(), header.Hash()) + tracingReceipt.Bloom = types.CreateBloom(types.Receipts{tracingReceipt}) + tracingReceipt.BlockHash = header.Hash() + tracingReceipt.BlockNumber = header.Number + tracingReceipt.TransactionIndex = uint(state.TxIndex()) + *receipts = append(*receipts, tracingReceipt) return nil } @@ -2140,17 +2178,12 @@ func (c chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { // apply message func applyMessage( msg *core.Message, + evm *vm.EVM, state vm.StateDB, header *types.Header, chainConfig *params.ChainConfig, chainContext core.ChainContext, ) (uint64, error) { - // Create a new context to be used in the EVM environment - context := core.NewEVMBlockContext(header, chainContext, nil) - // Create a new environment which holds all relevant information - // about the transaction and calling mechanisms. - evm := vm.NewEVM(context, state, chainConfig, vm.Config{}) - evm.SetTxContext(core.NewEVMTxContext(msg)) // Apply the transaction to the current state (included in the env) if chainConfig.IsCancun(header.Number, header.Time) { rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) diff --git a/consensus/parlia/parlia_test.go b/consensus/parlia/parlia_test.go index afd30b86e8..8b6309e903 100644 --- a/consensus/parlia/parlia_test.go +++ b/consensus/parlia/parlia_test.go @@ -1,17 +1,34 @@ package parlia import ( + "crypto/ecdsa" "crypto/rand" "fmt" + "math/big" mrand "math/rand" + "slices" + "strings" "testing" - "golang.org/x/crypto/sha3" - "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/systemcontracts" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/triedb" + "github.com/holiman/uint256" + "golang.org/x/crypto/sha3" ) const ( @@ -615,3 +632,231 @@ func TestSimulateP2P(t *testing.T) { } } } + +var ( + // testKey is a private key to use for funding a tester account. + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) +) + +func TestParlia_applyTransactionTracing(t *testing.T) { + frdir := t.TempDir() + db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false) + if err != nil { + t.Fatalf("failed to create database with ancient backend") + } + + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + config := params.ParliaTestChainConfig + gspec := &core.Genesis{ + Config: params.ParliaTestChainConfig, + Alloc: types.GenesisAlloc{testAddr: {Balance: new(big.Int).SetUint64(10 * params.Ether)}}, + } + + mockEngine := &mockParlia{} + genesisBlock := gspec.MustCommit(db, trieDB) + + chain, _ := core.NewBlockChain(db, nil, gspec, nil, mockEngine, vm.Config{}, nil, nil) + signer := types.LatestSigner(config) + + bs, _ := core.GenerateChain(config, genesisBlock, mockEngine, db, 1, func(i int, gen *core.BlockGen) { + if !config.IsCancun(gen.Number(), gen.Timestamp()) { + tx, _ := makeMockTx(config, signer, testKey, gen.TxNonce(testAddr), gen.BaseFee().Uint64(), eip4844.CalcBlobFee(gen.ExcessBlobGas()).Uint64(), false) + gen.AddTxWithChain(chain, tx) + return + } + tx, sidecar := makeMockTx(config, signer, testKey, gen.TxNonce(testAddr), gen.BaseFee().Uint64(), eip4844.CalcBlobFee(gen.ExcessBlobGas()).Uint64(), true) + gen.AddTxWithChain(chain, tx) + gen.AddBlobSidecar(&types.BlobSidecar{ + BlobTxSidecar: *sidecar, + TxIndex: 0, + TxHash: tx.Hash(), + }) + }) + + engine := New(params.ParliaTestChainConfig, db, nil, genesisBlock.Hash()) + + stateDatabase := state.NewDatabase(trieDB, nil) + stateDB, err := state.New(genesisBlock.Root(), stateDatabase) + if err != nil { + t.Fatalf("failed to create stateDB: %v", err) + } + + method := "distributeFinalityReward" + data, err := engine.validatorSetABI.Pack(method, make([]common.Address, 0), make([]*big.Int, 0)) + if err != nil { + t.Fatalf("failed to pack system contract method %s: %v", method, err) + } + + msg := engine.getSystemMessage(genesisBlock.Coinbase(), common.HexToAddress(systemcontracts.ValidatorContract), data, common.Big0) + nonce := stateDB.GetNonce(msg.From) + expectedTx := types.NewTransaction(nonce, *msg.To, msg.Value, msg.GasLimit, msg.GasPrice, msg.Data) + + receivedTxs := []*types.Transaction{expectedTx} + txs := make([]*types.Transaction, 0, 1) + receipts := make([]*types.Receipt, 0, 1) + usedGas := uint64(0) + + recording := &recordingTracer{} + hooks := recording.hooks() + + cx := chainContext{Chain: chain, parlia: engine} + applyErr := engine.applyTransaction(msg, state.NewHookedState(stateDB, hooks), bs[0].Header(), cx, &txs, &receipts, &receivedTxs, &usedGas, false, vm.Config{ + Tracer: hooks, + }) + if applyErr != nil { + t.Fatalf("failed to apply system contract transaction: %v", applyErr) + } + + expectedRecords := []string{ + "system tx start", + "tx [0xe9a5597c7f5a6a10a18959d262319fbf19cecb4d9d1ce8f2c990089bd88016fc] from [0x0000000000000000000000000000000000000000] start", + "nonce change [0x0000000000000000000000000000000000000000]: 0 -> 1", + "call enter [0x0000000000000000000000000000000000000000] -> [0x0000000000000000000000000000000000001000] (type 241, gas 9223372036854775807, value 0)", + "call exit (depth 0, gas used 0, reverted false, err: )", + "tx [0xe9a5597c7f5a6a10a18959d262319fbf19cecb4d9d1ce8f2c990089bd88016fc] end (log count 0, cumulative gas used 0, err: )", + "system tx end", + } + + if !slices.Equal(recording.records, expectedRecords) { + t.Errorf("expected \n%s\n\ngot\n\n%s", formatRecords(recording.records), formatRecords(expectedRecords)) + } +} + +func formatRecords(records []string) string { + indented := make([]string, 0, len(records)) + for _, record := range records { + indented = append(indented, fmt.Sprintf(" %q,", record)) + } + + return "[\n" + strings.Join(indented, "\n") + "\n]" +} + +type errorView struct { + err error +} + +func (e errorView) String() string { + if e.err == nil { + return "" + } + + return e.err.Error() +} + +type recordingTracer struct { + records []string +} + +func (t *recordingTracer) record(format string, args ...any) { + t.records = append(t.records, fmt.Sprintf(format, args...)) +} + +func (t *recordingTracer) hooks() *tracing.Hooks { + return &tracing.Hooks{ + OnSystemTxStart: func() { t.record("system tx start") }, + OnTxStart: func(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { + t.record("tx [%s] from [%s] start", tx.Hash(), from) + }, + OnTxEnd: func(receipt *types.Receipt, err error) { + t.record("tx [%s] end (log count %d, cumulative gas used %d, err: %s)", receipt.TxHash, len(receipt.Logs), receipt.CumulativeGasUsed, errorView{err}) + }, + OnSystemTxEnd: func() { t.record("system tx end") }, + OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + t.record("call enter [%s] -> [%s] (type %d, gas %d, value %s)", from, to, typ, gas, value) + }, + OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + t.record("call exit (depth %d, gas used %d, reverted %v, err: %s)", depth, gasUsed, reverted, errorView{err}) + }, + OnNonceChange: func(addr common.Address, prev, new uint64) { + t.record("nonce change [%s]: %d -> %d", addr, prev, new) + }, + } +} + +var ( + emptyBlob = kzg4844.Blob{} + emptyBlobCommit, _ = kzg4844.BlobToCommitment(&emptyBlob) + emptyBlobProof, _ = kzg4844.ComputeBlobProof(&emptyBlob, emptyBlobCommit) +) + +func makeMockTx(config *params.ChainConfig, signer types.Signer, key *ecdsa.PrivateKey, nonce uint64, baseFee uint64, blobBaseFee uint64, isBlobTx bool) (*types.Transaction, *types.BlobTxSidecar) { + if !isBlobTx { + raw := &types.DynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + GasTipCap: big.NewInt(10), + GasFeeCap: new(big.Int).SetUint64(baseFee + 10), + Gas: params.TxGas, + To: &common.Address{0x00}, + Value: big.NewInt(0), + } + tx, _ := types.SignTx(types.NewTx(raw), signer, key) + return tx, nil + } + sidecar := &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob, emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof}, + } + raw := &types.BlobTx{ + ChainID: uint256.MustFromBig(config.ChainID), + Nonce: nonce, + GasTipCap: uint256.NewInt(10), + GasFeeCap: uint256.NewInt(baseFee + 10), + Gas: params.TxGas, + To: common.Address{0x00}, + Value: uint256.NewInt(0), + BlobFeeCap: uint256.NewInt(blobBaseFee), + BlobHashes: sidecar.BlobHashes(), + } + tx, _ := types.SignTx(types.NewTx(raw), signer, key) + return tx, sidecar +} + +type mockParlia struct { + consensus.Engine +} + +func (c *mockParlia) Author(header *types.Header) (common.Address, error) { + return header.Coinbase, nil +} + +func (c *mockParlia) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + return nil +} + +func (c *mockParlia) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { + return nil +} + +func (c *mockParlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) { + abort := make(chan<- struct{}) + results := make(chan error, len(headers)) + for i := 0; i < len(headers); i++ { + results <- nil + } + return abort, results +} + +func (c *mockParlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, + _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64, tracer *tracing.Hooks) (err error) { + return +} + +func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { + // Finalize block + c.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil, tracer) + + // Assign the final state root to header. + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + + // Header seems complete, assemble into a block and return + return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), receipts, nil +} + +func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { + return big.NewInt(1) +} diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8a75f7f437..e7e90b3ebe 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -40,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/program" @@ -4212,13 +4213,13 @@ func (c *mockParlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers [] } func (c *mockParlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, - _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) { + _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64, tracer *tracing.Hooks) (err error) { return } -func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { +func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { // Finalize block - c.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil) + c.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil, tracer) // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) diff --git a/core/chain_makers.go b/core/chain_makers.go index 4059ed5fa5..9e6ff779f1 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -396,7 +396,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals} - block, _, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts) + block, _, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts, nil) if err != nil { panic(err) } @@ -508,7 +508,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine Uncles: b.uncles, Withdrawals: b.withdrawals, } - block, _, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts) + block, _, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts, nil) if err != nil { panic(err) } diff --git a/core/state_processor.go b/core/state_processor.go index cf5edc41a3..bbc4baa4c4 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -167,7 +167,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - err = p.chain.engine.Finalize(p.chain, header, tracingStateDB, &commonTxs, block.Uncles(), block.Withdrawals(), &receipts, &systemTxs, usedGas) + err = p.chain.engine.Finalize(p.chain, header, tracingStateDB, &commonTxs, block.Uncles(), block.Withdrawals(), &receipts, &systemTxs, usedGas, cfg.Tracer) if err != nil { return nil, err } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 18c0b66a89..c0ada6d5ac 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -154,9 +154,41 @@ type ( // beacon block root. OnSystemCallEndHook = func() - // OnSystemTxEndHook is called when tracing a system transaction, which does not calculate intrinsic gas during execution. + // OnSystemTxFixIntrinsicGasHook is called when tracing a system transaction, which does not calculate intrinsic gas during execution. // this hook will subtract intrinsic gas from the total gas used. - OnSystemTxEndHook = func(uint64) + OnSystemTxFixIntrinsicGasHook = func(uint64) + + // OnSystemTxStartHook is called when a system transaction is about to be executed within the Parlia consensus + // engine, like before upgrading system contracts, distribution of rewards, and other "chain" related transactions. + // + // This will be called in addition to the `OnTxStart` hook so the flow of event you will receive in your tracer will + // look kike this: + // + // - OnSystemTxStart + // - OnTxStart + // - OnTxEnd + // - OnSystemTxEnd + // + // This event flow enables transactions to be traced correctly with just OnTxStart/End being set but also + // enables special routing of those transactions by having OnSystemTxStart/End defined and keeping a special + // system state to do something different when OnTxEnd is called. + OnSystemTxStartHook func() + + // OnSystemTxEnd is called when a system transaction is about to completed its execution within the Parlia consensus + // engine, like after upgrading system contracts, distribution of rewards, and other "chain" related transactions. + // + // This will be called in addition to the `OnTxStart` hook so the flow of event you will receive in your tracer will + // look kike this: + // + // - OnSystemTxStart + // - OnTxStart + // - OnTxEnd + // - OnSystemTxEnd + // + // This event flow enables transactions to be traced correctly with just OnTxStart/End being set but also + // enables special routing of those transactions by having OnSystemTxStart/End defined and keeping a special + // system state to do something different when OnTxEnd is called. + OnSystemTxEndHook func() /* - State events - @@ -198,7 +230,9 @@ type Hooks struct { OnSystemCallStartV2 OnSystemCallStartHookV2 OnSystemCallEnd OnSystemCallEndHook - OnSystemTxEnd OnSystemTxEndHook + OnSystemTxStart OnSystemTxStartHook + OnSystemTxEnd OnSystemTxEndHook + OnSystemTxFixIntrinsicGas OnSystemTxFixIntrinsicGasHook // State events OnBalanceChange BalanceChangeHook @@ -257,6 +291,15 @@ const ( // account within the same tx (captured at end of tx). // Note it doesn't account for a self-destruct which appoints itself as recipient. BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 + + // BSC specific balance changes + + // BalanceDecreaseBSCDistributeReward is a balance change that decreases system address' balance and happens + // when BSC is distributing rewards to validator. + BalanceDecreaseBSCDistributeReward BalanceChangeReason = 210 + // BalanceIncreaseBSCDistributeReward is a balance change that increases the block validator's balance and + // happens when BSC is distributing rewards to validator. + BalanceIncreaseBSCDistributeReward BalanceChangeReason = 211 ) // GasChangeReason is used to indicate the reason for a gas change, useful diff --git a/eth/handler_test.go b/eth/handler_test.go index 8b65c3ce78..dca53b50c9 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/trie" @@ -238,13 +239,13 @@ func (c *mockParlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers [] } func (c *mockParlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal, - _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) { + _ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64, tracer *tracing.Hooks) (err error) { return } -func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { +func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, tracer *tracing.Hooks) (*types.Block, []*types.Receipt, error) { // Finalize block - c.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil) + c.Finalize(chain, header, state, &body.Transactions, body.Uncles, body.Withdrawals, nil, nil, nil, tracer) // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 9602b8b73e..13ac03ad9d 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -1170,8 +1170,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } - if tracer.OnSystemTxEnd != nil { - tracer.OnSystemTxEnd(intrinsicGas) + if tracer.OnSystemTxFixIntrinsicGas != nil { + tracer.OnSystemTxFixIntrinsicGas(intrinsicGas) } return tracer.GetResult() } diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index ae1c0786a5..dc8f3c8b82 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -240,11 +240,11 @@ func NewStructLogger(cfg *Config) *StructLogger { func (l *StructLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - OnTxStart: l.OnTxStart, - OnTxEnd: l.OnTxEnd, - OnExit: l.OnExit, - OnOpcode: l.OnOpcode, - OnSystemTxEnd: l.OnSystemTxEnd, + OnTxStart: l.OnTxStart, + OnTxEnd: l.OnTxEnd, + OnExit: l.OnExit, + OnOpcode: l.OnOpcode, + OnSystemTxFixIntrinsicGas: l.OnSystemTxFixIntrinsicGas, } } @@ -375,7 +375,7 @@ func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) { } } -func (l *StructLogger) OnSystemTxEnd(intrinsicGas uint64) { +func (l *StructLogger) OnSystemTxFixIntrinsicGas(intrinsicGas uint64) { l.usedGas -= intrinsicGas } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 37865ffeaf..01c9ce4b52 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -133,12 +133,12 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *param } return &tracers.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnLog: t.OnLog, - OnSystemTxEnd: t.OnSystemTxEnd, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnLog: t.OnLog, + OnSystemTxFixIntrinsicGas: t.OnSystemTxFixIntrinsicGas, }, GetResult: t.GetResult, Stop: t.Stop, @@ -234,7 +234,7 @@ func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) { } } -func (t *callTracer) OnSystemTxEnd(intrinsicGas uint64) { +func (t *callTracer) OnSystemTxFixIntrinsicGas(intrinsicGas uint64) { t.callstack[0].GasUsed -= intrinsicGas } diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index effccde3e7..3168d26af0 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -218,8 +218,8 @@ func (t *flatCallTracer) OnTxEnd(receipt *types.Receipt, err error) { t.tracer.OnTxEnd(receipt, err) } -func (t *flatCallTracer) OnSystemTxEnd(intrinsicGas uint64) { - t.tracer.OnSystemTxEnd(intrinsicGas) +func (t *flatCallTracer) OnSystemTxFixIntrinsicGas(intrinsicGas uint64) { + t.tracer.OnSystemTxFixIntrinsicGas(intrinsicGas) } // GetResult returns an empty json object. diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 439c65e85f..d63b59c89b 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -58,19 +58,19 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params t := &muxTracer{names: names, tracers: objects} return &tracers.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnOpcode: t.OnOpcode, - OnFault: t.OnFault, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, - OnSystemTxEnd: t.OnSystemTxEnd, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, + OnSystemTxFixIntrinsicGas: t.OnSystemTxFixIntrinsicGas, }, GetResult: t.GetResult, Stop: t.Stop, @@ -173,9 +173,9 @@ func (t *muxTracer) OnLog(log *types.Log) { } } -func (t *muxTracer) OnSystemTxEnd(intrinsicGas uint64) { +func (t *muxTracer) OnSystemTxFixIntrinsicGas(intrinsicGas uint64) { for _, t := range t.tracers { - t.OnSystemTxEnd(intrinsicGas) + t.OnSystemTxFixIntrinsicGas(intrinsicGas) } } diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go index d9d1cb5934..7057462ecd 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/native/noop.go @@ -40,19 +40,19 @@ func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *param t := &noopTracer{} return &tracers.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnOpcode: t.OnOpcode, - OnFault: t.OnFault, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, - OnSystemTxEnd: t.OnSystemTxEnd, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, + OnSystemTxFixIntrinsicGas: t.OnSystemTxFixIntrinsicGas, }, GetResult: t.GetResult, Stop: t.Stop, @@ -90,7 +90,7 @@ func (*noopTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) { func (*noopTracer) OnLog(log *types.Log) {} -func (*noopTracer) OnSystemTxEnd(intrinsicGas uint64) {} +func (*noopTracer) OnSystemTxFixIntrinsicGas(intrinsicGas uint64) {} // GetResult returns an empty json object. func (t *noopTracer) GetResult() (json.RawMessage, error) { diff --git a/miner/worker.go b/miner/worker.go index e600e17546..f77aa19aed 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1195,7 +1195,7 @@ func (w *worker) generateWork(params *generateParams, witness bool) *newPayloadR } fees := work.state.GetBalance(consensus.SystemAddress) - block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, &body, work.receipts) + block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, &body, work.receipts, nil) if err != nil { return &newPayloadResult{err: err} } @@ -1459,7 +1459,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti if env.header.EmptyWithdrawalsHash() { body.Withdrawals = make([]*types.Withdrawal, 0) } - block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, types.CopyHeader(env.header), env.state, &body, env.receipts) + block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, types.CopyHeader(env.header), env.state, &body, env.receipts, nil) if err != nil { return err }