github.com/ava-labs/avalanchego@v1.11.11/vms/components/chain/state_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package chain 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "testing" 11 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/require" 14 15 "github.com/ava-labs/avalanchego/database" 16 "github.com/ava-labs/avalanchego/ids" 17 "github.com/ava-labs/avalanchego/snow/consensus/snowman" 18 "github.com/ava-labs/avalanchego/snow/consensus/snowman/snowmantest" 19 "github.com/ava-labs/avalanchego/snow/snowtest" 20 "github.com/ava-labs/avalanchego/utils/hashing" 21 ) 22 23 const ( 24 Unknown snowtest.Status = -1 25 26 defaultBlockCacheSize = 256 27 ) 28 29 var ( 30 errCantBuildBlock = errors.New("can't build new block") 31 errVerify = errors.New("verify failed") 32 errAccept = errors.New("accept failed") 33 errReject = errors.New("reject failed") 34 errUnexpectedBlockBytes = errors.New("unexpected block bytes") 35 ) 36 37 // NewTestBlock returns a new test block with height, bytes, and ID derived from [i] 38 // and using [parentID] as the parent block ID 39 func NewTestBlock(i uint64, parentID ids.ID) *snowmantest.Block { 40 b := []byte{byte(i)} 41 id := hashing.ComputeHash256Array(b) 42 return &snowmantest.Block{ 43 Decidable: snowtest.Decidable{ 44 IDV: id, 45 Status: Unknown, 46 }, 47 HeightV: i, 48 ParentV: parentID, 49 BytesV: b, 50 } 51 } 52 53 // NewTestBlocks generates [numBlocks] consecutive blocks 54 func NewTestBlocks(numBlocks uint64) []*snowmantest.Block { 55 blks := make([]*snowmantest.Block, 0, numBlocks) 56 parentID := ids.Empty 57 for i := uint64(0); i < numBlocks; i++ { 58 blks = append(blks, NewTestBlock(i, parentID)) 59 parent := blks[len(blks)-1] 60 parentID = parent.ID() 61 } 62 63 return blks 64 } 65 66 func createInternalBlockFuncs(blks []*snowmantest.Block) ( 67 func(ctx context.Context, blkID ids.ID) (snowman.Block, error), 68 func(ctx context.Context, b []byte) (snowman.Block, error), 69 ) { 70 blkMap := make(map[ids.ID]*snowmantest.Block) 71 blkBytesMap := make(map[string]*snowmantest.Block) 72 for _, blk := range blks { 73 blkMap[blk.ID()] = blk 74 blkBytes := blk.Bytes() 75 blkBytesMap[string(blkBytes)] = blk 76 } 77 78 getBlock := func(_ context.Context, id ids.ID) (snowman.Block, error) { 79 blk, ok := blkMap[id] 80 if !ok || blk.Status == Unknown { 81 return nil, database.ErrNotFound 82 } 83 84 return blk, nil 85 } 86 87 parseBlk := func(_ context.Context, b []byte) (snowman.Block, error) { 88 blk, ok := blkBytesMap[string(b)] 89 if !ok { 90 return nil, fmt.Errorf("%w: %x", errUnexpectedBlockBytes, b) 91 } 92 if blk.Status == Unknown { 93 blk.Status = snowtest.Undecided 94 } 95 blkMap[blk.ID()] = blk 96 97 return blk, nil 98 } 99 100 return getBlock, parseBlk 101 } 102 103 func cantBuildBlock(context.Context) (snowman.Block, error) { 104 return nil, errCantBuildBlock 105 } 106 107 // checkProcessingBlock checks that [blk] is of the correct type and is 108 // correctly uniquified when calling GetBlock and ParseBlock. 109 func checkProcessingBlock(t *testing.T, s *State, blk snowman.Block) { 110 require := require.New(t) 111 112 require.IsType(&BlockWrapper{}, blk) 113 114 parsedBlk, err := s.ParseBlock(context.Background(), blk.Bytes()) 115 require.NoError(err) 116 require.Equal(blk.ID(), parsedBlk.ID()) 117 require.Equal(blk.Bytes(), parsedBlk.Bytes()) 118 require.Equal(blk, parsedBlk) 119 120 getBlk, err := s.GetBlock(context.Background(), blk.ID()) 121 require.NoError(err) 122 require.Equal(parsedBlk, getBlk) 123 } 124 125 // checkDecidedBlock asserts that [blk] is returned with the correct status by ParseBlock 126 // and GetBlock. 127 // expectedStatus should be either Accepted or Rejected. 128 func checkDecidedBlock(t *testing.T, s *State, blk snowman.Block, cached bool) { 129 require := require.New(t) 130 131 require.IsType(&BlockWrapper{}, blk) 132 133 if cached { 134 _, ok := s.decidedBlocks.Get(blk.ID()) 135 require.True(ok) 136 } 137 138 parsedBlk, err := s.ParseBlock(context.Background(), blk.Bytes()) 139 require.NoError(err) 140 require.Equal(blk.ID(), parsedBlk.ID()) 141 require.Equal(blk.Bytes(), parsedBlk.Bytes()) 142 143 _, ok := s.decidedBlocks.Get(blk.ID()) 144 require.True(ok) 145 146 // If the block should be in the cache, assert that the returned block is identical to [blk] 147 if cached { 148 require.Equal(blk, parsedBlk) 149 } 150 151 getBlk, err := s.GetBlock(context.Background(), blk.ID()) 152 require.NoError(err) 153 require.Equal(blk.ID(), getBlk.ID()) 154 require.Equal(blk.Bytes(), getBlk.Bytes()) 155 156 // Since ParseBlock should have triggered a cache hit, assert that the block is identical 157 // to the parsed block. 158 require.Equal(parsedBlk, getBlk) 159 } 160 161 func TestState(t *testing.T) { 162 require := require.New(t) 163 164 testBlks := NewTestBlocks(3) 165 genesisBlock := testBlks[0] 166 genesisBlock.Status = snowtest.Accepted 167 blk1 := testBlks[1] 168 blk2 := testBlks[2] 169 // Need to create a block with a different bytes and hash here 170 // to generate a conflict with blk2 171 blk3 := snowmantest.BuildChild(blk1) 172 testBlks = append(testBlks, blk3) 173 174 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 175 chainState := NewState(&Config{ 176 DecidedCacheSize: defaultBlockCacheSize, 177 MissingCacheSize: defaultBlockCacheSize, 178 UnverifiedCacheSize: defaultBlockCacheSize, 179 BytesToIDCacheSize: defaultBlockCacheSize, 180 LastAcceptedBlock: genesisBlock, 181 GetBlock: getBlock, 182 UnmarshalBlock: parseBlock, 183 BuildBlock: cantBuildBlock, 184 }) 185 186 lastAccepted, err := chainState.LastAccepted(context.Background()) 187 require.NoError(err) 188 require.Equal(genesisBlock.ID(), lastAccepted) 189 190 wrappedGenesisBlk, err := chainState.GetBlock(context.Background(), genesisBlock.ID()) 191 require.NoError(err) 192 193 // Check that a cache miss on a block is handled correctly 194 _, err = chainState.GetBlock(context.Background(), blk1.ID()) 195 require.ErrorIs(err, database.ErrNotFound) 196 197 // Parse and verify blk1 and blk2 198 parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes()) 199 require.NoError(err) 200 require.NoError(parsedBlk1.Verify(context.Background())) 201 202 parsedBlk2, err := chainState.ParseBlock(context.Background(), blk2.Bytes()) 203 require.NoError(err) 204 require.NoError(parsedBlk2.Verify(context.Background())) 205 206 // Check that the verified blocks have been placed in the processing map 207 require.Len(chainState.verifiedBlocks, 2) 208 209 parsedBlk3, err := chainState.ParseBlock(context.Background(), blk3.Bytes()) 210 require.NoError(err) 211 getBlk3, err := chainState.GetBlock(context.Background(), blk3.ID()) 212 require.NoError(err) 213 require.Equal(parsedBlk3.ID(), getBlk3.ID(), "State GetBlock returned the wrong block") 214 215 // Check that parsing blk3 does not add it to processing blocks since it has 216 // not been verified. 217 require.Len(chainState.verifiedBlocks, 2) 218 219 require.NoError(parsedBlk3.Verify(context.Background())) 220 // Check that blk3 has been added to processing blocks. 221 require.Len(chainState.verifiedBlocks, 3) 222 223 // Decide the blocks and ensure they are removed from the processing blocks map 224 require.NoError(parsedBlk1.Accept(context.Background())) 225 require.NoError(parsedBlk2.Accept(context.Background())) 226 require.NoError(parsedBlk3.Reject(context.Background())) 227 228 require.Empty(chainState.verifiedBlocks) 229 230 // Check that the last accepted block was updated correctly 231 lastAcceptedID, err := chainState.LastAccepted(context.Background()) 232 require.NoError(err) 233 require.Equal(blk2.ID(), lastAcceptedID) 234 require.Equal(blk2.ID(), chainState.LastAcceptedBlock().ID()) 235 236 // Flush the caches to ensure decided blocks are handled correctly on cache misses. 237 chainState.Flush() 238 checkDecidedBlock(t, chainState, wrappedGenesisBlk, false) 239 checkDecidedBlock(t, chainState, parsedBlk1, false) 240 checkDecidedBlock(t, chainState, parsedBlk2, false) 241 checkDecidedBlock(t, chainState, parsedBlk3, false) 242 } 243 244 func TestBuildBlock(t *testing.T) { 245 require := require.New(t) 246 247 testBlks := NewTestBlocks(2) 248 genesisBlock := testBlks[0] 249 genesisBlock.Status = snowtest.Accepted 250 blk1 := testBlks[1] 251 252 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 253 buildBlock := func(context.Context) (snowman.Block, error) { 254 // Once the block is built, mark it as processing 255 blk1.Status = snowtest.Undecided 256 return blk1, nil 257 } 258 259 chainState := NewState(&Config{ 260 DecidedCacheSize: defaultBlockCacheSize, 261 MissingCacheSize: defaultBlockCacheSize, 262 UnverifiedCacheSize: defaultBlockCacheSize, 263 BytesToIDCacheSize: defaultBlockCacheSize, 264 LastAcceptedBlock: genesisBlock, 265 GetBlock: getBlock, 266 UnmarshalBlock: parseBlock, 267 BuildBlock: buildBlock, 268 }) 269 270 builtBlk, err := chainState.BuildBlock(context.Background()) 271 require.NoError(err) 272 require.Empty(chainState.verifiedBlocks) 273 274 require.NoError(builtBlk.Verify(context.Background())) 275 require.Len(chainState.verifiedBlocks, 1) 276 277 checkProcessingBlock(t, chainState, builtBlk) 278 279 require.NoError(builtBlk.Accept(context.Background())) 280 281 checkDecidedBlock(t, chainState, builtBlk, true) 282 } 283 284 func TestStateDecideBlock(t *testing.T) { 285 require := require.New(t) 286 287 testBlks := NewTestBlocks(4) 288 genesisBlock := testBlks[0] 289 genesisBlock.Status = snowtest.Accepted 290 badAcceptBlk := testBlks[1] 291 badAcceptBlk.AcceptV = errAccept 292 badVerifyBlk := testBlks[2] 293 badVerifyBlk.VerifyV = errVerify 294 badRejectBlk := testBlks[3] 295 badRejectBlk.RejectV = errReject 296 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 297 chainState := NewState(&Config{ 298 DecidedCacheSize: defaultBlockCacheSize, 299 MissingCacheSize: defaultBlockCacheSize, 300 UnverifiedCacheSize: defaultBlockCacheSize, 301 BytesToIDCacheSize: defaultBlockCacheSize, 302 LastAcceptedBlock: genesisBlock, 303 GetBlock: getBlock, 304 UnmarshalBlock: parseBlock, 305 BuildBlock: cantBuildBlock, 306 }) 307 308 // Parse badVerifyBlk (which should fail verification) 309 badBlk, err := chainState.ParseBlock(context.Background(), badVerifyBlk.Bytes()) 310 require.NoError(err) 311 err = badBlk.Verify(context.Background()) 312 require.ErrorIs(err, errVerify) 313 // Ensure a block that fails verification is not marked as processing 314 require.Empty(chainState.verifiedBlocks) 315 316 // Ensure that an error during block acceptance is propagated correctly 317 badBlk, err = chainState.ParseBlock(context.Background(), badAcceptBlk.Bytes()) 318 require.NoError(err) 319 require.NoError(badBlk.Verify(context.Background())) 320 require.Len(chainState.verifiedBlocks, 1) 321 322 err = badBlk.Accept(context.Background()) 323 require.ErrorIs(err, errAccept) 324 325 // Ensure that an error during block reject is propagated correctly 326 badBlk, err = chainState.ParseBlock(context.Background(), badRejectBlk.Bytes()) 327 require.NoError(err) 328 require.NoError(badBlk.Verify(context.Background())) 329 // Note: an error during block Accept/Reject is fatal, so it is undefined whether 330 // the block that failed on Accept should be removed from processing or not. We allow 331 // either case here to make this test more flexible. 332 numProcessing := len(chainState.verifiedBlocks) 333 require.Contains([]int{1, 2}, numProcessing) 334 335 err = badBlk.Reject(context.Background()) 336 require.ErrorIs(err, errReject) 337 } 338 339 func TestStateParent(t *testing.T) { 340 require := require.New(t) 341 342 testBlks := NewTestBlocks(3) 343 genesisBlock := testBlks[0] 344 genesisBlock.Status = snowtest.Accepted 345 blk1 := testBlks[1] 346 blk2 := testBlks[2] 347 348 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 349 chainState := NewState(&Config{ 350 DecidedCacheSize: defaultBlockCacheSize, 351 MissingCacheSize: defaultBlockCacheSize, 352 UnverifiedCacheSize: defaultBlockCacheSize, 353 BytesToIDCacheSize: defaultBlockCacheSize, 354 LastAcceptedBlock: genesisBlock, 355 GetBlock: getBlock, 356 UnmarshalBlock: parseBlock, 357 BuildBlock: cantBuildBlock, 358 }) 359 360 parsedBlk2, err := chainState.ParseBlock(context.Background(), blk2.Bytes()) 361 require.NoError(err) 362 363 missingBlk1ID := parsedBlk2.Parent() 364 365 _, err = chainState.GetBlock(context.Background(), missingBlk1ID) 366 require.ErrorIs(err, database.ErrNotFound) 367 368 parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes()) 369 require.NoError(err) 370 371 genesisBlkParentID := parsedBlk1.Parent() 372 genesisBlkParent, err := chainState.GetBlock(context.Background(), genesisBlkParentID) 373 require.NoError(err) 374 checkDecidedBlock(t, chainState, genesisBlkParent, true) 375 376 parentBlk1ID := parsedBlk2.Parent() 377 parentBlk1, err := chainState.GetBlock(context.Background(), parentBlk1ID) 378 require.NoError(err) 379 checkProcessingBlock(t, chainState, parentBlk1) 380 } 381 382 func TestGetBlockInternal(t *testing.T) { 383 require := require.New(t) 384 testBlks := NewTestBlocks(1) 385 genesisBlock := testBlks[0] 386 genesisBlock.Status = snowtest.Accepted 387 388 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 389 chainState := NewState(&Config{ 390 DecidedCacheSize: defaultBlockCacheSize, 391 MissingCacheSize: defaultBlockCacheSize, 392 UnverifiedCacheSize: defaultBlockCacheSize, 393 BytesToIDCacheSize: defaultBlockCacheSize, 394 LastAcceptedBlock: genesisBlock, 395 GetBlock: getBlock, 396 UnmarshalBlock: parseBlock, 397 BuildBlock: cantBuildBlock, 398 }) 399 400 genesisBlockInternal := chainState.LastAcceptedBlockInternal() 401 require.IsType(&snowmantest.Block{}, genesisBlockInternal) 402 require.Equal(genesisBlock.ID(), genesisBlockInternal.ID()) 403 404 blk, err := chainState.GetBlockInternal(context.Background(), genesisBlock.ID()) 405 require.NoError(err) 406 407 require.IsType(&snowmantest.Block{}, blk) 408 require.Equal(genesisBlock.ID(), blk.ID()) 409 } 410 411 func TestGetBlockError(t *testing.T) { 412 require := require.New(t) 413 414 testBlks := NewTestBlocks(2) 415 genesisBlock := testBlks[0] 416 genesisBlock.Status = snowtest.Accepted 417 blk1 := testBlks[1] 418 419 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 420 wrappedGetBlock := func(ctx context.Context, id ids.ID) (snowman.Block, error) { 421 blk, err := getBlock(ctx, id) 422 if err != nil { 423 return nil, fmt.Errorf("wrapping error to prevent caching miss: %w", err) 424 } 425 return blk, nil 426 } 427 chainState := NewState(&Config{ 428 DecidedCacheSize: defaultBlockCacheSize, 429 MissingCacheSize: defaultBlockCacheSize, 430 UnverifiedCacheSize: defaultBlockCacheSize, 431 BytesToIDCacheSize: defaultBlockCacheSize, 432 LastAcceptedBlock: genesisBlock, 433 GetBlock: wrappedGetBlock, 434 UnmarshalBlock: parseBlock, 435 BuildBlock: cantBuildBlock, 436 }) 437 438 _, err := chainState.GetBlock(context.Background(), blk1.ID()) 439 require.ErrorIs(err, database.ErrNotFound) 440 441 // Update the status to Undecided, so that it will be returned by the 442 // internal get block function. 443 blk1.Status = snowtest.Undecided 444 blk, err := chainState.GetBlock(context.Background(), blk1.ID()) 445 require.NoError(err) 446 require.Equal(blk1.ID(), blk.ID()) 447 checkProcessingBlock(t, chainState, blk) 448 } 449 450 func TestParseBlockError(t *testing.T) { 451 testBlks := NewTestBlocks(1) 452 genesisBlock := testBlks[0] 453 genesisBlock.Status = snowtest.Accepted 454 455 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 456 chainState := NewState(&Config{ 457 DecidedCacheSize: defaultBlockCacheSize, 458 MissingCacheSize: defaultBlockCacheSize, 459 UnverifiedCacheSize: defaultBlockCacheSize, 460 BytesToIDCacheSize: defaultBlockCacheSize, 461 LastAcceptedBlock: genesisBlock, 462 GetBlock: getBlock, 463 UnmarshalBlock: parseBlock, 464 BuildBlock: cantBuildBlock, 465 }) 466 467 _, err := chainState.ParseBlock(context.Background(), []byte{255}) 468 require.ErrorIs(t, err, errUnexpectedBlockBytes) 469 } 470 471 func TestBuildBlockError(t *testing.T) { 472 testBlks := NewTestBlocks(1) 473 genesisBlock := testBlks[0] 474 genesisBlock.Status = snowtest.Accepted 475 476 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 477 chainState := NewState(&Config{ 478 DecidedCacheSize: defaultBlockCacheSize, 479 MissingCacheSize: defaultBlockCacheSize, 480 UnverifiedCacheSize: defaultBlockCacheSize, 481 BytesToIDCacheSize: defaultBlockCacheSize, 482 LastAcceptedBlock: genesisBlock, 483 GetBlock: getBlock, 484 UnmarshalBlock: parseBlock, 485 BuildBlock: cantBuildBlock, 486 }) 487 488 _, err := chainState.BuildBlock(context.Background()) 489 require.ErrorIs(t, err, errCantBuildBlock) 490 } 491 492 func TestMeteredCache(t *testing.T) { 493 require := require.New(t) 494 495 registry := prometheus.NewRegistry() 496 497 testBlks := NewTestBlocks(1) 498 genesisBlock := testBlks[0] 499 genesisBlock.Status = snowtest.Accepted 500 501 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 502 config := &Config{ 503 DecidedCacheSize: defaultBlockCacheSize, 504 MissingCacheSize: defaultBlockCacheSize, 505 UnverifiedCacheSize: defaultBlockCacheSize, 506 BytesToIDCacheSize: defaultBlockCacheSize, 507 LastAcceptedBlock: genesisBlock, 508 GetBlock: getBlock, 509 UnmarshalBlock: parseBlock, 510 BuildBlock: cantBuildBlock, 511 } 512 _, err := NewMeteredState(registry, config) 513 require.NoError(err) 514 _, err = NewMeteredState(registry, config) 515 require.Error(err) //nolint:forbidigo // error is not exported https://github.com/prometheus/client_golang/blob/main/prometheus/registry.go#L315 516 } 517 518 // Test the bytesToIDCache 519 func TestStateBytesToIDCache(t *testing.T) { 520 require := require.New(t) 521 522 testBlks := NewTestBlocks(3) 523 genesisBlock := testBlks[0] 524 genesisBlock.Status = snowtest.Accepted 525 blk1 := testBlks[1] 526 blk2 := testBlks[2] 527 528 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 529 buildBlock := func(context.Context) (snowman.Block, error) { 530 require.FailNow("shouldn't have been called") 531 return nil, nil 532 } 533 534 chainState := NewState(&Config{ 535 DecidedCacheSize: 0, 536 MissingCacheSize: 0, 537 UnverifiedCacheSize: 0, 538 BytesToIDCacheSize: 1 + ids.IDLen, // Size of one block 539 LastAcceptedBlock: genesisBlock, 540 GetBlock: getBlock, 541 UnmarshalBlock: parseBlock, 542 BuildBlock: buildBlock, 543 }) 544 545 // Shouldn't have blk1 ID to start with 546 _, err := chainState.GetBlock(context.Background(), blk1.ID()) 547 require.ErrorIs(err, database.ErrNotFound) 548 _, ok := chainState.bytesToIDCache.Get(string(blk1.Bytes())) 549 require.False(ok) 550 551 // Parse blk1 from bytes 552 _, err = chainState.ParseBlock(context.Background(), blk1.Bytes()) 553 require.NoError(err) 554 555 // blk1 should be in cache now 556 _, ok = chainState.bytesToIDCache.Get(string(blk1.Bytes())) 557 require.True(ok) 558 559 // Parse another block 560 _, err = chainState.ParseBlock(context.Background(), blk2.Bytes()) 561 require.NoError(err) 562 563 // Should have bumped blk1 from cache 564 _, ok = chainState.bytesToIDCache.Get(string(blk2.Bytes())) 565 require.True(ok) 566 _, ok = chainState.bytesToIDCache.Get(string(blk1.Bytes())) 567 require.False(ok) 568 } 569 570 // TestSetLastAcceptedBlock ensures chainState's last accepted block 571 // can be updated by calling [SetLastAcceptedBlock]. 572 func TestSetLastAcceptedBlock(t *testing.T) { 573 require := require.New(t) 574 575 testBlks := NewTestBlocks(1) 576 genesisBlock := testBlks[0] 577 genesisBlock.Status = snowtest.Accepted 578 579 postSetBlk1ParentID := hashing.ComputeHash256Array([]byte{byte(199)}) 580 postSetBlk1 := NewTestBlock(200, postSetBlk1ParentID) 581 postSetBlk2 := NewTestBlock(201, postSetBlk1.ID()) 582 583 // note we do not need to parse postSetBlk1 so it is omitted here 584 testBlks = append(testBlks, postSetBlk2) 585 586 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 587 chainState := NewState(&Config{ 588 DecidedCacheSize: defaultBlockCacheSize, 589 MissingCacheSize: defaultBlockCacheSize, 590 UnverifiedCacheSize: defaultBlockCacheSize, 591 BytesToIDCacheSize: defaultBlockCacheSize, 592 LastAcceptedBlock: genesisBlock, 593 GetBlock: getBlock, 594 UnmarshalBlock: parseBlock, 595 BuildBlock: cantBuildBlock, 596 }) 597 lastAcceptedID, err := chainState.LastAccepted(context.Background()) 598 require.NoError(err) 599 require.Equal(genesisBlock.ID(), lastAcceptedID) 600 601 // call SetLastAcceptedBlock for postSetBlk1 602 require.NoError(chainState.SetLastAcceptedBlock(postSetBlk1)) 603 lastAcceptedID, err = chainState.LastAccepted(context.Background()) 604 require.NoError(err) 605 require.Equal(postSetBlk1.ID(), lastAcceptedID) 606 require.Equal(postSetBlk1.ID(), chainState.LastAcceptedBlock().ID()) 607 608 // ensure further blocks can be accepted 609 parsedpostSetBlk2, err := chainState.ParseBlock(context.Background(), postSetBlk2.Bytes()) 610 require.NoError(err) 611 require.NoError(parsedpostSetBlk2.Verify(context.Background())) 612 require.NoError(parsedpostSetBlk2.Accept(context.Background())) 613 lastAcceptedID, err = chainState.LastAccepted(context.Background()) 614 require.NoError(err) 615 require.Equal(postSetBlk2.ID(), lastAcceptedID) 616 require.Equal(postSetBlk2.ID(), chainState.LastAcceptedBlock().ID()) 617 618 checkDecidedBlock(t, chainState, parsedpostSetBlk2, false) 619 } 620 621 func TestSetLastAcceptedBlockWithProcessingBlocksErrors(t *testing.T) { 622 require := require.New(t) 623 624 testBlks := NewTestBlocks(5) 625 genesisBlock := testBlks[0] 626 genesisBlock.Status = snowtest.Accepted 627 blk1 := testBlks[1] 628 resetBlk := testBlks[4] 629 630 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 631 buildBlock := func(context.Context) (snowman.Block, error) { 632 // Once the block is built, mark it as undecided 633 genesisBlock.Status = snowtest.Undecided 634 return blk1, nil 635 } 636 637 chainState := NewState(&Config{ 638 DecidedCacheSize: defaultBlockCacheSize, 639 MissingCacheSize: defaultBlockCacheSize, 640 UnverifiedCacheSize: defaultBlockCacheSize, 641 BytesToIDCacheSize: defaultBlockCacheSize, 642 LastAcceptedBlock: genesisBlock, 643 GetBlock: getBlock, 644 UnmarshalBlock: parseBlock, 645 BuildBlock: buildBlock, 646 }) 647 648 builtBlk, err := chainState.BuildBlock(context.Background()) 649 require.NoError(err) 650 require.Empty(chainState.verifiedBlocks) 651 652 require.NoError(builtBlk.Verify(context.Background())) 653 require.Len(chainState.verifiedBlocks, 1) 654 655 checkProcessingBlock(t, chainState, builtBlk) 656 657 err = chainState.SetLastAcceptedBlock(resetBlk) 658 require.ErrorIs(err, errSetAcceptedWithProcessing) 659 } 660 661 func TestStateParseTransitivelyAcceptedBlock(t *testing.T) { 662 require := require.New(t) 663 664 testBlks := NewTestBlocks(3) 665 genesisBlock := testBlks[0] 666 genesisBlock.Status = snowtest.Accepted 667 blk1 := testBlks[1] 668 blk2 := testBlks[2] 669 blk2.Status = snowtest.Accepted 670 671 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 672 chainState := NewState(&Config{ 673 DecidedCacheSize: defaultBlockCacheSize, 674 MissingCacheSize: defaultBlockCacheSize, 675 UnverifiedCacheSize: defaultBlockCacheSize, 676 BytesToIDCacheSize: defaultBlockCacheSize, 677 LastAcceptedBlock: blk2, 678 GetBlock: getBlock, 679 UnmarshalBlock: parseBlock, 680 BuildBlock: cantBuildBlock, 681 }) 682 683 parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes()) 684 require.NoError(err) 685 require.Equal(blk1.Height(), parsedBlk1.Height()) 686 } 687 688 func TestIsProcessing(t *testing.T) { 689 require := require.New(t) 690 691 testBlks := NewTestBlocks(2) 692 genesisBlock := testBlks[0] 693 genesisBlock.Status = snowtest.Accepted 694 blk1 := testBlks[1] 695 696 getBlock, parseBlock := createInternalBlockFuncs(testBlks) 697 chainState := NewState(&Config{ 698 DecidedCacheSize: defaultBlockCacheSize, 699 MissingCacheSize: defaultBlockCacheSize, 700 UnverifiedCacheSize: defaultBlockCacheSize, 701 BytesToIDCacheSize: defaultBlockCacheSize, 702 LastAcceptedBlock: genesisBlock, 703 GetBlock: getBlock, 704 UnmarshalBlock: parseBlock, 705 BuildBlock: cantBuildBlock, 706 }) 707 708 // Parse blk1 709 parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes()) 710 require.NoError(err) 711 712 // Check that it is not processing in consensus 713 require.False(chainState.IsProcessing(parsedBlk1.ID())) 714 715 // Verify blk1 716 require.NoError(parsedBlk1.Verify(context.Background())) 717 718 // Check that it is processing in consensus 719 require.True(chainState.IsProcessing(parsedBlk1.ID())) 720 721 // Accept blk1 722 require.NoError(parsedBlk1.Accept(context.Background())) 723 724 // Check that it is no longer processing in consensus 725 require.False(chainState.IsProcessing(parsedBlk1.ID())) 726 }