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  }