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 }