github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/core/state_processor.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 core 18 19 import ( 20 "fmt" 21 "github.com/intfoundation/intchain/common" 22 "github.com/intfoundation/intchain/consensus" 23 "github.com/intfoundation/intchain/core/state" 24 "github.com/intfoundation/intchain/core/types" 25 "github.com/intfoundation/intchain/core/vm" 26 "github.com/intfoundation/intchain/crypto" 27 intAbi "github.com/intfoundation/intchain/intabi/abi" 28 "github.com/intfoundation/intchain/log" 29 "github.com/intfoundation/intchain/params" 30 "math/big" 31 ) 32 33 // StateProcessor is a basic Processor, which takes care of transitioning 34 // state from one point to another. 35 // 36 // StateProcessor implements Processor. 37 type StateProcessor struct { 38 config *params.ChainConfig // Chain configuration options 39 bc *BlockChain // Canonical block chain 40 engine consensus.Engine // Consensus engine used for block rewards 41 cch CrossChainHelper 42 } 43 44 // NewStateProcessor initialises a new StateProcessor. 45 func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine, cch CrossChainHelper) *StateProcessor { 46 return &StateProcessor{ 47 config: config, 48 bc: bc, 49 engine: engine, 50 cch: cch, 51 } 52 } 53 54 // Process processes the state changes according to the Ethereum rules by running 55 // the transaction messages using the statedb and applying any rewards to both 56 // the processor (coinbase) and any included uncles. 57 // 58 // Process returns the receipts and logs accumulated during the process and 59 // returns the amount of gas that was used in the process. If any of the 60 // transactions failed to execute due to insufficient gas it will return an error. 61 func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *types.PendingOps, error) { 62 var ( 63 receipts types.Receipts 64 usedGas = new(uint64) 65 header = block.Header() 66 allLogs []*types.Log 67 gp = new(GasPool).AddGas(block.GasLimit()) 68 ops = new(types.PendingOps) 69 ) 70 // Mutate the the block and state according to any hard-fork specs 71 //if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { 72 // misc.ApplyDAOHardFork(statedb) 73 //} 74 totalUsedMoney := big.NewInt(0) 75 // Iterate over and process the individual transactions 76 for i, tx := range block.Transactions() { 77 statedb.Prepare(tx.Hash(), block.Hash(), i) 78 //receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) 79 receipt, err := ApplyTransactionEx(p.config, p.bc, nil, gp, statedb, ops, header, tx, 80 usedGas, totalUsedMoney, cfg, p.cch, false) 81 log.Debugf("(p *StateProcessor) Process(),after ApplyTransactionEx, receipt is %v\n", receipt) 82 if err != nil { 83 return nil, nil, 0, nil, err 84 } 85 receipts = append(receipts, receipt) 86 allLogs = append(allLogs, receipt.Logs...) 87 } 88 // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) 89 _, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), totalUsedMoney, block.Uncles(), receipts, ops) 90 if err != nil { 91 return nil, nil, 0, nil, err 92 } 93 94 return receipts, allLogs, *usedGas, ops, nil 95 } 96 97 // ApplyTransaction attempts to apply a transaction to the given state database 98 // and uses the input parameters for its environment. It returns the receipt 99 // for the transaction, gas used and an error if the transaction failed, 100 // indicating the block was invalid. 101 func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { 102 msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) 103 if err != nil { 104 return nil, err 105 } 106 // Create a new context to be used in the EVM environment 107 context := NewEVMContext(msg, header, bc, author) 108 // Create a new environment which holds all relevant information 109 // about the transaction and calling mechanisms. 110 vmenv := vm.NewEVM(context, statedb, config, cfg) 111 // Apply the transaction to the current state (included in the env) 112 result, err := ApplyMessage(vmenv, msg, gp) 113 if err != nil { 114 return nil, err 115 } 116 // Update the state with pending changes 117 var root []byte 118 if config.IsByzantium(header.Number) { 119 statedb.Finalise(true) 120 } else { 121 root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() 122 } 123 *usedGas += result.UsedGas 124 125 // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx 126 // based on the eip phase, we're passing wether the root touch-delete accounts. 127 receipt := types.NewReceipt(root, result.Failed(), *usedGas) 128 receipt.TxHash = tx.Hash() 129 receipt.GasUsed = result.UsedGas 130 // if the transaction created a contract, store the creation address in the receipt. 131 if msg.To() == nil { 132 receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) 133 } 134 // Set the receipt logs and create a bloom for filtering 135 receipt.Logs = statedb.GetLogs(tx.Hash()) 136 receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) 137 138 return receipt, err 139 } 140 141 // ApplyTransactionEx attempts to apply a transaction to the given state database 142 // and uses the input parameters for its environment. It returns the receipt 143 // for the transaction, gas used and an error if the transaction failed, 144 // indicating the block was invalid. 145 func ApplyTransactionEx(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, ops *types.PendingOps, 146 header *types.Header, tx *types.Transaction, usedGas *uint64, totalUsedMoney *big.Int, cfg vm.Config, cch CrossChainHelper, mining bool) (*types.Receipt, error) { 147 148 signer := types.MakeSigner(config, header.Number) 149 msg, err := tx.AsMessage(signer) 150 if err != nil { 151 return nil, err 152 } 153 154 if !intAbi.IsIntChainContractAddr(tx.To()) { 155 156 //log.Debugf("ApplyTransactionEx 1\n") 157 158 // Create a new context to be used in the EVM environment 159 context := NewEVMContext(msg, header, bc, author) 160 161 //log.Debugf("ApplyTransactionEx 2\n") 162 163 // Create a new environment which holds all relevant information 164 // about the transaction and calling mechanisms. 165 vmenv := vm.NewEVM(context, statedb, config, cfg) 166 // Apply the transaction to the current state (included in the env) 167 result, money, err := ApplyMessageEx(vmenv, msg, gp) 168 if err != nil { 169 return nil, err 170 } 171 172 //log.Debugf("ApplyTransactionEx 3\n") 173 // Update the state with pending changes 174 var root []byte 175 if config.IsByzantium(header.Number) { 176 //log.Debugf("ApplyTransactionEx(), is byzantium\n") 177 statedb.Finalise(true) 178 } else { 179 //log.Debugf("ApplyTransactionEx(), is not byzantium\n") 180 root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() 181 } 182 *usedGas += result.UsedGas 183 totalUsedMoney.Add(totalUsedMoney, money) 184 185 // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx 186 // based on the eip phase, we're passing wether the root touch-delete accounts. 187 receipt := types.NewReceipt(root, result.Failed(), *usedGas) 188 log.Debugf("ApplyTransactionEx,new receipt with (root,failed,*usedGas) = (%v,%v,%v)\n", root, result.Failed(), *usedGas) 189 receipt.TxHash = tx.Hash() 190 //log.Debugf("ApplyTransactionEx,new receipt with txhash %v\n", receipt.TxHash) 191 receipt.GasUsed = result.UsedGas 192 //log.Debugf("ApplyTransactionEx,new receipt with gas %v\n", receipt.GasUsed) 193 // if the transaction created a contract, store the creation address in the receipt. 194 if msg.To() == nil { 195 receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) 196 } 197 // Set the receipt logs and create a bloom for filtering 198 receipt.Logs = statedb.GetLogs(tx.Hash()) 199 //log.Debugf("ApplyTransactionEx,new receipt with receipt.Logs %v\n", receipt.Logs) 200 receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) 201 receipt.BlockHash = statedb.BlockHash() 202 receipt.BlockNumber = header.Number 203 receipt.TransactionIndex = uint(statedb.TxIndex()) 204 //log.Debugf("ApplyTransactionEx,new receipt with receipt.Bloom %v\n", receipt.Bloom) 205 //log.Debugf("ApplyTransactionEx 4\n") 206 return receipt, err 207 208 } else { 209 210 // the first 4 bytes is the function identifier 211 data := tx.Data() 212 function, err := intAbi.FunctionTypeFromId(data[:4]) 213 if err != nil { 214 return nil, err 215 } 216 log.Infof("ApplyTransactionEx() 0, Chain Function is %v", function.String()) 217 218 // check Function main/child flag 219 if config.IsMainChain() && !function.AllowInMainChain() { 220 return nil, ErrNotAllowedInMainChain 221 } else if !config.IsMainChain() && !function.AllowInChildChain() { 222 return nil, ErrNotAllowedInChildChain 223 } 224 225 from := msg.From() 226 // Make sure this transaction's nonce is correct 227 if msg.CheckNonce() { 228 nonce := statedb.GetNonce(from) 229 if nonce < msg.Nonce() { 230 log.Info("ApplyTransactionEx() abort due to nonce too high") 231 return nil, ErrNonceTooHigh 232 } else if nonce > msg.Nonce() { 233 log.Info("ApplyTransactionEx() abort due to nonce too low") 234 return nil, ErrNonceTooLow 235 } 236 } 237 238 // pre-buy gas according to the gas limit 239 gasLimit := tx.Gas() 240 gasValue := new(big.Int).Mul(new(big.Int).SetUint64(gasLimit), tx.GasPrice()) 241 if statedb.GetBalance(from).Cmp(gasValue) < 0 { 242 return nil, fmt.Errorf("insufficient INT for gas (%x). Req %v, has %v", from.Bytes()[:4], gasValue, statedb.GetBalance(from)) 243 } 244 if err := gp.SubGas(gasLimit); err != nil { 245 return nil, err 246 } 247 statedb.SubBalance(from, gasValue) 248 //log.Infof("ApplyTransactionEx() 1, gas is %v, gasPrice is %v, gasValue is %v\n", gasLimit, tx.GasPrice(), gasValue) 249 250 // use gas 251 gas := function.RequiredGas() 252 if gasLimit < gas { 253 return nil, vm.ErrOutOfGas 254 } 255 256 // Check Tx Amount 257 if statedb.GetBalance(from).Cmp(tx.Value()) == -1 { 258 return nil, fmt.Errorf("insufficient INT for tx amount (%x). Req %v, has %v", from.Bytes()[:4], tx.Value(), statedb.GetBalance(from)) 259 } 260 261 if applyCb := GetApplyCb(function); applyCb != nil { 262 if function.IsCrossChainType() { 263 if fn, ok := applyCb.(CrossChainApplyCb); ok { 264 cch.GetMutex().Lock() 265 err := fn(tx, statedb, ops, cch, mining) 266 cch.GetMutex().Unlock() 267 268 if err != nil { 269 return nil, err 270 } 271 } else { 272 panic("callback func is wrong, this should not happened, please check the code") 273 } 274 } else { 275 if fn, ok := applyCb.(NonCrossChainApplyCb); ok { 276 if err := fn(tx, statedb, bc, ops); err != nil { 277 return nil, err 278 } 279 } else { 280 panic("callback func is wrong, this should not happened, please check the code") 281 } 282 } 283 } 284 285 // refund gas 286 remainingGas := gasLimit - gas 287 remaining := new(big.Int).Mul(new(big.Int).SetUint64(remainingGas), tx.GasPrice()) 288 statedb.AddBalance(from, remaining) 289 gp.AddGas(remainingGas) 290 291 *usedGas += gas 292 totalUsedMoney.Add(totalUsedMoney, new(big.Int).Mul(new(big.Int).SetUint64(gas), tx.GasPrice())) 293 log.Infof("ApplyTransactionEx() 2, totalUsedMoney is %v\n", totalUsedMoney) 294 295 // Update the state with pending changes 296 var root []byte 297 if config.IsByzantium(header.Number) { 298 statedb.Finalise(true) 299 } else { 300 root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() 301 } 302 303 // TODO: whether true of false for the transaction receipt 304 receipt := types.NewReceipt(root, false, *usedGas) 305 receipt.TxHash = tx.Hash() 306 receipt.GasUsed = gas 307 308 // Set the receipt logs and create a bloom for filtering 309 receipt.Logs = statedb.GetLogs(tx.Hash()) 310 receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) 311 receipt.BlockHash = statedb.BlockHash() 312 receipt.BlockNumber = header.Number 313 receipt.TransactionIndex = uint(statedb.TxIndex()) 314 315 statedb.SetNonce(msg.From(), statedb.GetNonce(msg.From())+1) 316 //log.Infof("ApplyTransactionEx() 3, totalUsedMoney is %v\n", totalUsedMoney) 317 318 return receipt, nil 319 } 320 }