github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/types/result.go (about)

     1  package types
     2  
     3  import (
     4  	gethCommon "github.com/onflow/go-ethereum/common"
     5  	gethTypes "github.com/onflow/go-ethereum/core/types"
     6  )
     7  
     8  // InvalidTransactionGasCost is a gas cost we charge when
     9  // a transaction or call fails at validation step.
    10  // in typical evm environment this doesn't exist given
    11  // if a transaction is invalid it won't be included
    12  // and no fees can be charged for users even though
    13  // the validation has used some resources, in our case
    14  // given we charge the fees on flow transaction and we
    15  // are doing on chain validation we can/should charge the
    16  // user for the validation fee.
    17  const InvalidTransactionGasCost = 1_000
    18  
    19  // Status captures the status of an interaction to the emulator
    20  type Status uint8
    21  
    22  var (
    23  	StatusUnknown Status = 0
    24  	// StatusInvalid shows that the transaction was not a valid
    25  	// transaction and rejected to be executed and included in any block.
    26  	StatusInvalid Status = 1
    27  	// StatusFailed shows that the transaction has been executed,
    28  	// but the output of the execution was an error
    29  	// for this case a block is formed and receipts are available
    30  	StatusFailed Status = 2
    31  	// StatusSuccessful shows that the transaction has been executed and the execution has returned success
    32  	// for this case a block is formed and receipts are available
    33  	StatusSuccessful Status = 3
    34  )
    35  
    36  // ResultSummary summerizes the outcome of a EVM call or tx run
    37  type ResultSummary struct {
    38  	Status                  Status
    39  	ErrorCode               ErrorCode
    40  	GasConsumed             uint64
    41  	DeployedContractAddress *Address
    42  	ReturnedValue           Data
    43  }
    44  
    45  // NewInvalidResult creates a new result that hold transaction validation
    46  // error as well as the defined gas cost for validation.
    47  func NewInvalidResult(tx *gethTypes.Transaction, err error) *Result {
    48  	return &Result{
    49  		TxType:          tx.Type(),
    50  		TxHash:          tx.Hash(),
    51  		ValidationError: err,
    52  		GasConsumed:     InvalidTransactionGasCost,
    53  	}
    54  }
    55  
    56  // Result captures the result of an interaction to the emulator
    57  // it could be the output of a direct call or output of running an
    58  // evm transaction.
    59  // Its more comprehensive than typical evm receipt, usually
    60  // the receipt generation requires some extra calculation (e.g. Deployed contract address)
    61  // but we take a different apporach here and include more data so that
    62  // it requires less work for anyone who tracks and consume results.
    63  type Result struct {
    64  	// captures error returned during validation step (pre-checks)
    65  	ValidationError error
    66  	// captures error returned by the EVM
    67  	VMError error
    68  	// type of transaction defined by the evm package
    69  	// see DirectCallTxType as extra type we added type for direct calls.
    70  	TxType uint8
    71  	// total gas consumed during an opeartion
    72  	GasConsumed uint64
    73  	// the address where the contract is deployed (if any)
    74  	DeployedContractAddress *Address
    75  	// returned value from a function call
    76  	ReturnedValue []byte
    77  	// EVM logs (events that are emited by evm)
    78  	Logs []*gethTypes.Log
    79  	// TX hash holdes the cached value of tx hash
    80  	TxHash gethCommon.Hash
    81  	// transaction block inclusion index
    82  	Index uint16
    83  }
    84  
    85  // Invalid returns true if transaction has been rejected
    86  func (res *Result) Invalid() bool {
    87  	return res.ValidationError != nil
    88  }
    89  
    90  // Failed returns true if transaction has been executed but VM has returned some error
    91  func (res *Result) Failed() bool {
    92  	return res.VMError != nil
    93  }
    94  
    95  // SetValidationError sets the validation error
    96  // and also sets the gas used to the fixed invalid gas usage
    97  func (res *Result) SetValidationError(err error) {
    98  	res.ValidationError = err
    99  	res.GasConsumed = InvalidTransactionGasCost
   100  }
   101  
   102  // returns the VM error as an string, if no error it returns an empty string
   103  func (res *Result) VMErrorString() string {
   104  	if res.VMError != nil {
   105  		return res.VMError.Error()
   106  	}
   107  	return ""
   108  }
   109  
   110  // Receipt constructs an EVM-style receipt
   111  // can be used by json-rpc and other integration to be returned.
   112  //
   113  // This is method is also used to construct block receipt root hash
   114  // which requires the return receipt satisfy RLP encoding and cover these feilds
   115  // Type (txType), PostState or Status, CumulativeGasUsed, Logs and Logs Bloom
   116  // and for each log, Address, Topics, Data (consensus fields)
   117  // During execution we also do fill in BlockNumber, TxIndex, Index (event index)
   118  func (res *Result) Receipt() *gethTypes.Receipt {
   119  	if res.Invalid() {
   120  		return nil
   121  	}
   122  	receipt := &gethTypes.Receipt{
   123  		Type:              res.TxType,
   124  		CumulativeGasUsed: res.GasConsumed, // TODO: update to capture cumulative
   125  		Logs:              res.Logs,
   126  	}
   127  	if res.DeployedContractAddress != nil {
   128  		receipt.ContractAddress = res.DeployedContractAddress.ToCommon()
   129  	}
   130  	if res.Failed() {
   131  		receipt.Status = gethTypes.ReceiptStatusFailed
   132  	} else {
   133  		receipt.Status = gethTypes.ReceiptStatusSuccessful
   134  	}
   135  
   136  	receipt.Bloom = gethTypes.CreateBloom(gethTypes.Receipts{receipt})
   137  	return receipt
   138  }
   139  
   140  // ResultSummary constructs a result summary
   141  func (res *Result) ResultSummary() *ResultSummary {
   142  	rs := &ResultSummary{
   143  		GasConsumed:             res.GasConsumed,
   144  		DeployedContractAddress: res.DeployedContractAddress,
   145  		ReturnedValue:           res.ReturnedValue,
   146  		Status:                  StatusSuccessful,
   147  	}
   148  
   149  	if res.Invalid() {
   150  		rs.ErrorCode = ValidationErrorCode(res.ValidationError)
   151  		rs.Status = StatusInvalid
   152  		return rs
   153  	}
   154  
   155  	if res.Failed() {
   156  		rs.ErrorCode = ExecutionErrorCode(res.VMError)
   157  		rs.Status = StatusFailed
   158  		return rs
   159  	}
   160  
   161  	return rs
   162  }