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 }