github.com/core-coin/go-core/v2@v2.1.9/core/state_transition.go (about)

     1  // Copyright 2014 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"math/big"
    23  
    24  	"github.com/core-coin/go-core/v2/common"
    25  	"github.com/core-coin/go-core/v2/core/vm"
    26  	"github.com/core-coin/go-core/v2/params"
    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 the necessary work to work out a valid new state root.
    34  
    35  1) Nonce handling
    36  2) Pre pay energy
    37  3) Create a new state object if the recipient is \0*32
    38  4) Value transfer
    39  == If contract creation ==
    40  
    41  	4a) Attempt to run transaction data
    42  	4b) If valid, use result as code for the new state object
    43  
    44  == end ==
    45  5) Run Script section
    46  6) Derive new state root
    47  */
    48  type StateTransition struct {
    49  	gp            *EnergyPool
    50  	msg           Message
    51  	energy        uint64
    52  	energyPrice   *big.Int
    53  	initialEnergy uint64
    54  	value         *big.Int
    55  	data          []byte
    56  	state         vm.StateDB
    57  	cvm           *vm.CVM
    58  }
    59  
    60  // Message represents a message sent to a contract.
    61  type Message interface {
    62  	From() common.Address
    63  	To() *common.Address
    64  
    65  	EnergyPrice() *big.Int
    66  	Energy() uint64
    67  	Value() *big.Int
    68  
    69  	Nonce() uint64
    70  	CheckNonce() bool
    71  	Data() []byte
    72  }
    73  
    74  // ExecutionResult includes all output after executing given cvm
    75  // message no matter the execution itself is successful or not.
    76  type ExecutionResult struct {
    77  	UsedEnergy uint64 // Total used energy but include the refunded energy
    78  	Err        error  // Any error encountered during the execution(listed in core/vm/errors.go)
    79  	ReturnData []byte // Returned data from cvm(function result or data supplied with revert opcode)
    80  }
    81  
    82  // Unwrap returns the internal cvm error which allows us for further
    83  // analysis outside.
    84  func (result *ExecutionResult) Unwrap() error {
    85  	return result.Err
    86  }
    87  
    88  // Failed returns the indicator whether the execution is successful or not
    89  func (result *ExecutionResult) Failed() bool { return result.Err != nil }
    90  
    91  // Return is a helper function to help caller distinguish between revert reason
    92  // and function return. Return returns the data after execution if no error occurs.
    93  func (result *ExecutionResult) Return() []byte {
    94  	if result.Err != nil {
    95  		return nil
    96  	}
    97  	return common.CopyBytes(result.ReturnData)
    98  }
    99  
   100  // Revert returns the concrete revert reason if the execution is aborted by `REVERT`
   101  // opcode. Note the reason can be nil if no data supplied with revert opcode.
   102  func (result *ExecutionResult) Revert() []byte {
   103  	if result.Err != vm.ErrExecutionReverted {
   104  		return nil
   105  	}
   106  	return common.CopyBytes(result.ReturnData)
   107  }
   108  
   109  // IntrinsicEnergy computes the 'intrinsic energy' for a message with the given data.
   110  func IntrinsicEnergy(data []byte, contractCreation bool) (uint64, error) {
   111  	// Set the starting energy for the raw transaction
   112  	var energy uint64
   113  	if contractCreation {
   114  		energy = params.TxEnergyContractCreation
   115  	} else {
   116  		energy = params.TxEnergy
   117  	}
   118  	// Bump the required energy by the amount of transactional data
   119  	if len(data) > 0 {
   120  		// Zero and non-zero bytes are priced differently
   121  		var nz uint64
   122  		for _, byt := range data {
   123  			if byt != 0 {
   124  				nz++
   125  			}
   126  		}
   127  		// Make sure we don't exceed uint64 for all data combinations
   128  		nonZeroEnergy := params.TxDataNonZeroEnergy
   129  		if (math.MaxUint64-energy)/nonZeroEnergy < nz {
   130  			return 0, ErrEnergyUintOverflow
   131  		}
   132  		energy += nz * nonZeroEnergy
   133  
   134  		z := uint64(len(data)) - nz
   135  		if (math.MaxUint64-energy)/params.TxDataZeroEnergy < z {
   136  			return 0, ErrEnergyUintOverflow
   137  		}
   138  		energy += z * params.TxDataZeroEnergy
   139  	}
   140  	return energy, nil
   141  }
   142  
   143  // NewStateTransition initialises and returns a new state transition object.
   144  func NewStateTransition(cvm *vm.CVM, msg Message, gp *EnergyPool) *StateTransition {
   145  	return &StateTransition{
   146  		gp:          gp,
   147  		cvm:         cvm,
   148  		msg:         msg,
   149  		energyPrice: msg.EnergyPrice(),
   150  		value:       msg.Value(),
   151  		data:        msg.Data(),
   152  		state:       cvm.StateDB,
   153  	}
   154  }
   155  
   156  // ApplyMessage computes the new state by applying the given message
   157  // against the old state within the environment.
   158  //
   159  // ApplyMessage returns the bytes returned by any CVM execution (if it took place),
   160  // the energy used (which includes energy refunds) and an error if it failed. An error always
   161  // indicates a core error meaning that the message would always fail for that particular
   162  // state and would never be accepted within a block.
   163  func ApplyMessage(cvm *vm.CVM, msg Message, gp *EnergyPool) (*ExecutionResult, error) {
   164  	return NewStateTransition(cvm, msg, gp).TransitionDb()
   165  }
   166  
   167  // to returns the recipient of the message.
   168  func (st *StateTransition) to() common.Address {
   169  	if st.msg == nil || st.msg.To() == nil /* contract creation */ {
   170  		return common.Address{}
   171  	}
   172  	return *st.msg.To()
   173  }
   174  
   175  func (st *StateTransition) buyEnergy() error {
   176  	mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Energy()), st.energyPrice)
   177  	if have, want := st.state.GetBalance(st.msg.From()), mgval; have.Cmp(want) < 0 {
   178  		return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
   179  	}
   180  	if err := st.gp.SubEnergy(st.msg.Energy()); err != nil {
   181  		return err
   182  	}
   183  	st.energy += st.msg.Energy()
   184  
   185  	st.initialEnergy = st.msg.Energy()
   186  	st.state.SubBalance(st.msg.From(), mgval)
   187  	return nil
   188  }
   189  
   190  func (st *StateTransition) preCheck() error {
   191  	// Make sure this transaction's nonce is correct.
   192  	if st.msg.CheckNonce() {
   193  		stNonce := st.state.GetNonce(st.msg.From())
   194  		if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
   195  			return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
   196  				st.msg.From().Hex(), msgNonce, stNonce)
   197  		} else if stNonce > msgNonce {
   198  			return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
   199  				st.msg.From().Hex(), msgNonce, stNonce)
   200  		}
   201  	}
   202  	return st.buyEnergy()
   203  }
   204  
   205  // TransitionDb will transition the state by applying the current message and
   206  // returning the cvm execution result with following fields.
   207  //
   208  //   - used energy:
   209  //     total energy used (including energy being refunded)
   210  //   - returndata:
   211  //     the returned data from cvm
   212  //   - concrete execution error:
   213  //     various **CVM** error which aborts the execution,
   214  //     e.g. ErrOutOfEnergy, ErrExecutionReverted
   215  //
   216  // However if any consensus issue encountered, return the error directly with
   217  // nil cvm execution result.
   218  func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
   219  	// First check this message satisfies all consensus rules before
   220  	// applying the message. The rules include these clauses
   221  	//
   222  	// 1. the nonce of the message caller is correct
   223  	// 2. caller has enough balance to cover transaction fee(energylimit * energyprice)
   224  	// 3. the amount of energy required is available in the block
   225  	// 4. the purchased energy is enough to cover intrinsic usage
   226  	// 5. there is no overflow when calculating intrinsic energy
   227  	// 6. caller has enough balance to cover asset transfer for **topmost** call
   228  
   229  	// Check clauses 1-3, buy energy if everything is correct
   230  	if err := st.preCheck(); err != nil {
   231  		return nil, err
   232  	}
   233  	msg := st.msg
   234  	sender := vm.AccountRef(msg.From())
   235  	contractCreation := msg.To() == nil
   236  
   237  	// Check clauses 4-5, subtract intrinsic energy if everything is correct
   238  	energy, err := IntrinsicEnergy(st.data, contractCreation)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	if st.energy < energy {
   243  		return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicEnergy, st.energy, energy)
   244  	}
   245  	st.energy -= energy
   246  
   247  	// Check clause 6
   248  	if msg.Value().Sign() > 0 && !st.cvm.Context.CanTransfer(st.state, msg.From(), msg.Value()) {
   249  		return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex())
   250  	}
   251  	var (
   252  		ret   []byte
   253  		vmerr error // vm errors do not effect consensus and are therefore not assigned to err
   254  	)
   255  	if contractCreation {
   256  		ret, _, st.energy, vmerr = st.cvm.Create(sender, st.data, st.energy, st.value)
   257  	} else {
   258  		// Increment the nonce for the next transaction
   259  		st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
   260  		ret, st.energy, vmerr = st.cvm.Call(sender, st.to(), st.data, st.energy, st.value)
   261  	}
   262  	st.refundEnergy()
   263  	st.state.AddBalance(st.cvm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.energyUsed()), st.energyPrice))
   264  
   265  	return &ExecutionResult{
   266  		UsedEnergy: st.energyUsed(),
   267  		Err:        vmerr,
   268  		ReturnData: ret,
   269  	}, nil
   270  }
   271  
   272  func (st *StateTransition) refundEnergy() {
   273  	// Apply refund counter, capped to half of the used energy.
   274  	refund := st.energyUsed() / 2
   275  	if refund > st.state.GetRefund() {
   276  		refund = st.state.GetRefund()
   277  	}
   278  	st.energy += refund
   279  
   280  	// Return XCB for remaining energy, exchanged at the original rate.
   281  	remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.energy), st.energyPrice)
   282  	st.state.AddBalance(st.msg.From(), remaining)
   283  
   284  	// Also return remaining energy to the block energy counter so it is
   285  	// available for the next transaction.
   286  	st.gp.AddEnergy(st.energy)
   287  }
   288  
   289  // energyUsed returns the amount of energy used up by the state transition.
   290  func (st *StateTransition) energyUsed() uint64 {
   291  	return st.initialEnergy - st.energy
   292  }