github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/forest/leveled_forest.go (about) 1 package forest 2 3 import ( 4 "github.com/onflow/flow-go/model/flow" 5 "github.com/onflow/flow-go/module/mempool" 6 ) 7 8 // LevelledForest contains multiple trees (which is a potentially disconnected planar graph). 9 // Each vertex in the graph has a level and a hash. A vertex can only have one parent, which 10 // must have strictly smaller level. A vertex can have multiple children, all with strictly 11 // larger level. 12 // A LevelledForest provides the ability to prune all vertices up to a specific level. 13 // A tree whose root is below the pruning threshold might decompose into multiple 14 // disconnected subtrees as a result of pruning. 15 // By design, the LevelledForest does _not_ touch the parent information for vertices 16 // that are on the lowest retained level. Thereby, it is possible to initialize the 17 // LevelledForest with a root vertex at the lowest retained level, without this root 18 // needing to have a parent. Furthermore, the root vertex can be at level 0 and in 19 // absence of a parent still satisfy the condition that any parent must be of lower level 20 // (mathematical principle of vacuous truth) without the implementation needing to worry 21 // about unsigned integer underflow. 22 // 23 // LevelledForest is NOT safe for concurrent use by multiple goroutines. 24 type LevelledForest struct { 25 vertices VertexSet 26 verticesAtLevel map[uint64]VertexList 27 size uint64 28 LowestLevel uint64 29 } 30 31 type VertexList []*vertexContainer 32 type VertexSet map[flow.Identifier]*vertexContainer 33 34 // vertexContainer holds information about a tree vertex. Internally, we distinguish between 35 // - FULL container: has non-nil value for vertex. 36 // Used for vertices, which have been added to the tree. 37 // - EMPTY container: has NIL value for vertex. 38 // Used for vertices, which have NOT been added to the tree, but are 39 // referenced by vertices in the tree. An empty container is converted to a 40 // full container when the respective vertex is added to the tree 41 type vertexContainer struct { 42 id flow.Identifier 43 level uint64 44 children VertexList 45 46 // the following are only set if the block is actually known 47 vertex Vertex 48 } 49 50 // NewLevelledForest initializes a LevelledForest 51 func NewLevelledForest(lowestLevel uint64) *LevelledForest { 52 return &LevelledForest{ 53 vertices: make(VertexSet), 54 verticesAtLevel: make(map[uint64]VertexList), 55 LowestLevel: lowestLevel, 56 } 57 } 58 59 // PruneUpToLevel prunes all blocks UP TO but NOT INCLUDING `level`. 60 // Error returns: 61 // * mempool.BelowPrunedThresholdError if input level is below the lowest retained level 62 func (f *LevelledForest) PruneUpToLevel(level uint64) error { 63 if level < f.LowestLevel { 64 return mempool.NewBelowPrunedThresholdErrorf("new lowest level %d cannot be smaller than previous last retained level %d", level, f.LowestLevel) 65 } 66 if len(f.vertices) == 0 { 67 f.LowestLevel = level 68 return nil 69 } 70 71 elementsPruned := 0 72 73 // to optimize the pruning large level-ranges, we compare: 74 // * the number of levels for which we have stored vertex containers: len(f.verticesAtLevel) 75 // * the number of levels that need to be pruned: level-f.LowestLevel 76 // We iterate over the dimension which is smaller. 77 if uint64(len(f.verticesAtLevel)) < level-f.LowestLevel { 78 for l, vertices := range f.verticesAtLevel { 79 if l < level { 80 for _, v := range vertices { 81 if !f.isEmptyContainer(v) { 82 elementsPruned++ 83 } 84 delete(f.vertices, v.id) 85 } 86 delete(f.verticesAtLevel, l) 87 } 88 } 89 } else { 90 for l := f.LowestLevel; l < level; l++ { 91 verticesAtLevel := f.verticesAtLevel[l] 92 for _, v := range verticesAtLevel { // nil map behaves like empty map when iterating over it 93 if !f.isEmptyContainer(v) { 94 elementsPruned++ 95 } 96 delete(f.vertices, v.id) 97 } 98 delete(f.verticesAtLevel, l) 99 100 } 101 } 102 f.LowestLevel = level 103 f.size -= uint64(elementsPruned) 104 return nil 105 } 106 107 // HasVertex returns true iff full vertex exists. 108 func (f *LevelledForest) HasVertex(id flow.Identifier) bool { 109 container, exists := f.vertices[id] 110 return exists && !f.isEmptyContainer(container) 111 } 112 113 // isEmptyContainer returns true iff vertexContainer container is empty, i.e. full vertex itself has not been added 114 func (f *LevelledForest) isEmptyContainer(vertexContainer *vertexContainer) bool { 115 return vertexContainer.vertex == nil 116 } 117 118 // GetVertex returns (<full vertex>, true) if the vertex with `id` and `level` was found 119 // (nil, false) if full vertex is unknown 120 func (f *LevelledForest) GetVertex(id flow.Identifier) (Vertex, bool) { 121 container, exists := f.vertices[id] 122 if !exists || f.isEmptyContainer(container) { 123 return nil, false 124 } 125 return container.vertex, true 126 } 127 128 // GetSize returns the total number of vertices above the lowest pruned level. 129 // Note this call is not concurrent-safe, caller is responsible to ensure concurrency safety. 130 func (f *LevelledForest) GetSize() uint64 { 131 return f.size 132 } 133 134 // GetChildren returns a VertexIterator to iterate over the children 135 // An empty VertexIterator is returned, if no vertices are known whose parent is `id`. 136 func (f *LevelledForest) GetChildren(id flow.Identifier) VertexIterator { 137 // if vertex does not exist, container will be nil 138 if container, ok := f.vertices[id]; ok { 139 return newVertexIterator(container.children) 140 } 141 return newVertexIterator(nil) // VertexIterator gracefully handles nil slices 142 } 143 144 // GetNumberOfChildren returns number of children of given vertex 145 func (f *LevelledForest) GetNumberOfChildren(id flow.Identifier) int { 146 container := f.vertices[id] // if vertex does not exist, container is the default zero value for vertexContainer, which contains a nil-slice for its children 147 num := 0 148 for _, child := range container.children { 149 if child.vertex != nil { 150 num++ 151 } 152 } 153 return num 154 } 155 156 // GetVerticesAtLevel returns a VertexIterator to iterate over the Vertices at the specified level. 157 // An empty VertexIterator is returned, if no vertices are known at the specified level. 158 // If `level` is already pruned, an empty VertexIterator is returned. 159 func (f *LevelledForest) GetVerticesAtLevel(level uint64) VertexIterator { 160 return newVertexIterator(f.verticesAtLevel[level]) // go returns the zero value for a missing level. Here, a nil slice 161 } 162 163 // GetNumberOfVerticesAtLevel returns the number of full vertices at given level. 164 // A full vertex is a vertex that was explicitly added to the forest. In contrast, 165 // an empty vertex container represents a vertex that is _referenced_ as parent by 166 // one or more full vertices, but has not been added itself to the forest. 167 // We only count vertices that have been explicitly added to the forest and not yet 168 // pruned. (In comparision, we do _not_ count vertices that are _referenced_ as 169 // parent by vertices, but have not been added themselves). 170 func (f *LevelledForest) GetNumberOfVerticesAtLevel(level uint64) int { 171 num := 0 172 for _, container := range f.verticesAtLevel[level] { 173 if !f.isEmptyContainer(container) { 174 num++ 175 } 176 } 177 return num 178 } 179 180 // AddVertex adds vertex to forest if vertex is within non-pruned levels 181 // Handles repeated addition of same vertex (keeps first added vertex). 182 // If vertex is at or below pruning level: method is NoOp. 183 // UNVALIDATED: 184 // requires that vertex would pass validity check LevelledForest.VerifyVertex(vertex). 185 func (f *LevelledForest) AddVertex(vertex Vertex) { 186 if vertex.Level() < f.LowestLevel { 187 return 188 } 189 container := f.getOrCreateVertexContainer(vertex.VertexID(), vertex.Level()) 190 if !f.isEmptyContainer(container) { // the vertex was already stored 191 return 192 } 193 // container is empty, i.e. full vertex is new and should be stored in container 194 container.vertex = vertex // add vertex to container 195 f.registerWithParent(container) 196 f.size += 1 197 } 198 199 // registerWithParent retrieves the parent and registers the given vertex as a child. 200 // For a block, whose level equal to the pruning threshold, we do not inspect the parent at all. 201 // Thereby, this implementation can gracefully handle the corner case where the tree has a defined 202 // end vertex (distinct root). This is commonly the case in blockchain (genesis, or spork root block). 203 // Mathematically, this means that this library can also represent bounded trees. 204 func (f *LevelledForest) registerWithParent(vertexContainer *vertexContainer) { 205 // caution, necessary for handling bounded trees: 206 // For root vertex (genesis block) the view is _exactly_ at LowestLevel. For these blocks, 207 // a parent does not exist. In the implementation, we deliberately do not call the `Parent()` method, 208 // as its output is conceptually undefined. Thereby, we can gracefully handle the corner case of 209 // vertex.level = vertex.Parent().Level = LowestLevel = 0 210 if vertexContainer.level <= f.LowestLevel { // check (a) 211 return 212 } 213 214 _, parentView := vertexContainer.vertex.Parent() 215 if parentView < f.LowestLevel { 216 return 217 } 218 parentContainer := f.getOrCreateVertexContainer(vertexContainer.vertex.Parent()) 219 parentContainer.children = append(parentContainer.children, vertexContainer) // append works on nil slices: creates slice with capacity 2 220 } 221 222 // getOrCreateVertexContainer returns the vertexContainer if there exists one 223 // or creates a new vertexContainer and adds it to the internal data structures. 224 // (i.e. there exists an empty or full container with the same id but different level). 225 func (f *LevelledForest) getOrCreateVertexContainer(id flow.Identifier, level uint64) *vertexContainer { 226 container, exists := f.vertices[id] // try to find vertex container with same ID 227 if !exists { // if no vertex container found, create one and store it 228 container = &vertexContainer{ 229 id: id, 230 level: level, 231 } 232 f.vertices[container.id] = container 233 vertices := f.verticesAtLevel[container.level] // returns nil slice if not yet present 234 f.verticesAtLevel[container.level] = append(vertices, container) // append works on nil slices: creates slice with capacity 2 235 } 236 return container 237 } 238 239 // VerifyVertex verifies that adding vertex `v` would yield a valid Levelled Forest. 240 // Specifically, we verify that _all_ of the following conditions are satisfied: 241 // 242 // 1. `v.Level()` must be strictly larger than the level that `v` reports 243 // for its parent (maintains an acyclic graph). 244 // 245 // 2. If a vertex with the same ID as `v.VertexID()` exists in the graph or is 246 // referenced by another vertex within the graph, the level must be identical. 247 // (In other words, we don't have vertices with the same ID but different level) 248 // 249 // 3. Let `ParentLevel`, `ParentID` denote the level, ID that `v` reports for its parent. 250 // If a vertex with `ParentID` exists (or is referenced by other vertices as their parent), 251 // we require that the respective level is identical to `ParentLevel`. 252 // 253 // Notes: 254 // - If `v.Level()` has already been pruned, adding it to the forest is a NoOp. 255 // Hence, any vertex with level below the pruning threshold automatically passes. 256 // - By design, the LevelledForest does _not_ touch the parent information for vertices 257 // that are on the lowest retained level. Thereby, it is possible to initialize the 258 // LevelledForest with a root vertex at the lowest retained level, without this root 259 // needing to have a parent. Furthermore, the root vertex can be at level 0 and in 260 // absence of a parent still satisfy the condition that any parent must be of lower level 261 // (mathematical principle of vacuous truth) without the implementation needing to worry 262 // about unsigned integer underflow. 263 // 264 // Error returns: 265 // * InvalidVertexError if the input vertex is invalid for insertion to the forest. 266 func (f *LevelledForest) VerifyVertex(v Vertex) error { 267 if v.Level() < f.LowestLevel { 268 return nil 269 } 270 271 storedContainer, haveVertexContainer := f.vertices[v.VertexID()] 272 if !haveVertexContainer { // have no vertex with same id stored 273 // the only thing remaining to check is the parent information 274 return f.ensureConsistentParent(v) 275 } 276 277 // Found a vertex container, i.e. `v` already exists, or it is referenced by some other vertex. 278 // In all cases, `v.Level()` should match the vertexContainer's information 279 if v.Level() != storedContainer.level { 280 return NewInvalidVertexErrorf(v, "level conflicts with stored vertex with same id (%d!=%d)", v.Level(), storedContainer.level) 281 } 282 283 // vertex container is empty, i.e. `v` is referenced by some other vertex as its parent: 284 if f.isEmptyContainer(storedContainer) { 285 // the only thing remaining to check is the parent information 286 return f.ensureConsistentParent(v) 287 } 288 289 // vertex container holds a vertex with the same ID as `v`: 290 // The parent information from vertexContainer has already been checked for consistency. So 291 // we simply compare with the existing vertex for inconsistencies 292 293 // the vertex is at or below the lowest retained level, so we can't check the parent (it's pruned) 294 if v.Level() == f.LowestLevel { 295 return nil 296 } 297 298 newParentId, newParentLevel := v.Parent() 299 storedParentId, storedParentLevel := storedContainer.vertex.Parent() 300 if newParentId != storedParentId { 301 return NewInvalidVertexErrorf(v, "parent ID conflicts with stored parent (%x!=%x)", newParentId, storedParentId) 302 } 303 if newParentLevel != storedParentLevel { 304 return NewInvalidVertexErrorf(v, "parent level conflicts with stored parent (%d!=%d)", newParentLevel, storedParentLevel) 305 } 306 // all _relevant_ fields identical 307 return nil 308 } 309 310 // ensureConsistentParent verifies that vertex.Parent() is consistent with current forest. 311 // Returns InvalidVertexError if: 312 // * there is a parent with the same ID but different level; 313 // * the parent's level is _not_ smaller than the vertex's level 314 func (f *LevelledForest) ensureConsistentParent(vertex Vertex) error { 315 if vertex.Level() <= f.LowestLevel { 316 // the vertex is at or below the lowest retained level, so we can't check the parent (it's pruned) 317 return nil 318 } 319 320 // verify parent 321 parentID, parentLevel := vertex.Parent() 322 if !(vertex.Level() > parentLevel) { 323 return NewInvalidVertexErrorf(vertex, "vertex parent level (%d) must be smaller than proposed vertex level (%d)", parentLevel, vertex.Level()) 324 } 325 storedParent, haveParentStored := f.GetVertex(parentID) 326 if !haveParentStored { 327 return nil 328 } 329 if storedParent.Level() != parentLevel { 330 return NewInvalidVertexErrorf(vertex, "parent level conflicts with stored parent (%d!=%d)", parentLevel, storedParent.Level()) 331 } 332 return nil 333 }