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