github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/forest/leveled_forest_test.go (about)

     1  package forest
     2  
     3  import (
     4  	"strconv"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/module/forest/mock"
    12  	"github.com/onflow/flow-go/module/mempool"
    13  )
    14  
    15  // ~~~~~~~~~~~~~~~~~~~~~ Mock implementation for Vertex ~~~~~~~~~~~~~~~~~~~~~ //
    16  type VertexMock struct {
    17  	id    flow.Identifier
    18  	level uint64
    19  
    20  	parentId    flow.Identifier
    21  	parentLevel uint64
    22  }
    23  
    24  func (v *VertexMock) VertexID() flow.Identifier         { return v.id }
    25  func (v *VertexMock) Level() uint64                     { return v.level }
    26  func (v *VertexMock) Parent() (flow.Identifier, uint64) { return v.parentId, v.parentLevel }
    27  
    28  func NewVertexMock(vertexId string, vertexLevel uint64, parentId string, parentLevel uint64) *mock.Vertex {
    29  	v := &mock.Vertex{}
    30  	v.On("VertexID").Return(string2Identifier(vertexId))
    31  	v.On("Level").Return(vertexLevel)
    32  	v.On("Parent").Return(string2Identifier(parentId), parentLevel)
    33  	return v
    34  }
    35  
    36  // FOREST:
    37  //
    38  //	                       ↙-- [A]
    39  //	(Genesis) ← [B] ← [C]  ←-- [D]
    40  //	   ⋮         ⋮      ⋮        ⋮
    41  //	   ⋮         ⋮      ⋮   (Missing1) ←---- [W]
    42  //	   ⋮         ⋮      ⋮        ⋮        (Missing2) ← [X] ← [Y]
    43  //	   ⋮         ⋮      ⋮        ⋮             ⋮        ⋮  ↖ [Z]
    44  //	   ⋮         ⋮      ⋮        ⋮             ⋮        ⋮     ⋮
    45  //
    46  // LEVEL:  0         1     2        3             4       5     6
    47  // Nomenclature:
    48  //
    49  //	[B] Vertex B (internally represented as a full vertex container)
    50  //	(M) referenced vertex that has not been added (internally represented as empty vertex container)
    51  var TestVertices = map[string]*mock.Vertex{
    52  	"A": NewVertexMock("A", 3, "Genesis", 0),
    53  	"B": NewVertexMock("B", 1, "Genesis", 0),
    54  	"C": NewVertexMock("C", 2, "B", 1),
    55  	"D": NewVertexMock("D", 3, "C", 2),
    56  	"W": NewVertexMock("W", 4, "Missing1", 3),
    57  	"X": NewVertexMock("X", 5, "Missing2", 4),
    58  	"Y": NewVertexMock("Y", 6, "X", 5),
    59  	"Z": NewVertexMock("Z", 6, "X", 5),
    60  }
    61  
    62  // ~~~~~~~~~~~~~~~~~~~~~~~~ Tests for VertexIterator ~~~~~~~~~~~~~~~~~~~~~~~~ //
    63  
    64  // TestVertexIterator tests VertexIterator on non-empty list
    65  func TestVertexIterator(t *testing.T) {
    66  	var vl VertexList
    67  	for i := 1; i <= 10; i++ {
    68  		b := VertexMock{level: uint64(i)}
    69  		c := vertexContainer{vertex: &b}
    70  		vl = append(vl, &c)
    71  	}
    72  
    73  	i := 0
    74  	for I := newVertexIterator(vl); I.HasNext(); {
    75  		assert.Equal(t, vl[i].vertex, I.NextVertex())
    76  		i++
    77  	}
    78  }
    79  
    80  // TestVertexIteratorOnEmpty tests VertexIterator on non-empty and nil lists
    81  func TestVertexIteratorOnEmpty(t *testing.T) {
    82  	I := newVertexIterator(nil)
    83  	assert.False(t, I.HasNext())
    84  	assert.True(t, I.NextVertex() == nil)
    85  
    86  	I = newVertexIterator(VertexList{})
    87  	assert.False(t, I.HasNext())
    88  	assert.True(t, I.NextVertex() == nil)
    89  }
    90  
    91  // ~~~~~~~~~~~~~~~~~~~~~~~~ Tests for LevelledForest ~~~~~~~~~~~~~~~~~~~~~~~~ //
    92  
    93  // TestLevelledForest_AddVertex tests that Vertex can be added twice without problems
    94  func TestLevelledForest_AddVertex(t *testing.T) {
    95  	F := NewLevelledForest(0)
    96  	v := NewVertexMock("A", 3, "Genesis", 0)
    97  	err := F.VerifyVertex(v)
    98  	require.NoError(t, err)
    99  	F.AddVertex(v)
   100  	assert.True(t, F.HasVertex(string2Identifier("A")))
   101  
   102  	// Adding Vertex twice should be fine
   103  	v = NewVertexMock("A", 3, "Genesis", 0)
   104  	err = F.VerifyVertex(v)
   105  	require.NoError(t, err)
   106  	F.AddVertex(v)
   107  	assert.True(t, F.HasVertex(string2Identifier("A")))
   108  }
   109  
   110  // TestLevelledForest_AcceptingGenesis checks that Levelled Forest accepts vertices
   111  // whose level are at LevelledForest.LowestLevel without requiring the parent.
   112  // we test this by having the mock.vertex.Parent() panic
   113  func TestLevelledForest_AcceptingGenesis(t *testing.T) {
   114  	// LevelledForest.LowestLevel on initial conditions
   115  	F := populateNewForest(t)
   116  	v1 := &mock.Vertex{}
   117  	v1.On("VertexID").Return(string2Identifier("Root-Vertex-A_@Level0"))
   118  	v1.On("Level").Return(uint64(0))
   119  	v1.On("Parent").Return(func() (flow.Identifier, uint64) { panic("Parent() should not have been called") })
   120  	assert.NotPanics(t, func() { F.AddVertex(v1) })
   121  
   122  	v2 := &mock.Vertex{}
   123  	v2.On("VertexID").Return(string2Identifier("Root-Vertex-B_@Level0"))
   124  	v2.On("Level").Return(uint64(0))
   125  	v2.On("Parent").Return(func() (flow.Identifier, uint64) { panic("Parent() should not have been called") })
   126  	assert.NotPanics(t, func() { F.AddVertex(v2) })
   127  	assert.NotPanics(t, func() { F.AddVertex(v2) })
   128  
   129  	F = populateNewForest(t)
   130  	err := F.PruneUpToLevel(8) // LevelledForest.LowestLevel on initial conditions
   131  	assert.NoError(t, err)
   132  	v3 := &mock.Vertex{}
   133  	v3.On("VertexID").Return(string2Identifier("Root-Vertex-A_@Level8"))
   134  	v3.On("Level").Return(uint64(8))
   135  	v3.On("Parent").Return(func() (flow.Identifier, uint64) { panic("Parent() should not have been called") })
   136  	assert.NotPanics(t, func() { F.AddVertex(v3) })
   137  
   138  	v4 := &mock.Vertex{}
   139  	v4.On("VertexID").Return(string2Identifier("Root-Vertex-B_@Level8"))
   140  	v4.On("Level").Return(uint64(8))
   141  	v4.On("Parent").Return(func() (flow.Identifier, uint64) { panic("Parent() should not have been called") })
   142  	assert.NotPanics(t, func() { F.AddVertex(v4) })
   143  	assert.NotPanics(t, func() { F.AddVertex(v4) })
   144  }
   145  
   146  // TestLevelledForest_VerifyVertex checks that invalid Vertices are detected.
   147  // with an ID identical to a known reference BUT whose level is not consistent with the reference
   148  func TestLevelledForest_VerifyVertex(t *testing.T) {
   149  	F := populateNewForest(t)
   150  
   151  	// KNOWN vertex but with wrong level number
   152  	err := F.VerifyVertex(NewVertexMock("D", 10, "C", 2))
   153  	assert.Error(t, err)
   154  	assert.True(t, IsInvalidVertexError(err))
   155  
   156  	// KNOWN vertex whose PARENT references a known vertex but with mismatching level
   157  	err = F.VerifyVertex(NewVertexMock("D", 3, "C", 1))
   158  	assert.Error(t, err)
   159  	assert.True(t, IsInvalidVertexError(err))
   160  
   161  	// unknown vertex whose PARENT references a known vertex but with mismatching level
   162  	err = F.VerifyVertex(NewVertexMock("F", 4, "Genesis", 10))
   163  	assert.Error(t, err)
   164  	assert.True(t, IsInvalidVertexError(err))
   165  
   166  	// UNKNOWN vertex (Missing) that is already referenced as parent by an existing vertex [W]
   167  	// _but_ new vertex has inconsistent level compared to the parent information [W] reports
   168  	err = F.VerifyVertex(NewVertexMock("Missing1", 2, "Genesis", 0))
   169  	assert.Error(t, err)
   170  	assert.True(t, IsInvalidVertexError(err))
   171  }
   172  
   173  // TestLevelledForest_HasVertex test that vertices as correctly reported as contained in Forest
   174  // NOTE: We consider a vertex added only if it has been directly added through the AddVertex method.
   175  // Vertices that references bvy known vertices but have not themselves are considered to be not in the tree.
   176  func TestLevelledForest_HasVertex(t *testing.T) {
   177  	F := populateNewForest(t)
   178  	assert.True(t, F.HasVertex(string2Identifier("A")))
   179  	assert.True(t, F.HasVertex(string2Identifier("B")))
   180  	assert.True(t, F.HasVertex(string2Identifier("X")))
   181  
   182  	assert.False(t, F.HasVertex(string2Identifier("Genesis")))     // Genesis block never directly added (only referenced) => unknown
   183  	assert.False(t, F.HasVertex(string2Identifier("NotYetAdded"))) // Block never mentioned before
   184  }
   185  
   186  // TestLevelledForest_GetChildren tests that children are returned properly
   187  func TestLevelledForest_GetChildren(t *testing.T) {
   188  	F := populateNewForest(t)
   189  
   190  	// testing children for Block that is contained in Tree
   191  	it := F.GetChildren(string2Identifier("X"))
   192  	expectedChildren := []*mock.Vertex{
   193  		TestVertices["Y"],
   194  		TestVertices["Z"],
   195  	}
   196  	assert.ElementsMatch(t, expectedChildren, children2List(&it))
   197  
   198  	// testing children for referenced Block that is NOT contained in Tree
   199  	it = F.GetChildren(string2Identifier("Genesis"))
   200  	expectedChildren = []*mock.Vertex{
   201  		TestVertices["A"],
   202  		TestVertices["B"],
   203  	}
   204  	assert.ElementsMatch(t, expectedChildren, children2List(&it))
   205  
   206  	// testing children for Block that is contained in Tree but no children are known
   207  	it = F.GetChildren(string2Identifier("D"))
   208  	assert.False(t, it.HasNext())
   209  }
   210  
   211  // TestLevelledForest_GetNumberOfChildren tests that children are returned properly
   212  func TestLevelledForest_GetNumberOfChildren(t *testing.T) {
   213  	F := populateNewForest(t)
   214  
   215  	// testing children for Block that is contained in Tree
   216  	assert.Equal(t, 2, F.GetNumberOfChildren(string2Identifier("X")))
   217  
   218  	// testing children for referenced Block that is NOT contained in Tree
   219  	assert.Equal(t, 2, F.GetNumberOfChildren(string2Identifier("Genesis")))
   220  
   221  	// testing children for Block that is contained in Tree but no children are known
   222  	assert.Equal(t, 0, F.GetNumberOfChildren(string2Identifier("D")))
   223  }
   224  
   225  // TestLevelledForest_GetVerticesAtLevel tests that Vertex blob is returned properly
   226  func TestLevelledForest_GetVerticesAtLevel(t *testing.T) {
   227  	F := populateNewForest(t)
   228  
   229  	// testing vertices for level that are contained in Tree
   230  	it := F.GetVerticesAtLevel(6)
   231  	expectedChildren := []*mock.Vertex{
   232  		TestVertices["Y"],
   233  		TestVertices["Z"],
   234  	}
   235  	assert.ElementsMatch(t, expectedChildren, children2List(&it))
   236  
   237  	// testing vertices for level that are not in Tree but referenced by vertices in the Tree
   238  	it = F.GetVerticesAtLevel(0)
   239  	assert.ElementsMatch(t, []*mock.Vertex{}, children2List(&it))
   240  
   241  	// testing vertices for level with a mixture of referenced but unknown vertices and known vertices
   242  	it = F.GetVerticesAtLevel(4)
   243  	expectedChildren = []*mock.Vertex{
   244  		TestVertices["W"],
   245  	}
   246  	assert.ElementsMatch(t, expectedChildren, children2List(&it))
   247  
   248  	// testing vertices for level that are neither in Tree nor referenced by vertices in the Tree
   249  	it = F.GetVerticesAtLevel(100000)
   250  	assert.ElementsMatch(t, []*mock.Vertex{}, children2List(&it))
   251  }
   252  
   253  // TestLevelledForest_GetNumberOfVerticesAtLevel tests that the number of vertices at a specified level is reported correctly.
   254  func TestLevelledForest_GetNumberOfVerticesAtLevel(t *testing.T) {
   255  	F := populateNewForest(t)
   256  
   257  	// testing vertices for level that are contained in Tree
   258  	assert.Equal(t, 2, F.GetNumberOfVerticesAtLevel(6))
   259  
   260  	// testing vertices for level that are not in Tree but referenced by vertices in the Tree
   261  	assert.Equal(t, 0, F.GetNumberOfVerticesAtLevel(0))
   262  
   263  	// testing vertices for level with a mixture of referenced but unknown vertices and known vertices
   264  	assert.Equal(t, 1, F.GetNumberOfVerticesAtLevel(4))
   265  
   266  	// testing vertices for level that are neither in Tree nor referenced by vertices in the Tree
   267  	assert.Equal(t, 0, F.GetNumberOfVerticesAtLevel(100000))
   268  }
   269  
   270  // TestLevelledForest_GetVertex tests that Vertex blob is returned properly
   271  func TestLevelledForest_GetVertex(t *testing.T) {
   272  	F := populateNewForest(t)
   273  	v, exists := F.GetVertex(string2Identifier("D"))
   274  	assert.Equal(t, TestVertices["D"], v)
   275  	assert.True(t, exists)
   276  
   277  	v, exists = F.GetVertex(string2Identifier("X"))
   278  	assert.Equal(t, TestVertices["X"], v)
   279  	assert.True(t, exists)
   280  
   281  	v, exists = F.GetVertex(string2Identifier("Genesis"))
   282  	assert.Equal(t, (Vertex)(nil), v)
   283  	assert.False(t, exists)
   284  }
   285  
   286  // TestLevelledForest_GetSize tests that GetSize returns valid size when adding and pruning vertices
   287  func TestLevelledForest_GetSize(t *testing.T) {
   288  	F := NewLevelledForest(0)
   289  	numberOfNodes := uint64(10)
   290  	parentLevel := uint64(0)
   291  	for i := uint64(1); i <= numberOfNodes; i++ {
   292  		vertexId := strconv.FormatUint(i, 10)
   293  		parentId := strconv.FormatUint(parentLevel, 10)
   294  		F.AddVertex(NewVertexMock(vertexId, i, parentId, parentLevel))
   295  		parentLevel = i
   296  	}
   297  	assert.Equal(t, numberOfNodes, F.GetSize())
   298  	assert.NoError(t, F.PruneUpToLevel(numberOfNodes/2))
   299  	// pruning removes element till some level but not including, that's why if we prune
   300  	// to numberOfNodes/2 then we actually expect elements with level >= numberOfNodes/2
   301  	assert.Equal(t, numberOfNodes/2+1, F.GetSize())
   302  	assert.NoError(t, F.PruneUpToLevel(numberOfNodes+1))
   303  	assert.Equal(t, uint64(0), F.GetSize())
   304  }
   305  
   306  // TestLevelledForest_GetSize_PruningTwice tests that GetSize returns same size when pruned twice to same height
   307  func TestLevelledForest_GetSize_PruningTwice(t *testing.T) {
   308  	F := NewLevelledForest(0)
   309  	numberOfNodes := uint64(10)
   310  	parentLevel := uint64(0)
   311  	for i := uint64(1); i <= numberOfNodes; i++ {
   312  		vertexId := strconv.FormatUint(i, 10)
   313  		parentId := strconv.FormatUint(parentLevel, 10)
   314  		F.AddVertex(NewVertexMock(vertexId, i, parentId, parentLevel))
   315  		parentLevel = i
   316  	}
   317  	assert.NoError(t, F.PruneUpToLevel(numberOfNodes/2))
   318  	size := F.GetSize()
   319  
   320  	assert.NoError(t, F.PruneUpToLevel(numberOfNodes/2))
   321  	// pruning again with the same level should not change size
   322  	assert.Equal(t, size, F.GetSize())
   323  }
   324  
   325  // TestLevelledForest_GetSize_DuplicatedNodes tests that GetSize returns valid size when adding duplicated nodes
   326  func TestLevelledForest_GetSize_DuplicatedNodes(t *testing.T) {
   327  	F := NewLevelledForest(0)
   328  	for _, vertex := range TestVertices {
   329  		F.AddVertex(vertex)
   330  	}
   331  	size := F.GetSize()
   332  	for _, vertex := range TestVertices {
   333  		F.AddVertex(vertex)
   334  	}
   335  	assert.Equal(t, size, F.GetSize())
   336  }
   337  
   338  // TestLevelledForest_GetVertex tests that Vertex blob is returned properly
   339  func TestLevelledForest_PruneAtLevel(t *testing.T) {
   340  	F := populateNewForest(t)
   341  	err := F.PruneUpToLevel(1)
   342  	assert.NoError(t, err)
   343  	assert.False(t, F.HasVertex(string2Identifier("Genesis")))
   344  	assert.True(t, F.HasVertex(string2Identifier("A")))
   345  	assert.True(t, F.HasVertex(string2Identifier("B")))
   346  	assert.True(t, F.HasVertex(string2Identifier("C")))
   347  	assert.True(t, F.HasVertex(string2Identifier("D")))
   348  	assert.True(t, F.HasVertex(string2Identifier("X")))
   349  	assert.True(t, F.HasVertex(string2Identifier("Y")))
   350  	assert.True(t, F.HasVertex(string2Identifier("Z")))
   351  
   352  	err = F.PruneUpToLevel(3)
   353  	assert.NoError(t, err)
   354  	assert.False(t, F.HasVertex(string2Identifier("Genesis")))
   355  	assert.True(t, F.HasVertex(string2Identifier("A")))
   356  	assert.False(t, F.HasVertex(string2Identifier("B")))
   357  	assert.False(t, F.HasVertex(string2Identifier("C")))
   358  	assert.True(t, F.HasVertex(string2Identifier("D")))
   359  	assert.True(t, F.HasVertex(string2Identifier("X")))
   360  	assert.True(t, F.HasVertex(string2Identifier("Y")))
   361  	assert.True(t, F.HasVertex(string2Identifier("Z")))
   362  
   363  	err = F.PruneUpToLevel(6)
   364  	assert.NoError(t, err)
   365  	assert.False(t, F.HasVertex(string2Identifier("Genesis")))
   366  	assert.False(t, F.HasVertex(string2Identifier("A")))
   367  	assert.False(t, F.HasVertex(string2Identifier("B")))
   368  	assert.False(t, F.HasVertex(string2Identifier("C")))
   369  	assert.False(t, F.HasVertex(string2Identifier("D")))
   370  	assert.False(t, F.HasVertex(string2Identifier("X")))
   371  	assert.True(t, F.HasVertex(string2Identifier("Y")))
   372  	assert.True(t, F.HasVertex(string2Identifier("Z")))
   373  
   374  	// pruning at same level repeatedly should be fine
   375  	err = F.PruneUpToLevel(6)
   376  	assert.NoError(t, err)
   377  	assert.True(t, F.HasVertex(string2Identifier("Y")))
   378  	assert.True(t, F.HasVertex(string2Identifier("Z")))
   379  
   380  	// checking that pruning at lower level than what is already pruned results in error
   381  	err = F.PruneUpToLevel(5)
   382  	assert.Error(t, err)
   383  	assert.True(t, mempool.IsBelowPrunedThresholdError(err))
   384  }
   385  
   386  func TestIsInvalidVertexError(t *testing.T) {
   387  	vertex := NewVertexMock("A", 1, "B", 0)
   388  	err := NewInvalidVertexErrorf(vertex, "some error")
   389  	assert.True(t, IsInvalidVertexError(err))
   390  }
   391  
   392  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
   393  
   394  func populateNewForest(t *testing.T) *LevelledForest {
   395  	F := NewLevelledForest(0)
   396  	for _, v := range TestVertices {
   397  		if err := F.VerifyVertex(v); err != nil {
   398  			assert.Fail(t, err.Error())
   399  		}
   400  		F.AddVertex(v)
   401  	}
   402  	return F
   403  }
   404  
   405  func children2List(it *VertexIterator) []*mock.Vertex {
   406  	l := []*mock.Vertex{}
   407  	for it.HasNext() {
   408  		// Vertex interface is implemented by mock.Vertex POINTER!
   409  		// Hence, the concrete type is *mock.Vertex
   410  		v := it.NextVertex().(*mock.Vertex)
   411  		l = append(l, v)
   412  	}
   413  	return l
   414  }
   415  
   416  func string2Identifier(s string) flow.Identifier {
   417  	var identifier flow.Identifier
   418  	copy(identifier[:], []byte(s))
   419  	return identifier
   420  }