github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/evmcore/state_transition.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package evmcore 18 19 import ( 20 "fmt" 21 "math" 22 "math/big" 23 24 "github.com/unicornultrafoundation/go-u2u/common" 25 "github.com/unicornultrafoundation/go-u2u/core/types" 26 "github.com/unicornultrafoundation/go-u2u/core/vm" 27 "github.com/unicornultrafoundation/go-u2u/crypto" 28 "github.com/unicornultrafoundation/go-u2u/log" 29 "github.com/unicornultrafoundation/go-u2u/params" 30 ) 31 32 var emptyCodeHash = crypto.Keccak256Hash(nil) 33 34 /* 35 The State Transitioning Model 36 37 A state transition is a change made when a transaction is applied to the current world state 38 The state transitioning model does all the necessary work to work out a valid new state root. 39 40 1) Nonce handling 41 2) Pre pay gas 42 3) Create a new state object if the recipient is \0*32 43 4) Value transfer 44 == If contract creation == 45 46 4a) Attempt to run transaction data 47 4b) If valid, use result as code for the new state object 48 49 == end == 50 5) Run Script section 51 6) Derive new state root 52 */ 53 type StateTransition struct { 54 gp *GasPool 55 msg Message 56 gas uint64 57 gasPrice *big.Int 58 initialGas uint64 59 value *big.Int 60 data []byte 61 state vm.StateDB 62 evm *vm.EVM 63 } 64 65 // Message represents a message sent to a contract. 66 type Message interface { 67 From() common.Address 68 To() *common.Address 69 70 GasPrice() *big.Int 71 GasFeeCap() *big.Int 72 GasTipCap() *big.Int 73 Gas() uint64 74 Value() *big.Int 75 76 Nonce() uint64 77 IsFake() bool 78 Data() []byte 79 AccessList() types.AccessList 80 } 81 82 // ExecutionResult includes all output after executing given evm 83 // message no matter the execution itself is successful or not. 84 type ExecutionResult struct { 85 UsedGas uint64 // Total used gas but include the refunded gas 86 Err error // Any error encountered during the execution(listed in core/vm/errors.go) 87 ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) 88 } 89 90 // Unwrap returns the internal evm error which allows us for further 91 // analysis outside. 92 func (result *ExecutionResult) Unwrap() error { 93 return result.Err 94 } 95 96 // Failed returns the indicator whether the execution is successful or not 97 func (result *ExecutionResult) Failed() bool { return result.Err != nil } 98 99 // Return is a helper function to help caller distinguish between revert reason 100 // and function return. Return returns the data after execution if no error occurs. 101 func (result *ExecutionResult) Return() []byte { 102 if result.Err != nil { 103 return nil 104 } 105 return common.CopyBytes(result.ReturnData) 106 } 107 108 // Revert returns the concrete revert reason if the execution is aborted by `REVERT` 109 // opcode. Note the reason can be nil if no data supplied with revert opcode. 110 func (result *ExecutionResult) Revert() []byte { 111 if result.Err != vm.ErrExecutionReverted { 112 return nil 113 } 114 return common.CopyBytes(result.ReturnData) 115 } 116 117 // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. 118 func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool) (uint64, error) { 119 // Set the starting gas for the raw transaction 120 var gas uint64 121 if isContractCreation { 122 gas = params.TxGasContractCreation 123 } else { 124 gas = params.TxGas 125 } 126 // Bump the required gas by the amount of transactional data 127 if len(data) > 0 { 128 // Zero and non-zero bytes are priced differently 129 var nz uint64 130 for _, byt := range data { 131 if byt != 0 { 132 nz++ 133 } 134 } 135 // Make sure we don't exceed uint64 for all data combinations 136 if (math.MaxUint64-gas)/params.TxDataNonZeroGasEIP2028 < nz { 137 return 0, vm.ErrOutOfGas 138 } 139 gas += nz * params.TxDataNonZeroGasEIP2028 140 141 z := uint64(len(data)) - nz 142 if (math.MaxUint64-gas)/params.TxDataZeroGas < z { 143 return 0, ErrGasUintOverflow 144 } 145 gas += z * params.TxDataZeroGas 146 } 147 if accessList != nil { 148 gas += uint64(len(accessList)) * params.TxAccessListAddressGas 149 gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas 150 } 151 return gas, nil 152 } 153 154 // NewStateTransition initialises and returns a new state transition object. 155 func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { 156 return &StateTransition{ 157 gp: gp, 158 evm: evm, 159 msg: msg, 160 gasPrice: msg.GasPrice(), 161 value: msg.Value(), 162 data: msg.Data(), 163 state: evm.StateDB, 164 } 165 } 166 167 // ApplyMessage computes the new state by applying the given message 168 // against the old state within the environment. 169 // 170 // ApplyMessage returns the bytes returned by any EVM execution (if it took place), 171 // the gas used (which includes gas refunds) and an error if it failed. An error always 172 // indicates a core error meaning that the message would always fail for that particular 173 // state and would never be accepted within a block. 174 func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) { 175 res, err := NewStateTransition(evm, msg, gp).TransitionDb() 176 if err != nil { 177 log.Debug("Tx skipped", "err", err) 178 } 179 return res, err 180 } 181 182 // to returns the recipient of the message. 183 func (st *StateTransition) to() common.Address { 184 if st.msg == nil || st.msg.To() == nil /* contract creation */ { 185 return common.Address{} 186 } 187 return *st.msg.To() 188 } 189 190 func (st *StateTransition) buyGas() error { 191 mgval := new(big.Int).SetUint64(st.msg.Gas()) 192 mgval = mgval.Mul(mgval, st.gasPrice) 193 // Note: U2U doesn't need to check against gasFeeCap instead of gasPrice, as it's too aggressive in the asynchronous environment 194 if have, want := st.state.GetBalance(st.msg.From()), mgval; have.Cmp(want) < 0 { 195 return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) 196 } 197 if err := st.gp.SubGas(st.msg.Gas()); err != nil { 198 return err 199 } 200 st.gas += st.msg.Gas() 201 202 st.initialGas = st.msg.Gas() 203 st.state.SubBalance(st.msg.From(), mgval) 204 return nil 205 } 206 207 func (st *StateTransition) preCheck() error { 208 // Only check transactions that are not fake 209 if !st.msg.IsFake() { 210 // Make sure this transaction's nonce is correct. 211 stNonce := st.state.GetNonce(st.msg.From()) 212 if msgNonce := st.msg.Nonce(); stNonce < msgNonce { 213 return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh, 214 st.msg.From().Hex(), msgNonce, stNonce) 215 } else if stNonce > msgNonce { 216 return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, 217 st.msg.From().Hex(), msgNonce, stNonce) 218 } 219 // Make sure the sender is an EOA 220 if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { 221 return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, 222 st.msg.From().Hex(), codeHash) 223 } 224 } 225 // Note: U2U doesn't need to check gasFeeCap >= BaseFee, because it's already checked by epochcheck 226 return st.buyGas() 227 } 228 229 func (st *StateTransition) internal() bool { 230 zeroAddr := common.Address{} 231 return st.msg.From() == zeroAddr 232 } 233 234 // TransitionDb will transition the state by applying the current message and 235 // returning the evm execution result with following fields. 236 // 237 // - used gas: 238 // total gas used (including gas being refunded) 239 // - returndata: 240 // the returned data from evm 241 // - concrete execution error: 242 // various **EVM** error which aborts the execution, 243 // e.g. ErrOutOfGas, ErrExecutionReverted 244 // 245 // However if any consensus issue encountered, return the error directly with 246 // nil evm execution result. 247 func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { 248 // First check this message satisfies all consensus rules before 249 // applying the message. The rules include these clauses 250 // 251 // 1. the nonce of the message caller is correct 252 // 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) 253 // 3. the amount of gas required is available in the block 254 // 4. the purchased gas is enough to cover intrinsic usage 255 // 5. there is no overflow when calculating intrinsic gas 256 257 // Note: insufficient balance for **topmost** call isn't a consensus error in U2U, unlike Ethereum 258 // Such transaction will revert and consume sender's gas 259 260 // Check clauses 1-3, buy gas if everything is correct 261 if err := st.preCheck(); err != nil { 262 return nil, err 263 } 264 msg := st.msg 265 sender := vm.AccountRef(msg.From()) 266 contractCreation := msg.To() == nil 267 268 london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) 269 270 // Check clauses 4-5, subtract intrinsic gas if everything is correct 271 gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation) 272 if err != nil { 273 return nil, err 274 } 275 if st.gas < gas { 276 return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) 277 } 278 st.gas -= gas 279 280 // Set up the initial access list. 281 if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin { 282 st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) 283 } 284 285 var ( 286 ret []byte 287 vmerr error // vm errors do not effect consensus and are therefore not assigned to err 288 ) 289 if contractCreation { 290 ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value) 291 } else { 292 // Increment the nonce for the next transaction 293 st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) 294 ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) 295 } 296 // use 10% of not used gas 297 if !st.internal() { 298 st.gas -= st.gas / 10 299 } 300 301 if !london { 302 // Before EIP-3529: refunds were capped to gasUsed / 2 303 st.refundGas(params.RefundQuotient) 304 } else { 305 // After EIP-3529: refunds are capped to gasUsed / 5 306 st.refundGas(params.RefundQuotientEIP3529) 307 } 308 309 return &ExecutionResult{ 310 UsedGas: st.gasUsed(), 311 Err: vmerr, 312 ReturnData: ret, 313 }, nil 314 } 315 316 func (st *StateTransition) refundGas(refundQuotient uint64) { 317 // Apply refund counter, capped to a refund quotient 318 refund := st.gasUsed() / refundQuotient 319 if refund > st.state.GetRefund() { 320 refund = st.state.GetRefund() 321 } 322 st.gas += refund 323 324 // Return wei for remaining gas, exchanged at the original rate. 325 remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) 326 st.state.AddBalance(st.msg.From(), remaining) 327 328 // Also return remaining gas to the block gas counter so it is 329 // available for the next transaction. 330 st.gp.AddGas(st.gas) 331 } 332 333 // gasUsed returns the amount of gas used up by the state transition. 334 func (st *StateTransition) gasUsed() uint64 { 335 return st.initialGas - st.gas 336 }