github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/execution_result.go (about)

     1  package flow
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  )
     7  
     8  var ErrNoChunks = errors.New("execution result has no chunks")
     9  
    10  // ExecutionResult is cryptographic commitment to the computation
    11  // result(s) from executing a block
    12  type ExecutionResult struct {
    13  	PreviousResultID Identifier // commit of the previous ER
    14  	BlockID          Identifier // commit of the current block
    15  	Chunks           ChunkList
    16  	ServiceEvents    ServiceEventList
    17  	ExecutionDataID  Identifier // hash commitment to flow.BlockExecutionDataRoot
    18  }
    19  
    20  func NewExecutionResult(
    21  	previousResultID Identifier,
    22  	blockID Identifier,
    23  	chunks ChunkList,
    24  	serviceEvents ServiceEventList,
    25  	executionDataID Identifier,
    26  ) *ExecutionResult {
    27  	return &ExecutionResult{
    28  		PreviousResultID: previousResultID,
    29  		BlockID:          blockID,
    30  		Chunks:           chunks,
    31  		ServiceEvents:    serviceEvents,
    32  		ExecutionDataID:  executionDataID,
    33  	}
    34  }
    35  
    36  // ID returns the hash of the execution result body
    37  func (er ExecutionResult) ID() Identifier {
    38  	return MakeID(er)
    39  }
    40  
    41  // Checksum ...
    42  func (er ExecutionResult) Checksum() Identifier {
    43  	return MakeID(er)
    44  }
    45  
    46  // ValidateChunksLength checks whether the number of chuncks is zero.
    47  //
    48  // It returns false if the number of chunks is zero (invalid).
    49  // By protocol definition, each ExecutionReceipt must contain at least one
    50  // chunk (system chunk).
    51  func (er ExecutionResult) ValidateChunksLength() bool {
    52  	return er.Chunks.Len() != 0
    53  }
    54  
    55  // FinalStateCommitment returns the Execution Result's commitment to the final
    56  // execution state of the block, i.e. the last chunk's output state.
    57  // Error returns:
    58  //   - ErrNoChunks: if there are no chunks (ExecutionResult is malformed)
    59  func (er ExecutionResult) FinalStateCommitment() (StateCommitment, error) {
    60  	if !er.ValidateChunksLength() {
    61  		return DummyStateCommitment, ErrNoChunks
    62  	}
    63  	return er.Chunks[er.Chunks.Len()-1].EndState, nil
    64  }
    65  
    66  // InitialStateCommit returns a commitment to the execution state used as input
    67  // for computing the block, i.e. the leading chunk's input state.
    68  // Error returns:
    69  //   - ErrNoChunks: if there are no chunks (ExecutionResult is malformed)
    70  func (er ExecutionResult) InitialStateCommit() (StateCommitment, error) {
    71  	if !er.ValidateChunksLength() {
    72  		return DummyStateCommitment, ErrNoChunks
    73  	}
    74  	return er.Chunks[0].StartState, nil
    75  }
    76  
    77  func (er ExecutionResult) MarshalJSON() ([]byte, error) {
    78  	type Alias ExecutionResult
    79  	return json.Marshal(struct {
    80  		Alias
    81  		ID string
    82  	}{
    83  		Alias: Alias(er),
    84  		ID:    er.ID().String(),
    85  	})
    86  }
    87  
    88  /*******************************************************************************
    89  GROUPING allows to split a list of results by some property
    90  *******************************************************************************/
    91  
    92  // ExecutionResultList is a slice of ExecutionResults with the additional
    93  // functionality to group them by various properties
    94  type ExecutionResultList []*ExecutionResult
    95  
    96  // ExecutionResultGroupedList is a partition of an ExecutionResultList
    97  type ExecutionResultGroupedList map[Identifier]ExecutionResultList
    98  
    99  // ExecutionResultGroupingFunction is a function that assigns an identifier to each ExecutionResult
   100  type ExecutionResultGroupingFunction func(*ExecutionResult) Identifier
   101  
   102  // GroupBy partitions the ExecutionResultList. All ExecutionResults that are
   103  // mapped by the grouping function to the same identifier are placed in the same group.
   104  // Within each group, the order and multiplicity of the ExecutionResults is preserved.
   105  func (l ExecutionResultList) GroupBy(grouper ExecutionResultGroupingFunction) ExecutionResultGroupedList {
   106  	groups := make(map[Identifier]ExecutionResultList)
   107  	for _, r := range l {
   108  		groupID := grouper(r)
   109  		groups[groupID] = append(groups[groupID], r)
   110  	}
   111  	return groups
   112  }
   113  
   114  // GroupByPreviousResultID partitions the ExecutionResultList by the their PreviousResultIDs.
   115  // Within each group, the order and multiplicity of the ExecutionResults is preserved.
   116  func (l ExecutionResultList) GroupByPreviousResultID() ExecutionResultGroupedList {
   117  	grouper := func(r *ExecutionResult) Identifier { return r.PreviousResultID }
   118  	return l.GroupBy(grouper)
   119  }
   120  
   121  // GroupByExecutedBlockID partitions the ExecutionResultList by the IDs of the executed blocks.
   122  // Within each group, the order and multiplicity of the ExecutionResults is preserved.
   123  func (l ExecutionResultList) GroupByExecutedBlockID() ExecutionResultGroupedList {
   124  	grouper := func(r *ExecutionResult) Identifier { return r.BlockID }
   125  	return l.GroupBy(grouper)
   126  }
   127  
   128  // Size returns the number of ExecutionResults in the list
   129  func (l ExecutionResultList) Size() int {
   130  	return len(l)
   131  }
   132  
   133  // GetGroup returns the ExecutionResults that were mapped to the same identifier by the
   134  // grouping function. Returns an empty (nil) ExecutionResultList if groupID does not exist.
   135  func (g ExecutionResultGroupedList) GetGroup(groupID Identifier) ExecutionResultList {
   136  	return g[groupID]
   137  }
   138  
   139  // NumberGroups returns the number of groups
   140  func (g ExecutionResultGroupedList) NumberGroups() int {
   141  	return len(g)
   142  }
   143  
   144  // Lookup generates a map from ExecutionResult ID to ExecutionResult
   145  func (l ExecutionResultList) Lookup() map[Identifier]*ExecutionResult {
   146  	resultsByID := make(map[Identifier]*ExecutionResult, len(l))
   147  	for _, result := range l {
   148  		resultsByID[result.ID()] = result
   149  	}
   150  	return resultsByID
   151  }