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  }