github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/core/state_transition.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package core 13 14 import ( 15 "errors" 16 "math" 17 "math/big" 18 19 "github.com/Sberex/go-sberex/common" 20 "github.com/Sberex/go-sberex/core/vm" 21 "github.com/Sberex/go-sberex/log" 22 "github.com/Sberex/go-sberex/params" 23 ) 24 25 var ( 26 errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas") 27 ) 28 29 /* 30 The State Transitioning Model 31 32 A state transition is a change made when a transaction is applied to the current world state 33 The state transitioning model does all all the necessary work to work out a valid new state root. 34 35 1) Nonce handling 36 2) Pre pay gas 37 3) Create a new state object if the recipient is \0*32 38 4) Value transfer 39 == If contract creation == 40 4a) Attempt to run transaction data 41 4b) If valid, use result as code for the new state object 42 == end == 43 5) Run Script section 44 6) Derive new state root 45 */ 46 type StateTransition struct { 47 gp *GasPool 48 msg Message 49 gas uint64 50 gasPrice *big.Int 51 initialGas uint64 52 value *big.Int 53 data []byte 54 state vm.StateDB 55 evm *vm.EVM 56 } 57 58 // Message represents a message sent to a contract. 59 type Message interface { 60 From() common.Address 61 //FromFrontier() (common.Address, error) 62 To() *common.Address 63 64 GasPrice() *big.Int 65 Gas() uint64 66 Value() *big.Int 67 68 Nonce() uint64 69 CheckNonce() bool 70 Data() []byte 71 } 72 73 // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. 74 func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) { 75 // Set the starting gas for the raw transaction 76 var gas uint64 77 if contractCreation && homestead { 78 gas = params.TxGasContractCreation 79 } else { 80 gas = params.TxGas 81 } 82 // Bump the required gas by the amount of transactional data 83 if len(data) > 0 { 84 // Zero and non-zero bytes are priced differently 85 var nz uint64 86 for _, byt := range data { 87 if byt != 0 { 88 nz++ 89 } 90 } 91 // Make sure we don't exceed uint64 for all data combinations 92 if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz { 93 return 0, vm.ErrOutOfGas 94 } 95 gas += nz * params.TxDataNonZeroGas 96 97 z := uint64(len(data)) - nz 98 if (math.MaxUint64-gas)/params.TxDataZeroGas < z { 99 return 0, vm.ErrOutOfGas 100 } 101 gas += z * params.TxDataZeroGas 102 } 103 return gas, nil 104 } 105 106 // NewStateTransition initialises and returns a new state transition object. 107 func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { 108 return &StateTransition{ 109 gp: gp, 110 evm: evm, 111 msg: msg, 112 gasPrice: msg.GasPrice(), 113 value: msg.Value(), 114 data: msg.Data(), 115 state: evm.StateDB, 116 } 117 } 118 119 // ApplyMessage computes the new state by applying the given message 120 // against the old state within the environment. 121 // 122 // ApplyMessage returns the bytes returned by any EVM execution (if it took place), 123 // the gas used (which includes gas refunds) and an error if it failed. An error always 124 // indicates a core error meaning that the message would always fail for that particular 125 // state and would never be accepted within a block. 126 func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) { 127 return NewStateTransition(evm, msg, gp).TransitionDb() 128 } 129 130 func (st *StateTransition) from() vm.AccountRef { 131 f := st.msg.From() 132 if !st.state.Exist(f) { 133 st.state.CreateAccount(f) 134 } 135 return vm.AccountRef(f) 136 } 137 138 func (st *StateTransition) to() vm.AccountRef { 139 if st.msg == nil { 140 return vm.AccountRef{} 141 } 142 to := st.msg.To() 143 if to == nil { 144 return vm.AccountRef{} // contract creation 145 } 146 147 reference := vm.AccountRef(*to) 148 if !st.state.Exist(*to) { 149 st.state.CreateAccount(*to) 150 } 151 return reference 152 } 153 154 func (st *StateTransition) useGas(amount uint64) error { 155 if st.gas < amount { 156 return vm.ErrOutOfGas 157 } 158 st.gas -= amount 159 160 return nil 161 } 162 163 func (st *StateTransition) buyGas() error { 164 var ( 165 state = st.state 166 sender = st.from() 167 ) 168 mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) 169 if state.GetBalance(sender.Address()).Cmp(mgval) < 0 { 170 return errInsufficientBalanceForGas 171 } 172 if err := st.gp.SubGas(st.msg.Gas()); err != nil { 173 return err 174 } 175 st.gas += st.msg.Gas() 176 177 st.initialGas = st.msg.Gas() 178 state.SubBalance(sender.Address(), mgval) 179 return nil 180 } 181 182 func (st *StateTransition) preCheck() error { 183 msg := st.msg 184 sender := st.from() 185 186 // Make sure this transaction's nonce is correct 187 if msg.CheckNonce() { 188 nonce := st.state.GetNonce(sender.Address()) 189 if nonce < msg.Nonce() { 190 return ErrNonceTooHigh 191 } else if nonce > msg.Nonce() { 192 return ErrNonceTooLow 193 } 194 } 195 return st.buyGas() 196 } 197 198 // TransitionDb will transition the state by applying the current message and 199 // returning the result including the the used gas. It returns an error if it 200 // failed. An error indicates a consensus issue. 201 func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) { 202 if err = st.preCheck(); err != nil { 203 return 204 } 205 msg := st.msg 206 sender := st.from() // err checked in preCheck 207 208 homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) 209 contractCreation := msg.To() == nil 210 211 // Pay intrinsic gas 212 gas, err := IntrinsicGas(st.data, contractCreation, homestead) 213 if err != nil { 214 return nil, 0, false, err 215 } 216 if err = st.useGas(gas); err != nil { 217 return nil, 0, false, err 218 } 219 220 var ( 221 evm = st.evm 222 // vm errors do not effect consensus and are therefor 223 // not assigned to err, except for insufficient balance 224 // error. 225 vmerr error 226 ) 227 if contractCreation { 228 ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) 229 } else { 230 // Increment the nonce for the next transaction 231 st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1) 232 ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) 233 } 234 if vmerr != nil { 235 log.Debug("VM returned with error", "err", vmerr) 236 // The only possible consensus-error would be if there wasn't 237 // sufficient balance to make the transfer happen. The first 238 // balance transfer may never fail. 239 if vmerr == vm.ErrInsufficientBalance { 240 return nil, 0, false, vmerr 241 } 242 } 243 st.refundGas() 244 st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) 245 246 return ret, st.gasUsed(), vmerr != nil, err 247 } 248 249 func (st *StateTransition) refundGas() { 250 // Apply refund counter, capped to half of the used gas. 251 refund := st.gasUsed() / 2 252 if refund > st.state.GetRefund() { 253 refund = st.state.GetRefund() 254 } 255 st.gas += refund 256 257 // Return ETH for remaining gas, exchanged at the original rate. 258 sender := st.from() 259 260 remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) 261 st.state.AddBalance(sender.Address(), remaining) 262 263 // Also return remaining gas to the block gas counter so it is 264 // available for the next transaction. 265 st.gp.AddGas(st.gas) 266 } 267 268 // gasUsed returns the amount of gas used up by the state transition. 269 func (st *StateTransition) gasUsed() uint64 { 270 return st.initialGas - st.gas 271 }