github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/forkchoice/protoarray/store_test.go (about) 1 package protoarray 2 3 import ( 4 "context" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 "github.com/prysmaticlabs/prysm/shared/bytesutil" 9 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 10 "github.com/prysmaticlabs/prysm/shared/testutil/require" 11 ) 12 13 func TestStore_PruneThreshold(t *testing.T) { 14 s := &Store{ 15 pruneThreshold: defaultPruneThreshold, 16 } 17 if got := s.PruneThreshold(); got != defaultPruneThreshold { 18 t.Errorf("PruneThreshold() = %v, want %v", got, defaultPruneThreshold) 19 } 20 } 21 22 func TestStore_JustifiedEpoch(t *testing.T) { 23 j := types.Epoch(100) 24 s := &Store{ 25 justifiedEpoch: j, 26 } 27 if got := s.JustifiedEpoch(); got != j { 28 t.Errorf("JustifiedEpoch() = %v, want %v", got, j) 29 } 30 } 31 32 func TestStore_FinalizedEpoch(t *testing.T) { 33 f := types.Epoch(50) 34 s := &Store{ 35 finalizedEpoch: f, 36 } 37 if got := s.FinalizedEpoch(); got != f { 38 t.Errorf("FinalizedEpoch() = %v, want %v", got, f) 39 } 40 } 41 42 func TestStore_Nodes(t *testing.T) { 43 nodes := []*Node{ 44 {slot: 100}, 45 {slot: 101}, 46 } 47 s := &Store{ 48 nodes: nodes, 49 } 50 require.DeepEqual(t, nodes, s.Nodes()) 51 } 52 53 func TestStore_NodesIndices(t *testing.T) { 54 nodeIndices := map[[32]byte]uint64{ 55 {'a'}: 1, 56 {'b'}: 2, 57 } 58 s := &Store{ 59 nodesIndices: nodeIndices, 60 } 61 require.DeepEqual(t, nodeIndices, s.NodesIndices()) 62 } 63 64 func TestForkChoice_HasNode(t *testing.T) { 65 nodeIndices := map[[32]byte]uint64{ 66 {'a'}: 1, 67 {'b'}: 2, 68 } 69 s := &Store{ 70 nodesIndices: nodeIndices, 71 } 72 f := &ForkChoice{store: s} 73 require.Equal(t, true, f.HasNode([32]byte{'a'})) 74 } 75 76 func TestForkChoice_Store(t *testing.T) { 77 nodeIndices := map[[32]byte]uint64{ 78 {'a'}: 1, 79 {'b'}: 2, 80 } 81 s := &Store{ 82 nodesIndices: nodeIndices, 83 } 84 f := &ForkChoice{store: s} 85 require.DeepEqual(t, s, f.Store()) 86 } 87 88 func TestForkChoice_Nodes(t *testing.T) { 89 nodes := []*Node{ 90 {slot: 100}, 91 {slot: 101}, 92 } 93 s := &Store{ 94 nodes: nodes, 95 } 96 f := &ForkChoice{store: s} 97 require.DeepEqual(t, s.nodes, f.Nodes()) 98 } 99 100 func TestStore_Head_UnknownJustifiedRoot(t *testing.T) { 101 s := &Store{nodesIndices: make(map[[32]byte]uint64)} 102 103 _, err := s.head(context.Background(), [32]byte{}) 104 assert.ErrorContains(t, errUnknownJustifiedRoot.Error(), err) 105 } 106 107 func TestStore_Head_UnknownJustifiedIndex(t *testing.T) { 108 r := [32]byte{'A'} 109 indices := make(map[[32]byte]uint64) 110 indices[r] = 1 111 s := &Store{nodesIndices: indices} 112 113 _, err := s.head(context.Background(), r) 114 assert.ErrorContains(t, errInvalidJustifiedIndex.Error(), err) 115 } 116 117 func TestStore_Head_Itself(t *testing.T) { 118 r := [32]byte{'A'} 119 indices := make(map[[32]byte]uint64) 120 indices[r] = 0 121 122 // Since the justified node does not have a best descendant so the best node 123 // is itself. 124 s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, bestDescendant: NonExistentNode}}, canonicalNodes: make(map[[32]byte]bool)} 125 h, err := s.head(context.Background(), r) 126 require.NoError(t, err) 127 assert.Equal(t, r, h) 128 } 129 130 func TestStore_Head_BestDescendant(t *testing.T) { 131 r := [32]byte{'A'} 132 best := [32]byte{'B'} 133 indices := make(map[[32]byte]uint64) 134 indices[r] = 0 135 136 // Since the justified node's best descendent is at index 1 and it's root is `best`, 137 // the head should be `best`. 138 s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, bestDescendant: 1}, {root: best}}, canonicalNodes: make(map[[32]byte]bool)} 139 h, err := s.head(context.Background(), r) 140 require.NoError(t, err) 141 assert.Equal(t, best, h) 142 } 143 144 func TestStore_Head_ContextCancelled(t *testing.T) { 145 ctx, cancel := context.WithCancel(context.Background()) 146 r := [32]byte{'A'} 147 best := [32]byte{'B'} 148 indices := make(map[[32]byte]uint64) 149 indices[r] = 0 150 s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, bestDescendant: 1}, {root: best}}, canonicalNodes: make(map[[32]byte]bool)} 151 cancel() 152 _, err := s.head(ctx, r) 153 require.ErrorContains(t, "context canceled", err) 154 } 155 156 func TestStore_Insert_UnknownParent(t *testing.T) { 157 // The new node does not have a parent. 158 s := &Store{nodesIndices: make(map[[32]byte]uint64)} 159 require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, [32]byte{}, 1, 1)) 160 assert.Equal(t, 1, len(s.nodes), "Did not insert block") 161 assert.Equal(t, 1, len(s.nodesIndices), "Did not insert block") 162 assert.Equal(t, NonExistentNode, s.nodes[0].parent, "Incorrect parent") 163 assert.Equal(t, types.Epoch(1), s.nodes[0].justifiedEpoch, "Incorrect justification") 164 assert.Equal(t, types.Epoch(1), s.nodes[0].finalizedEpoch, "Incorrect finalization") 165 assert.Equal(t, [32]byte{'A'}, s.nodes[0].root, "Incorrect root") 166 } 167 168 func TestStore_Insert_KnownParent(t *testing.T) { 169 // Similar to UnknownParent test, but this time the new node has a valid parent already in store. 170 // The new node builds on top of the parent. 171 s := &Store{nodesIndices: make(map[[32]byte]uint64)} 172 s.nodes = []*Node{{}} 173 p := [32]byte{'B'} 174 s.nodesIndices[p] = 0 175 require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, [32]byte{}, 1, 1)) 176 assert.Equal(t, 2, len(s.nodes), "Did not insert block") 177 assert.Equal(t, 2, len(s.nodesIndices), "Did not insert block") 178 assert.Equal(t, uint64(0), s.nodes[1].parent, "Incorrect parent") 179 assert.Equal(t, types.Epoch(1), s.nodes[1].justifiedEpoch, "Incorrect justification") 180 assert.Equal(t, types.Epoch(1), s.nodes[1].finalizedEpoch, "Incorrect finalization") 181 assert.Equal(t, [32]byte{'A'}, s.nodes[1].root, "Incorrect root") 182 } 183 184 func TestStore_ApplyScoreChanges_InvalidDeltaLength(t *testing.T) { 185 s := &Store{} 186 187 // This will fail because node indices has length of 0, and delta list has a length of 1. 188 err := s.applyWeightChanges(context.Background(), 0, 0, []int{1}) 189 assert.ErrorContains(t, errInvalidDeltaLength.Error(), err) 190 } 191 192 func TestStore_ApplyScoreChanges_UpdateEpochs(t *testing.T) { 193 s := &Store{} 194 195 // The justified and finalized epochs in Store should be updated to 1 and 1 given the following input. 196 require.NoError(t, s.applyWeightChanges(context.Background(), 1, 1, []int{})) 197 assert.Equal(t, types.Epoch(1), s.justifiedEpoch, "Did not update justified epoch") 198 assert.Equal(t, types.Epoch(1), s.finalizedEpoch, "Did not update finalized epoch") 199 } 200 201 func TestStore_ApplyScoreChanges_UpdateWeightsPositiveDelta(t *testing.T) { 202 // Construct 3 nodes with weight 100 on each node. The 3 nodes linked to each other. 203 s := &Store{nodes: []*Node{ 204 {root: [32]byte{'A'}, weight: 100}, 205 {root: [32]byte{'A'}, weight: 100}, 206 {parent: 1, root: [32]byte{'A'}, weight: 100}}} 207 208 // Each node gets one unique vote. The weight should look like 103 <- 102 <- 101 because 209 // they get propagated back. 210 require.NoError(t, s.applyWeightChanges(context.Background(), 0, 0, []int{1, 1, 1})) 211 assert.Equal(t, uint64(103), s.nodes[0].weight) 212 assert.Equal(t, uint64(102), s.nodes[1].weight) 213 assert.Equal(t, uint64(101), s.nodes[2].weight) 214 } 215 216 func TestStore_ApplyScoreChanges_UpdateWeightsNegativeDelta(t *testing.T) { 217 // Construct 3 nodes with weight 100 on each node. The 3 nodes linked to each other. 218 s := &Store{nodes: []*Node{ 219 {root: [32]byte{'A'}, weight: 100}, 220 {root: [32]byte{'A'}, weight: 100}, 221 {parent: 1, root: [32]byte{'A'}, weight: 100}}} 222 223 // Each node gets one unique vote which contributes to negative delta. 224 // The weight should look like 97 <- 98 <- 99 because they get propagated back. 225 require.NoError(t, s.applyWeightChanges(context.Background(), 0, 0, []int{-1, -1, -1})) 226 assert.Equal(t, uint64(97), s.nodes[0].weight) 227 assert.Equal(t, uint64(98), s.nodes[1].weight) 228 assert.Equal(t, uint64(99), s.nodes[2].weight) 229 } 230 231 func TestStore_ApplyScoreChanges_UpdateWeightsMixedDelta(t *testing.T) { 232 // Construct 3 nodes with weight 100 on each node. The 3 nodes linked to each other. 233 s := &Store{nodes: []*Node{ 234 {root: [32]byte{'A'}, weight: 100}, 235 {root: [32]byte{'A'}, weight: 100}, 236 {parent: 1, root: [32]byte{'A'}, weight: 100}}} 237 238 // Each node gets one mixed vote. The weight should look like 100 <- 200 <- 250. 239 require.NoError(t, s.applyWeightChanges(context.Background(), 0, 0, []int{-100, -50, 150})) 240 assert.Equal(t, uint64(100), s.nodes[0].weight) 241 assert.Equal(t, uint64(200), s.nodes[1].weight) 242 assert.Equal(t, uint64(250), s.nodes[2].weight) 243 } 244 245 func TestStore_UpdateBestChildAndDescendant_RemoveChild(t *testing.T) { 246 // Make parent's best child equal's to input child index and child is not viable. 247 s := &Store{nodes: []*Node{{bestChild: 1}, {}}, justifiedEpoch: 1, finalizedEpoch: 1} 248 require.NoError(t, s.updateBestChildAndDescendant(0, 1)) 249 250 // Verify parent's best child and best descendant are `none`. 251 assert.Equal(t, NonExistentNode, s.nodes[0].bestChild, "Did not get correct best child index") 252 assert.Equal(t, NonExistentNode, s.nodes[0].bestDescendant, "Did not get correct best descendant index") 253 } 254 255 func TestStore_UpdateBestChildAndDescendant_UpdateDescendant(t *testing.T) { 256 // Make parent's best child equal to child index and child is viable. 257 s := &Store{nodes: []*Node{{bestChild: 1}, {bestDescendant: NonExistentNode}}} 258 require.NoError(t, s.updateBestChildAndDescendant(0, 1)) 259 260 // Verify parent's best child is the same and best descendant is not set to child index. 261 assert.Equal(t, uint64(1), s.nodes[0].bestChild, "Did not get correct best child index") 262 assert.Equal(t, uint64(1), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 263 } 264 265 func TestStore_UpdateBestChildAndDescendant_ChangeChildByViability(t *testing.T) { 266 // Make parent's best child not equal to child index, child leads to viable index and 267 // parents best child doesnt lead to viable index. 268 s := &Store{ 269 justifiedEpoch: 1, 270 finalizedEpoch: 1, 271 nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, 272 {bestDescendant: NonExistentNode}, 273 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}} 274 require.NoError(t, s.updateBestChildAndDescendant(0, 2)) 275 276 // Verify parent's best child and best descendant are set to child index. 277 assert.Equal(t, uint64(2), s.nodes[0].bestChild, "Did not get correct best child index") 278 assert.Equal(t, uint64(2), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 279 } 280 281 func TestStore_UpdateBestChildAndDescendant_ChangeChildByWeight(t *testing.T) { 282 // Make parent's best child not equal to child index, child leads to viable index and 283 // parents best child leads to viable index but child has more weight than parent's best child. 284 s := &Store{ 285 justifiedEpoch: 1, 286 finalizedEpoch: 1, 287 nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, 288 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, 289 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1, weight: 1}}} 290 require.NoError(t, s.updateBestChildAndDescendant(0, 2)) 291 292 // Verify parent's best child and best descendant are set to child index. 293 assert.Equal(t, uint64(2), s.nodes[0].bestChild, "Did not get correct best child index") 294 assert.Equal(t, uint64(2), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 295 } 296 297 func TestStore_UpdateBestChildAndDescendant_ChangeChildAtLeaf(t *testing.T) { 298 // Make parent's best child to none and input child leads to viable index. 299 s := &Store{ 300 justifiedEpoch: 1, 301 finalizedEpoch: 1, 302 nodes: []*Node{{bestChild: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, 303 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, 304 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}} 305 require.NoError(t, s.updateBestChildAndDescendant(0, 2)) 306 307 // Verify parent's best child and best descendant are set to child index. 308 assert.Equal(t, uint64(2), s.nodes[0].bestChild, "Did not get correct best child index") 309 assert.Equal(t, uint64(2), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 310 } 311 312 func TestStore_UpdateBestChildAndDescendant_NoChangeByViability(t *testing.T) { 313 // Make parent's best child not equal to child index, child leads to not viable index and 314 // parents best child leads to viable index. 315 s := &Store{ 316 justifiedEpoch: 1, 317 finalizedEpoch: 1, 318 nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, 319 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, 320 {bestDescendant: NonExistentNode}}} 321 require.NoError(t, s.updateBestChildAndDescendant(0, 2)) 322 323 // Verify parent's best child and best descendant are not changed. 324 assert.Equal(t, uint64(1), s.nodes[0].bestChild, "Did not get correct best child index") 325 assert.Equal(t, uint64(0), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 326 } 327 328 func TestStore_UpdateBestChildAndDescendant_NoChangeByWeight(t *testing.T) { 329 // Make parent's best child not equal to child index, child leads to viable index and 330 // parents best child leads to viable index but parent's best child has more weight. 331 s := &Store{ 332 justifiedEpoch: 1, 333 finalizedEpoch: 1, 334 nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, 335 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1, weight: 1}, 336 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}} 337 require.NoError(t, s.updateBestChildAndDescendant(0, 2)) 338 339 // Verify parent's best child and best descendant are not changed. 340 assert.Equal(t, uint64(1), s.nodes[0].bestChild, "Did not get correct best child index") 341 assert.Equal(t, uint64(0), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 342 } 343 344 func TestStore_UpdateBestChildAndDescendant_NoChangeAtLeaf(t *testing.T) { 345 // Make parent's best child to none and input child does not lead to viable index. 346 s := &Store{ 347 justifiedEpoch: 1, 348 finalizedEpoch: 1, 349 nodes: []*Node{{bestChild: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, 350 {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, 351 {bestDescendant: NonExistentNode}}} 352 require.NoError(t, s.updateBestChildAndDescendant(0, 2)) 353 354 // Verify parent's best child and best descendant are not changed. 355 assert.Equal(t, NonExistentNode, s.nodes[0].bestChild, "Did not get correct best child index") 356 assert.Equal(t, uint64(0), s.nodes[0].bestDescendant, "Did not get correct best descendant index") 357 } 358 359 func TestStore_Prune_LessThanThreshold(t *testing.T) { 360 // Define 100 nodes in store. 361 numOfNodes := 100 362 indices := make(map[[32]byte]uint64) 363 nodes := make([]*Node, 0) 364 for i := 0; i < numOfNodes; i++ { 365 indices[indexToHash(uint64(i))] = uint64(i) 366 nodes = append(nodes, &Node{slot: types.Slot(i)}) 367 } 368 369 s := &Store{nodes: nodes, nodesIndices: indices, pruneThreshold: 100} 370 371 // Finalized root is at index 99 so everything before 99 should be pruned, 372 // but PruneThreshold is at 100 so nothing will be pruned. 373 require.NoError(t, s.prune(context.Background(), indexToHash(99))) 374 assert.Equal(t, 100, len(s.nodes), "Incorrect nodes count") 375 assert.Equal(t, 100, len(s.nodesIndices), "Incorrect node indices count") 376 } 377 378 func TestStore_Prune_MoreThanThreshold(t *testing.T) { 379 // Define 100 nodes in store. 380 numOfNodes := 100 381 indices := make(map[[32]byte]uint64) 382 nodes := make([]*Node, 0) 383 for i := 0; i < numOfNodes; i++ { 384 indices[indexToHash(uint64(i))] = uint64(i) 385 nodes = append(nodes, &Node{slot: types.Slot(i), root: indexToHash(uint64(i)), 386 bestDescendant: NonExistentNode, bestChild: NonExistentNode}) 387 } 388 389 s := &Store{nodes: nodes, nodesIndices: indices} 390 391 // Finalized root is at index 99 so everything before 99 should be pruned. 392 require.NoError(t, s.prune(context.Background(), indexToHash(99))) 393 assert.Equal(t, 1, len(s.nodes), "Incorrect nodes count") 394 assert.Equal(t, 1, len(s.nodesIndices), "Incorrect node indices count") 395 } 396 397 func TestStore_Prune_MoreThanOnce(t *testing.T) { 398 // Define 100 nodes in store. 399 numOfNodes := 100 400 indices := make(map[[32]byte]uint64) 401 nodes := make([]*Node, 0) 402 for i := 0; i < numOfNodes; i++ { 403 indices[indexToHash(uint64(i))] = uint64(i) 404 nodes = append(nodes, &Node{slot: types.Slot(i), root: indexToHash(uint64(i)), 405 bestDescendant: NonExistentNode, bestChild: NonExistentNode}) 406 } 407 408 s := &Store{nodes: nodes, nodesIndices: indices} 409 410 // Finalized root is at index 11 so everything before 11 should be pruned. 411 require.NoError(t, s.prune(context.Background(), indexToHash(10))) 412 assert.Equal(t, 90, len(s.nodes), "Incorrect nodes count") 413 assert.Equal(t, 90, len(s.nodesIndices), "Incorrect node indices count") 414 415 // One more time. 416 require.NoError(t, s.prune(context.Background(), indexToHash(20))) 417 assert.Equal(t, 80, len(s.nodes), "Incorrect nodes count") 418 assert.Equal(t, 80, len(s.nodesIndices), "Incorrect node indices count") 419 } 420 func TestStore_LeadsToViableHead(t *testing.T) { 421 tests := []struct { 422 n *Node 423 justifiedEpoch types.Epoch 424 finalizedEpoch types.Epoch 425 want bool 426 }{ 427 {&Node{}, 0, 0, true}, 428 {&Node{}, 1, 0, false}, 429 {&Node{}, 0, 1, false}, 430 {&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, true}, 431 {&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, false}, 432 {&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true}, 433 } 434 for _, tc := range tests { 435 s := &Store{ 436 justifiedEpoch: tc.justifiedEpoch, 437 finalizedEpoch: tc.finalizedEpoch, 438 nodes: []*Node{tc.n}, 439 } 440 got, err := s.leadsToViableHead(tc.n) 441 require.NoError(t, err) 442 assert.Equal(t, tc.want, got) 443 } 444 } 445 446 func TestStore_ViableForHead(t *testing.T) { 447 tests := []struct { 448 n *Node 449 justifiedEpoch types.Epoch 450 finalizedEpoch types.Epoch 451 want bool 452 }{ 453 {&Node{}, 0, 0, true}, 454 {&Node{}, 1, 0, false}, 455 {&Node{}, 0, 1, false}, 456 {&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, true}, 457 {&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, false}, 458 {&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true}, 459 } 460 for _, tc := range tests { 461 s := &Store{ 462 justifiedEpoch: tc.justifiedEpoch, 463 finalizedEpoch: tc.finalizedEpoch, 464 } 465 assert.Equal(t, tc.want, s.viableForHead(tc.n)) 466 } 467 } 468 469 func TestStore_HasParent(t *testing.T) { 470 tests := []struct { 471 m map[[32]byte]uint64 472 n []*Node 473 r [32]byte 474 want bool 475 }{ 476 {r: [32]byte{'a'}, want: false}, 477 {m: map[[32]byte]uint64{{'a'}: 0}, r: [32]byte{'a'}, want: false}, 478 {m: map[[32]byte]uint64{{'a'}: 0}, r: [32]byte{'a'}, 479 n: []*Node{{parent: NonExistentNode}}, want: false}, 480 {m: map[[32]byte]uint64{{'a'}: 0}, 481 n: []*Node{{parent: 0}}, r: [32]byte{'a'}, 482 want: true}, 483 } 484 for _, tc := range tests { 485 f := &ForkChoice{store: &Store{ 486 nodesIndices: tc.m, 487 nodes: tc.n, 488 }} 489 assert.Equal(t, tc.want, f.HasParent(tc.r)) 490 } 491 } 492 493 func TestStore_AncestorRoot(t *testing.T) { 494 ctx := context.Background() 495 f := &ForkChoice{store: &Store{}} 496 f.store.nodesIndices = map[[32]byte]uint64{} 497 _, err := f.AncestorRoot(ctx, [32]byte{'a'}, 0) 498 assert.ErrorContains(t, "node does not exist", err) 499 f.store.nodesIndices[[32]byte{'a'}] = 0 500 _, err = f.AncestorRoot(ctx, [32]byte{'a'}, 0) 501 assert.ErrorContains(t, "node index out of range", err) 502 f.store.nodesIndices[[32]byte{'b'}] = 1 503 f.store.nodesIndices[[32]byte{'c'}] = 2 504 f.store.nodes = []*Node{ 505 {slot: 1, root: [32]byte{'a'}, parent: NonExistentNode}, 506 {slot: 2, root: [32]byte{'b'}, parent: 0}, 507 {slot: 3, root: [32]byte{'c'}, parent: 1}, 508 } 509 510 r, err := f.AncestorRoot(ctx, [32]byte{'c'}, 1) 511 require.NoError(t, err) 512 assert.Equal(t, bytesutil.ToBytes32(r), [32]byte{'a'}) 513 r, err = f.AncestorRoot(ctx, [32]byte{'c'}, 2) 514 require.NoError(t, err) 515 assert.Equal(t, bytesutil.ToBytes32(r), [32]byte{'b'}) 516 } 517 518 func TestStore_AncestorRootOutOfBound(t *testing.T) { 519 ctx := context.Background() 520 f := &ForkChoice{store: &Store{}} 521 f.store.nodesIndices = map[[32]byte]uint64{} 522 _, err := f.AncestorRoot(ctx, [32]byte{'a'}, 0) 523 assert.ErrorContains(t, "node does not exist", err) 524 f.store.nodesIndices[[32]byte{'a'}] = 0 525 _, err = f.AncestorRoot(ctx, [32]byte{'a'}, 0) 526 assert.ErrorContains(t, "node index out of range", err) 527 f.store.nodesIndices[[32]byte{'b'}] = 1 528 f.store.nodesIndices[[32]byte{'c'}] = 2 529 f.store.nodes = []*Node{ 530 {slot: 1, root: [32]byte{'a'}, parent: NonExistentNode}, 531 {slot: 2, root: [32]byte{'b'}, parent: 100}, // Out of bound parent. 532 {slot: 3, root: [32]byte{'c'}, parent: 1}, 533 } 534 535 _, err = f.AncestorRoot(ctx, [32]byte{'c'}, 1) 536 require.ErrorContains(t, "node index out of range", err) 537 } 538 539 func TestStore_UpdateCanonicalNodes_WholeList(t *testing.T) { 540 ctx := context.Background() 541 f := &ForkChoice{store: &Store{}} 542 f.store.canonicalNodes = map[[32]byte]bool{} 543 f.store.nodesIndices = map[[32]byte]uint64{} 544 f.store.nodes = []*Node{ 545 {slot: 1, root: [32]byte{'a'}, parent: NonExistentNode}, 546 {slot: 2, root: [32]byte{'b'}, parent: 0}, 547 {slot: 3, root: [32]byte{'c'}, parent: 1}, 548 } 549 f.store.nodesIndices[[32]byte{'c'}] = 2 550 require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'c'})) 551 require.Equal(t, len(f.store.nodes), len(f.store.canonicalNodes)) 552 require.Equal(t, true, f.IsCanonical([32]byte{'c'})) 553 require.Equal(t, true, f.IsCanonical([32]byte{'b'})) 554 require.Equal(t, true, f.IsCanonical([32]byte{'c'})) 555 require.DeepEqual(t, f.Node([32]byte{'c'}), f.store.nodes[2]) 556 require.Equal(t, f.Node([32]byte{'d'}), (*Node)(nil)) 557 } 558 559 func TestStore_UpdateCanonicalNodes_ParentAlreadyIn(t *testing.T) { 560 ctx := context.Background() 561 f := &ForkChoice{store: &Store{}} 562 f.store.canonicalNodes = map[[32]byte]bool{} 563 f.store.nodesIndices = map[[32]byte]uint64{} 564 f.store.nodes = []*Node{ 565 {}, 566 {slot: 2, root: [32]byte{'b'}, parent: 0}, 567 {slot: 3, root: [32]byte{'c'}, parent: 1}, 568 } 569 f.store.nodesIndices[[32]byte{'c'}] = 2 570 f.store.canonicalNodes[[32]byte{'b'}] = true 571 require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'c'})) 572 require.Equal(t, len(f.store.nodes)-1, len(f.store.canonicalNodes)) 573 574 require.Equal(t, true, f.IsCanonical([32]byte{'c'})) 575 require.Equal(t, true, f.IsCanonical([32]byte{'b'})) 576 } 577 578 func TestStore_UpdateCanonicalNodes_ContextCancelled(t *testing.T) { 579 ctx, cancel := context.WithCancel(context.Background()) 580 f := &ForkChoice{store: &Store{}} 581 f.store.canonicalNodes = map[[32]byte]bool{} 582 f.store.nodesIndices = map[[32]byte]uint64{} 583 f.store.nodes = []*Node{ 584 {slot: 1, root: [32]byte{'a'}, parent: NonExistentNode}, 585 {slot: 2, root: [32]byte{'b'}, parent: 0}, 586 {slot: 3, root: [32]byte{'c'}, parent: 1}, 587 } 588 f.store.nodesIndices[[32]byte{'c'}] = 2 589 cancel() 590 require.ErrorContains(t, "context canceled", f.store.updateCanonicalNodes(ctx, [32]byte{'c'})) 591 }