From 61accb021a7f3fc1e69a5c9de27bf35f6919df2e Mon Sep 17 00:00:00 2001 From: Jerry Date: Tue, 26 Jul 2022 11:36:33 -0700 Subject: [PATCH] Move fee burning and tipping out of state transition to reduce read/write dependencies between transactions --- core/parallel_state_processor.go | 87 ++++++++++++++++++++++++-------- core/state_transition.go | 87 +++++++++++++++++++++++--------- 2 files changed, 127 insertions(+), 47 deletions(-) diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go index c2d6647f65..dd0e9dc0a5 100644 --- a/core/parallel_state_processor.go +++ b/core/parallel_state_processor.go @@ -55,16 +55,17 @@ type ExecutionTask struct { msg types.Message config *params.ChainConfig - gasLimit uint64 - blockNumber *big.Int - blockHash common.Hash - blockContext vm.BlockContext - tx *types.Transaction - index int - statedb *state.StateDB // State database that stores the modified values after tx execution. - cleanStateDB *state.StateDB // A clean copy of the initial statedb. It should not be modified. - evmConfig vm.Config - result *ExecutionResult + gasLimit uint64 + blockNumber *big.Int + blockHash common.Hash + blockContext vm.BlockContext + tx *types.Transaction + index int + statedb *state.StateDB // State database that stores the modified values after tx execution. + cleanStateDB *state.StateDB // A clean copy of the initial statedb. It should not be modified. + evmConfig vm.Config + result *ExecutionResult + shouldDelayFeeCal *bool } func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (err error) { @@ -91,7 +92,11 @@ func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (er }() // Apply the transaction to the current state (included in the env). - result, err := ApplyMessage(evm, task.msg, new(GasPool).AddGas(task.gasLimit)) + if *task.shouldDelayFeeCal { + task.result, err = ApplyMessageNoFeeBurnOrTip(evm, task.msg, new(GasPool).AddGas(task.gasLimit)) + } else { + task.result, err = ApplyMessage(evm, task.msg, new(GasPool).AddGas(task.gasLimit)) + } if task.statedb.HadInvalidRead() || err != nil { err = blockstm.ErrExecAbort @@ -100,8 +105,6 @@ func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (er task.statedb.Finalise(false) - task.result = result - return } @@ -140,6 +143,8 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat tasks := make([]blockstm.ExecTask, 0, len(block.Transactions())) + shouldDelayFeeCal := true + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) @@ -148,18 +153,26 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } + bc := NewEVMBlockContext(header, p.bc, nil) + cleansdb := statedb.Copy() + if msg.From() == bc.Coinbase { + shouldDelayFeeCal = false + } + task := &ExecutionTask{ - msg: msg, - config: p.config, - gasLimit: block.GasLimit(), - blockNumber: blockNumber, - blockHash: blockHash, - tx: tx, - index: i, - cleanStateDB: cleansdb, - blockContext: NewEVMBlockContext(header, p.bc, nil), + msg: msg, + config: p.config, + gasLimit: block.GasLimit(), + blockNumber: blockNumber, + blockHash: blockHash, + tx: tx, + index: i, + cleanStateDB: cleansdb, + blockContext: bc, + evmConfig: cfg, + shouldDelayFeeCal: &shouldDelayFeeCal, } tasks = append(tasks, task) @@ -172,15 +185,45 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat return nil, nil, 0, err } + london := p.config.IsLondon(blockNumber) + for _, task := range tasks { task := task.(*ExecutionTask) statedb.Prepare(task.tx.Hash(), task.index) + + coinbaseBalance := statedb.GetBalance(task.blockContext.Coinbase) + statedb.ApplyMVWriteSet(task.MVWriteList()) for _, l := range task.statedb.GetLogs(task.tx.Hash(), blockHash) { statedb.AddLog(l) } + if shouldDelayFeeCal { + if london { + statedb.AddBalance(task.result.BurntContractAddress, task.result.FeeBurnt) + } + + statedb.AddBalance(task.blockContext.Coinbase, task.result.FeeTipped) + output1 := new(big.Int).SetBytes(task.result.senderInitBalance.Bytes()) + output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes()) + + // Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559 + // add transfer log + AddFeeTransferLog( + statedb, + + task.msg.From(), + task.blockContext.Coinbase, + + task.result.FeeTipped, + task.result.senderInitBalance, + coinbaseBalance, + output1.Sub(output1, task.result.FeeTipped), + output2.Add(output2, task.result.FeeTipped), + ) + } + for k, v := range task.statedb.Preimages() { statedb.AddPreimage(k, v) } diff --git a/core/state_transition.go b/core/state_transition.go index 3fc5a635e9..dee8ef271f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -62,6 +62,11 @@ type StateTransition struct { data []byte state vm.StateDB evm *vm.EVM + + // If true, fee burning and tipping won't happen during transition. Instead, their values will be included in the + // ExecutionResult, which caller can use the values to update the balance of burner and coinbase account. + // This is useful during parallel state transition, where the common account read/write should be minimized. + noFeeBurnAndTip bool } // Message represents a message sent to a contract. @@ -84,9 +89,13 @@ type Message interface { // ExecutionResult includes all output after executing given evm // message no matter the execution itself is successful or not. type ExecutionResult struct { - UsedGas uint64 // Total used gas but include the refunded gas - Err error // Any error encountered during the execution(listed in core/vm/errors.go) - ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + UsedGas uint64 // Total used gas but include the refunded gas + Err error // Any error encountered during the execution(listed in core/vm/errors.go) + ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + senderInitBalance *big.Int + FeeBurnt *big.Int + BurntContractAddress common.Address + FeeTipped *big.Int } // Unwrap returns the internal evm error which allows us for further @@ -183,6 +192,13 @@ func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, erro return NewStateTransition(evm, msg, gp).TransitionDb() } +func ApplyMessageNoFeeBurnOrTip(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) { + st := NewStateTransition(evm, msg, gp) + st.noFeeBurnAndTip = true + + return st.TransitionDb() +} + // to returns the recipient of the message. func (st *StateTransition) to() common.Address { if st.msg == nil || st.msg.To() == nil /* contract creation */ { @@ -276,7 +292,12 @@ func (st *StateTransition) preCheck() error { // nil evm execution result. func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { input1 := st.state.GetBalance(st.msg.From()) - input2 := st.state.GetBalance(st.evm.Context.Coinbase) + + var input2 *big.Int + + if !st.noFeeBurnAndTip { + input2 = st.state.GetBalance(st.evm.Context.Coinbase) + } // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses @@ -342,34 +363,50 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) } amount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip) + + var burnAmount *big.Int + + var burntContractAddress common.Address + if london { - burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context.BlockNumber.Uint64())) - burnAmount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee) - st.state.AddBalance(burntContractAddress, burnAmount) + burntContractAddress = common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context.BlockNumber.Uint64())) + burnAmount = new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee) + + if !st.noFeeBurnAndTip { + st.state.AddBalance(burntContractAddress, burnAmount) + } } - st.state.AddBalance(st.evm.Context.Coinbase, amount) - output1 := new(big.Int).SetBytes(input1.Bytes()) - output2 := new(big.Int).SetBytes(input2.Bytes()) - // Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559 - // add transfer log - AddFeeTransferLog( - st.state, + if !st.noFeeBurnAndTip { + st.state.AddBalance(st.evm.Context.Coinbase, amount) - msg.From(), - st.evm.Context.Coinbase, + output1 := new(big.Int).SetBytes(input1.Bytes()) + output2 := new(big.Int).SetBytes(input2.Bytes()) - amount, - input1, - input2, - output1.Sub(output1, amount), - output2.Add(output2, amount), - ) + // Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559 + // add transfer log + AddFeeTransferLog( + st.state, + + msg.From(), + st.evm.Context.Coinbase, + + amount, + input1, + input2, + output1.Sub(output1, amount), + output2.Add(output2, amount), + ) + } return &ExecutionResult{ - UsedGas: st.gasUsed(), - Err: vmerr, - ReturnData: ret, + UsedGas: st.gasUsed(), + Err: vmerr, + ReturnData: ret, + senderInitBalance: input1, + FeeBurnt: burnAmount, + BurntContractAddress: burntContractAddress, + FeeTipped: amount, }, nil }