-
Notifications
You must be signed in to change notification settings - Fork 177
/
chunk.go
191 lines (165 loc) · 6.39 KB
/
chunk.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package flow
import (
"github.com/ipfs/go-cid"
)
type ChunkBody struct {
CollectionIndex uint
// execution info
StartState StateCommitment // start state when starting executing this chunk
EventCollection Identifier // Events generated by executing results
BlockID Identifier // Block id of the execution result this chunk belongs to
// Computation consumption info
TotalComputationUsed uint64 // total amount of computation used by running all txs in this chunk
NumberOfTransactions uint64 // number of transactions inside the collection
}
type Chunk struct {
ChunkBody
Index uint64 // chunk index inside the ER (starts from zero)
// EndState inferred from next chunk or from the ER
EndState StateCommitment
}
func NewChunk(
blockID Identifier,
collectionIndex int,
startState StateCommitment,
numberOfTransactions int,
eventCollection Identifier,
endState StateCommitment,
totalComputationUsed uint64,
) *Chunk {
return &Chunk{
ChunkBody: ChunkBody{
BlockID: blockID,
CollectionIndex: uint(collectionIndex),
StartState: startState,
NumberOfTransactions: uint64(numberOfTransactions),
EventCollection: eventCollection,
TotalComputationUsed: totalComputationUsed,
},
Index: uint64(collectionIndex),
EndState: endState,
}
}
// ID returns a unique id for this entity
func (ch *Chunk) ID() Identifier {
return MakeID(ch.ChunkBody)
}
// Checksum provides a cryptographic commitment for a chunk content
func (ch *Chunk) Checksum() Identifier {
return MakeID(ch)
}
// ChunkDataPack holds all register touches (any read, or write).
//
// Note that we have to include merkle paths as storage proof for all registers touched (read or written) for
// the _starting_ state of the chunk (i.e. before the chunk computation updates the registers).
// For instance, if an execution state contains three registers: { A: 1, B: 2, C: 3}, and a certain
// chunk has a tx that assigns A = A + B, then its chunk data pack should include the merkle
// paths for { A: 1, B: 2 } as storage proof.
// C is not included because it's neither read or written by the chunk.
// B is included because it's read by the chunk.
// A is included because it's updated by the chunk, and its value 1 is included because it's
// the value before the chunk computation.
// This is necessary for Verification Nodes to (i) check that the read register values are
// consistent with the starting state's root hash and (ii) verify the correctness of the resulting
// state after the chunk computation. `Proof` includes merkle proofs for all touched registers
// during the execution of the chunk.
// Register proofs order must not be correlated to the order of register reads during
// the chunk execution in order to enforce the SPoCK secret high entropy.
type ChunkDataPack struct {
ChunkID Identifier // ID of the chunk this data pack is for
StartState StateCommitment // commitment for starting state
Proof StorageProof // proof for all registers touched (read or written) during the chunk execution
Collection *Collection // collection executed in this chunk
// ExecutionDataRoot is the root data structure of an execution_data.BlockExecutionData.
// It contains the necessary information for a verification node to validate that the
// BlockExecutionData produced is valid.
ExecutionDataRoot BlockExecutionDataRoot
}
// NewChunkDataPack returns an initialized chunk data pack.
func NewChunkDataPack(
chunkID Identifier,
startState StateCommitment,
proof StorageProof,
collection *Collection,
execDataRoot BlockExecutionDataRoot,
) *ChunkDataPack {
return &ChunkDataPack{
ChunkID: chunkID,
StartState: startState,
Proof: proof,
Collection: collection,
ExecutionDataRoot: execDataRoot,
}
}
// ID returns the unique identifier for the concrete view, which is the ID of
// the chunk the view is for.
func (c *ChunkDataPack) ID() Identifier {
return c.ChunkID
}
// Checksum returns the checksum of the chunk data pack.
func (c *ChunkDataPack) Checksum() Identifier {
return MakeID(c)
}
// TODO: This is the basic version of the list, we need to substitute it with something like Merkle tree at some point
type ChunkList []*Chunk
func (cl ChunkList) Fingerprint() Identifier {
return MerkleRoot(GetIDs(cl)...)
}
func (cl *ChunkList) Insert(ch *Chunk) {
*cl = append(*cl, ch)
}
func (cl ChunkList) Items() []*Chunk {
return cl
}
// Empty returns true if the chunk list is empty. Otherwise it returns false.
func (cl ChunkList) Empty() bool {
return len(cl) == 0
}
func (cl ChunkList) Indices() []uint64 {
indices := make([]uint64, len(cl))
for i, chunk := range cl {
indices[i] = chunk.Index
}
return indices
}
// ByChecksum returns an entity from the list by entity fingerprint
func (cl ChunkList) ByChecksum(cs Identifier) (*Chunk, bool) {
for _, ch := range cl {
if ch.Checksum() == cs {
return ch, true
}
}
return nil, false
}
// ByIndex returns an entity from the list by index
// if requested chunk is within range of list, it returns chunk and true
// if requested chunk is out of the range, it returns nil and false
// boolean return value indicates whether requested chunk is within range
func (cl ChunkList) ByIndex(i uint64) (*Chunk, bool) {
if i >= uint64(len(cl)) {
// index out of range
return nil, false
}
return cl[i], true
}
// Len returns the number of Chunks in the list. It is also part of the sort
// interface that makes ChunkList sortable
func (cl ChunkList) Len() int {
return len(cl)
}
// BlockExecutionDataRoot represents the root of a serialized execution_data.BlockExecutionData.
// The hash of the serialized BlockExecutionDataRoot is the ExecutionDataID used within an
// flow.ExecutionResult.
// Context:
// - The trie updates in BlockExecutionDataRoot contain the _mutated_ registers only, which is
// helpful for clients to truslessly replicate the state.
// - In comparison, the chunk data packs contains all the register values at the chunk's starting
// state that were _touched_ (written and/or read). This is necessary for Verification Nodes to
// re-run the chunk the computation.
type BlockExecutionDataRoot struct {
// BlockID is the ID of the block, whose result this execution data is for.
BlockID Identifier
// ChunkExecutionDataIDs is a list of the root CIDs for each serialized execution_data.ChunkExecutionData
// associated with this block.
ChunkExecutionDataIDs []cid.Cid
}