From 91322ebbefb83b149bb20f0542a290d62a167553 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Thu, 13 Oct 2022 08:32:59 -0700 Subject: [PATCH] Make tx/message data get accessed consistently through st.msg (#35) --- core/state_transition.go | 94 ++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 19dbcfd5da8b..2f9d9cdd69e3 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -50,18 +50,11 @@ The state transitioning model does all the necessary work to work out a valid ne 6) Derive new state root */ type StateTransition struct { - gp *GasPool - msg Message - gas uint64 - gasPrice *big.Int - gasFeeCap *big.Int - gasTipCap *big.Int - maxFeePerDataGas *big.Int - initialGas uint64 - value *big.Int - data []byte - state vm.StateDB - evm *vm.EVM + gp *GasPool + msg Message + gasRemaining uint64 + state vm.StateDB + evm *vm.EVM } // Message represents a message sent to a contract. @@ -169,16 +162,10 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b // NewStateTransition initialises and returns a new state transition object. func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ - gp: gp, - evm: evm, - msg: msg, - gasPrice: msg.GasPrice(), - gasFeeCap: msg.GasFeeCap(), - gasTipCap: msg.GasTipCap(), - maxFeePerDataGas: msg.MaxFeePerDataGas(), - value: msg.Value(), - data: msg.Data(), - state: evm.StateDB, + gp: gp, + evm: evm, + msg: msg, + state: evm.StateDB, } } @@ -203,7 +190,7 @@ func (st *StateTransition) to() common.Address { func (st *StateTransition) buyGas() error { mgval := new(big.Int).SetUint64(st.msg.Gas()) - mgval = mgval.Mul(mgval, st.gasPrice) + mgval = mgval.Mul(mgval, st.msg.GasPrice()) // compute data fee for eip-4844 data blobs if any dgval := new(big.Int) @@ -219,14 +206,14 @@ func (st *StateTransition) buyGas() error { // perform the required user balance checks balanceRequired := new(big.Int) - if st.gasFeeCap == nil { + if st.msg.GasFeeCap() == nil { balanceRequired.Set(mgval) } else { - balanceRequired.Add(st.value, dgval) + balanceRequired.Add(st.msg.Value(), dgval) // EIP-1559 mandates that the sender has enough balance to cover not just actual fee but // the max gas fee, so we compute this upper bound rather than use mgval here. maxGasFee := new(big.Int).SetUint64(st.msg.Gas()) - maxGasFee.Mul(maxGasFee, st.gasFeeCap) + maxGasFee.Mul(maxGasFee, st.msg.GasFeeCap()) balanceRequired.Add(balanceRequired, maxGasFee) } if have, want := st.state.GetBalance(st.msg.From()), balanceRequired; have.Cmp(want) < 0 { @@ -237,8 +224,7 @@ func (st *StateTransition) buyGas() error { if err := st.gp.SubGas(st.msg.Gas()); err != nil { return err } - st.gas += st.msg.Gas() - st.initialGas = st.msg.Gas() + st.gasRemaining += st.msg.Gas() if err := st.gp.SubDataGas(dataGasUsed); err != nil { return err } @@ -270,37 +256,39 @@ func (st *StateTransition) preCheck() error { st.msg.From().Hex(), codeHash) } } - // Make sure that transaction gasFeeCap is greater than the baseFee (post london) + // Make sure that transaction GasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { + gasFeeCap := st.msg.GasFeeCap() + gasTipCap := st.msg.GasTipCap() // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) - if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 { - if l := st.gasFeeCap.BitLen(); l > 256 { + if !st.evm.Config.NoBaseFee || gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 { + if l := gasFeeCap.BitLen(); l > 256 { return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh, st.msg.From().Hex(), l) } - if l := st.gasTipCap.BitLen(); l > 256 { + if l := gasTipCap.BitLen(); l > 256 { return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh, st.msg.From().Hex(), l) } - if st.gasFeeCap.Cmp(st.gasTipCap) < 0 { + if gasFeeCap.Cmp(gasTipCap) < 0 { return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, - st.msg.From().Hex(), st.gasTipCap, st.gasFeeCap) + st.msg.From().Hex(), gasTipCap, gasFeeCap) } // This will panic if baseFee is nil, but basefee presence is verified // as part of header validation. - if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { + if gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, - st.msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee) + st.msg.From().Hex(), gasFeeCap, st.evm.Context.BaseFee) } } } usesDataGas := st.dataGasUsed().Sign() > 0 if usesDataGas && st.evm.ChainConfig().IsSharding(st.evm.Context.BlockNumber) { dataGasPrice := misc.GetDataGasPrice(st.evm.Context.ExcessDataGas) - if dataGasPrice.Cmp(st.maxFeePerDataGas) > 0 { + if dataGasPrice.Cmp(st.msg.MaxFeePerDataGas()) > 0 { return fmt.Errorf("%w: address %v, maxFeePerDataGas: %v dataGasPrice: %v, excessDataGas: %v", ErrMaxFeePerDataGas, - st.msg.From().Hex(), st.maxFeePerDataGas, dataGasPrice, st.evm.Context.ExcessDataGas) + st.msg.From().Hex(), st.msg.MaxFeePerDataGas(), dataGasPrice, st.evm.Context.ExcessDataGas) } } return st.buyGas() @@ -338,9 +326,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } if st.evm.Config.Debug { - st.evm.Config.Tracer.CaptureTxStart(st.initialGas) + st.evm.Config.Tracer.CaptureTxStart(st.msg.Gas()) defer func() { - st.evm.Config.Tracer.CaptureTxEnd(st.gas) + st.evm.Config.Tracer.CaptureTxEnd(st.gasRemaining) }() } @@ -357,14 +345,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { EIP4844: rules.IsSharding, } // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, intrinsicGasRules) + gas, err := IntrinsicGas(msg.Data(), st.msg.AccessList(), contractCreation, intrinsicGasRules) if err != nil { return nil, err } - if st.gas < gas { - return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) + if st.gasRemaining < gas { + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } - st.gas -= gas + st.gasRemaining -= gas // Check clause 6 if msg.Value().Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) { @@ -380,11 +368,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { vmerr error // vm errors do not effect consensus and are therefore not assigned to err ) if contractCreation { - ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value) + ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data(), st.gasRemaining, msg.Value()) } else { // Increment the nonce for the next transaction st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) - ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) + ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data(), st.gasRemaining, msg.Value()) } // Note that unlike regular gas, data fee gas is not refunded if the tx is reverted, per @@ -397,12 +385,12 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.refundGas(params.RefundQuotientEIP3529) } - effectiveTip := st.gasPrice + effectiveTip := msg.GasPrice() if rules.IsLondon { - effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) + effectiveTip = cmath.BigMin(msg.GasTipCap(), new(big.Int).Sub(msg.GasFeeCap(), st.evm.Context.BaseFee)) } - if st.evm.Config.NoBaseFee && st.gasFeeCap.Sign() == 0 && st.gasTipCap.Sign() == 0 { + if st.evm.Config.NoBaseFee && msg.GasFeeCap().Sign() == 0 && msg.GasTipCap().Sign() == 0 { // Skip fee payment when NoBaseFee is set and the fee fields // are 0. This avoids a negative effectiveTip being applied to // the coinbase when simulating calls. @@ -425,20 +413,20 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { if refund > st.state.GetRefund() { refund = st.state.GetRefund() } - st.gas += refund + st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. - remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) + remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice()) st.state.AddBalance(st.msg.From(), remaining) // Also return remaining gas to the block gas counter so it is // available for the next transaction. - st.gp.AddGas(st.gas) + st.gp.AddGas(st.gasRemaining) } // gasUsed returns the amount of gas used up by the state transition. func (st *StateTransition) gasUsed() uint64 { - return st.initialGas - st.gas + return st.msg.Gas() - st.gasRemaining } func (st *StateTransition) dataGasUsed() *big.Int {