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 }