github.com/Gessiux/neatchain@v1.3.1/chain/core/state_processor1.go (about)

     1  package core
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  
     7  	"github.com/Gessiux/neatchain/chain/core/state"
     8  	"github.com/Gessiux/neatchain/chain/core/types"
     9  	"github.com/Gessiux/neatchain/chain/core/vm"
    10  	"github.com/Gessiux/neatchain/chain/log"
    11  	neatAbi "github.com/Gessiux/neatchain/neatabi/abi"
    12  	"github.com/Gessiux/neatchain/params"
    13  	"github.com/Gessiux/neatchain/utilities/common"
    14  	"github.com/Gessiux/neatchain/utilities/crypto"
    15  )
    16  
    17  // ApplyTransactionEx attempts to apply a transaction to the given state database
    18  // and uses the input parameters for its environment. It returns the receipt
    19  // for the transaction, gas used and an error if the transaction failed,
    20  // indicating the block was invalid.
    21  func ApplyTransactionEx(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, ops *types.PendingOps,
    22  	header *types.Header, tx *types.Transaction, usedGas *uint64, totalUsedMoney *big.Int, cfg vm.Config, cch CrossChainHelper, mining bool) (*types.Receipt, uint64, error) {
    23  
    24  	signer := types.MakeSigner(config, header.Number)
    25  	msg, err := tx.AsMessage(signer)
    26  	if err != nil {
    27  		return nil, 0, err
    28  	}
    29  
    30  	if !neatAbi.IsNeatChainContractAddr(tx.To()) {
    31  
    32  		//log.Debugf("ApplyTransactionEx 1\n")
    33  
    34  		// Create a new context to be used in the EVM environment
    35  		context := NewEVMContext(msg, header, bc, author)
    36  
    37  		//log.Debugf("ApplyTransactionEx 2\n")
    38  
    39  		// Create a new environment which holds all relevant information
    40  		// about the transaction and calling mechanisms.
    41  		vmenv := vm.NewEVM(context, statedb, config, cfg)
    42  		// Apply the transaction to the current state (included in the env)
    43  		_, gas, money, failed, err := ApplyMessageEx(vmenv, msg, gp)
    44  		if err != nil {
    45  			return nil, 0, err
    46  		}
    47  
    48  		//log.Debugf("ApplyTransactionEx 3\n")
    49  		// Update the state with pending changes
    50  		var root []byte
    51  		if config.IsByzantium(header.Number) {
    52  			//log.Debugf("ApplyTransactionEx(), is byzantium\n")
    53  			statedb.Finalise(true)
    54  		} else {
    55  			//log.Debugf("ApplyTransactionEx(), is not byzantium\n")
    56  			root = statedb.IntermediateRoot(false).Bytes()
    57  		}
    58  		*usedGas += gas
    59  		totalUsedMoney.Add(totalUsedMoney, money)
    60  
    61  		// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
    62  		// based on the eip phase, we're passing wether the root touch-delete accounts.
    63  		receipt := types.NewReceipt(root, failed, *usedGas)
    64  		log.Debugf("ApplyTransactionEx,new receipt with (root,failed,*usedGas) = (%v,%v,%v)\n", root, failed, *usedGas)
    65  		receipt.TxHash = tx.Hash()
    66  		//log.Debugf("ApplyTransactionEx,new receipt with txhash %v\n", receipt.TxHash)
    67  		receipt.GasUsed = gas
    68  		//log.Debugf("ApplyTransactionEx,new receipt with gas %v\n", receipt.GasUsed)
    69  		// if the transaction created a contract, store the creation address in the receipt.
    70  		if msg.To() == nil {
    71  			receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
    72  		}
    73  		// Set the receipt logs and create a bloom for filtering
    74  		receipt.Logs = statedb.GetLogs(tx.Hash())
    75  		//log.Debugf("ApplyTransactionEx,new receipt with receipt.Logs %v\n", receipt.Logs)
    76  		receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
    77  		receipt.BlockHash = statedb.BlockHash()
    78  		receipt.BlockNumber = header.Number
    79  		receipt.TransactionIndex = uint(statedb.TxIndex())
    80  		//log.Debugf("ApplyTransactionEx,new receipt with receipt.Bloom %v\n", receipt.Bloom)
    81  		//log.Debugf("ApplyTransactionEx 4\n")
    82  		return receipt, gas, err
    83  
    84  	} else {
    85  
    86  		// the first 4 bytes is the function identifier
    87  		data := tx.Data()
    88  		function, err := neatAbi.FunctionTypeFromId(data[:4])
    89  		if err != nil {
    90  			return nil, 0, err
    91  		}
    92  		log.Infof("ApplyTransactionEx() 0, Chain Function is %v", function.String())
    93  
    94  		// check Function main/side flag
    95  		if config.IsMainChain() && !function.AllowInMainChain() {
    96  			return nil, 0, ErrNotAllowedInMainChain
    97  		} else if !config.IsMainChain() && !function.AllowInSideChain() {
    98  			return nil, 0, ErrNotAllowedInSideChain
    99  		}
   100  
   101  		from := msg.From()
   102  		// Make sure this transaction's nonce is correct
   103  		if msg.CheckNonce() {
   104  			nonce := statedb.GetNonce(from)
   105  			if nonce < msg.Nonce() {
   106  				log.Info("ApplyTransactionEx() abort due to nonce too high")
   107  				return nil, 0, ErrNonceTooHigh
   108  			} else if nonce > msg.Nonce() {
   109  				log.Info("ApplyTransactionEx() abort due to nonce too low")
   110  				return nil, 0, ErrNonceTooLow
   111  			}
   112  		}
   113  
   114  		// pre-buy gas according to the gas limit
   115  		gasLimit := tx.Gas()
   116  		gasValue := new(big.Int).Mul(new(big.Int).SetUint64(gasLimit), tx.GasPrice())
   117  		if statedb.GetBalance(from).Cmp(gasValue) < 0 {
   118  			return nil, 0, fmt.Errorf("insufficient NEAT for gas (%x). Req %v, has %v", from.Bytes()[:4], gasValue, statedb.GetBalance(from))
   119  		}
   120  		if err := gp.SubGas(gasLimit); err != nil {
   121  			return nil, 0, err
   122  		}
   123  		statedb.SubBalance(from, gasValue)
   124  		//log.Infof("ApplyTransactionEx() 1, gas is %v, gasPrice is %v, gasValue is %v\n", gasLimit, tx.GasPrice(), gasValue)
   125  
   126  		// use gas
   127  		gas := function.RequiredGas()
   128  		if gasLimit < gas {
   129  			return nil, 0, vm.ErrOutOfGas
   130  		}
   131  
   132  		// Check Tx Amount
   133  		if statedb.GetBalance(from).Cmp(tx.Value()) == -1 {
   134  			return nil, 0, fmt.Errorf("insufficient NEAT for tx amount (%x). Req %v, has %v", from.Bytes()[:4], tx.Value(), statedb.GetBalance(from))
   135  		}
   136  
   137  		if applyCb := GetApplyCb(function); applyCb != nil {
   138  			if function.IsCrossChainType() {
   139  				if fn, ok := applyCb.(CrossChainApplyCb); ok {
   140  					cch.GetMutex().Lock()
   141  					err := fn(tx, statedb, ops, cch, mining)
   142  					cch.GetMutex().Unlock()
   143  
   144  					if err != nil {
   145  						return nil, 0, err
   146  					}
   147  				} else {
   148  					panic("callback func is wrong, this should not happened, please check the code")
   149  				}
   150  			} else {
   151  				if fn, ok := applyCb.(NonCrossChainApplyCb); ok {
   152  					if err := fn(tx, statedb, bc, ops); err != nil {
   153  						return nil, 0, err
   154  					}
   155  				} else {
   156  					panic("callback func is wrong, this should not happened, please check the code")
   157  				}
   158  			}
   159  		}
   160  
   161  		// refund gas
   162  		remainingGas := gasLimit - gas
   163  		remaining := new(big.Int).Mul(new(big.Int).SetUint64(remainingGas), tx.GasPrice())
   164  		statedb.AddBalance(from, remaining)
   165  		gp.AddGas(remainingGas)
   166  
   167  		*usedGas += gas
   168  		totalUsedMoney.Add(totalUsedMoney, new(big.Int).Mul(new(big.Int).SetUint64(gas), tx.GasPrice()))
   169  		log.Infof("ApplyTransactionEx() 2, totalUsedMoney is %v\n", totalUsedMoney)
   170  
   171  		// Update the state with pending changes
   172  		var root []byte
   173  		if config.IsByzantium(header.Number) {
   174  			statedb.Finalise(true)
   175  		} else {
   176  			root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
   177  		}
   178  		receipt := types.NewReceipt(root, false, *usedGas)
   179  		receipt.TxHash = tx.Hash()
   180  		receipt.GasUsed = gas
   181  
   182  		// Set the receipt logs and create a bloom for filtering
   183  		receipt.Logs = statedb.GetLogs(tx.Hash())
   184  		receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
   185  		receipt.BlockHash = statedb.BlockHash()
   186  		receipt.BlockNumber = header.Number
   187  		receipt.TransactionIndex = uint(statedb.TxIndex())
   188  
   189  		statedb.SetNonce(msg.From(), statedb.GetNonce(msg.From())+1)
   190  		//log.Infof("ApplyTransactionEx() 3, totalUsedMoney is %v\n", totalUsedMoney)
   191  
   192  		return receipt, 0, nil
   193  	}
   194  }