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