github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/post_fork_block_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 proposervm 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "testing" 11 "time" 12 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/snow/validators" 21 "github.com/ava-labs/avalanchego/utils/timer/mockable" 22 "github.com/ava-labs/avalanchego/vms/proposervm/block" 23 "github.com/ava-labs/avalanchego/vms/proposervm/proposer" 24 ) 25 26 var ( 27 errDuplicateVerify = errors.New("duplicate verify") 28 errUnexpectedBlockRejection = errors.New("unexpected block rejection") 29 ) 30 31 // ProposerBlock Option interface tests section 32 func TestOracle_PostForkBlock_ImplementsInterface(t *testing.T) { 33 require := require.New(t) 34 35 // setup 36 proBlk := postForkBlock{ 37 postForkCommonComponents: postForkCommonComponents{ 38 innerBlk: snowmantest.BuildChild(snowmantest.Genesis), 39 }, 40 } 41 42 // test 43 _, err := proBlk.Options(context.Background()) 44 require.Equal(snowman.ErrNotOracle, err) 45 46 // setup 47 var ( 48 activationTime = time.Unix(0, 0) 49 durangoTime = activationTime 50 ) 51 _, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 52 defer func() { 53 require.NoError(proVM.Shutdown(context.Background())) 54 }() 55 56 innerTestBlock := snowmantest.BuildChild(snowmantest.Genesis) 57 innerOracleBlk := &TestOptionsBlock{ 58 Block: *innerTestBlock, 59 opts: [2]*snowmantest.Block{ 60 snowmantest.BuildChild(innerTestBlock), 61 snowmantest.BuildChild(innerTestBlock), 62 }, 63 } 64 65 slb, err := block.Build( 66 ids.Empty, // refer unknown parent 67 time.Time{}, 68 0, // pChainHeight, 69 proVM.StakingCertLeaf, 70 innerOracleBlk.Bytes(), 71 proVM.ctx.ChainID, 72 proVM.StakingLeafSigner, 73 ) 74 require.NoError(err) 75 proBlk = postForkBlock{ 76 SignedBlock: slb, 77 postForkCommonComponents: postForkCommonComponents{ 78 vm: proVM, 79 innerBlk: innerOracleBlk, 80 }, 81 } 82 83 // test 84 _, err = proBlk.Options(context.Background()) 85 require.NoError(err) 86 } 87 88 // ProposerBlock.Verify tests section 89 func TestBlockVerify_PostForkBlock_PreDurango_ParentChecks(t *testing.T) { 90 require := require.New(t) 91 92 var ( 93 activationTime = time.Unix(0, 0) 94 durangoTime = mockable.MaxTime // pre Durango 95 ) 96 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 97 defer func() { 98 require.NoError(proVM.Shutdown(context.Background())) 99 }() 100 101 pChainHeight := uint64(100) 102 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 103 return pChainHeight, nil 104 } 105 106 // create parent block ... 107 parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis) 108 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 109 return parentCoreBlk, nil 110 } 111 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 112 switch blkID { 113 case snowmantest.GenesisID: 114 return snowmantest.Genesis, nil 115 case parentCoreBlk.ID(): 116 return parentCoreBlk, nil 117 default: 118 return nil, database.ErrNotFound 119 } 120 } 121 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 122 switch { 123 case bytes.Equal(b, snowmantest.GenesisBytes): 124 return snowmantest.Genesis, nil 125 case bytes.Equal(b, parentCoreBlk.Bytes()): 126 return parentCoreBlk, nil 127 default: 128 return nil, errUnknownBlock 129 } 130 } 131 132 parentBlk, err := proVM.BuildBlock(context.Background()) 133 require.NoError(err) 134 135 require.NoError(parentBlk.Verify(context.Background())) 136 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 137 138 // .. create child block ... 139 childCoreBlk := snowmantest.BuildChild(parentCoreBlk) 140 childBlk := postForkBlock{ 141 postForkCommonComponents: postForkCommonComponents{ 142 vm: proVM, 143 innerBlk: childCoreBlk, 144 }, 145 } 146 147 // set proVM to be able to build unsigned blocks 148 proVM.Set(proVM.Time().Add(proposer.MaxVerifyDelay)) 149 150 { 151 // child block referring unknown parent does not verify 152 childSlb, err := block.BuildUnsigned( 153 ids.Empty, // refer unknown parent 154 proVM.Time(), 155 pChainHeight, 156 childCoreBlk.Bytes(), 157 ) 158 require.NoError(err) 159 childBlk.SignedBlock = childSlb 160 161 err = childBlk.Verify(context.Background()) 162 require.ErrorIs(err, database.ErrNotFound) 163 } 164 165 { 166 // child block referring known parent does verify 167 childSlb, err := block.BuildUnsigned( 168 parentBlk.ID(), // refer known parent 169 proVM.Time(), 170 pChainHeight, 171 childCoreBlk.Bytes(), 172 ) 173 require.NoError(err) 174 childBlk.SignedBlock = childSlb 175 176 require.NoError(childBlk.Verify(context.Background())) 177 } 178 } 179 180 func TestBlockVerify_PostForkBlock_PostDurango_ParentChecks(t *testing.T) { 181 require := require.New(t) 182 183 var ( 184 activationTime = time.Unix(0, 0) 185 durangoTime = activationTime // post Durango 186 ) 187 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 188 defer func() { 189 require.NoError(proVM.Shutdown(context.Background())) 190 }() 191 192 pChainHeight := uint64(100) 193 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 194 return pChainHeight, nil 195 } 196 197 parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis) 198 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 199 return parentCoreBlk, nil 200 } 201 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 202 switch blkID { 203 case snowmantest.GenesisID: 204 return snowmantest.Genesis, nil 205 case parentCoreBlk.ID(): 206 return parentCoreBlk, nil 207 default: 208 return nil, database.ErrNotFound 209 } 210 } 211 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 212 switch { 213 case bytes.Equal(b, snowmantest.GenesisBytes): 214 return snowmantest.Genesis, nil 215 case bytes.Equal(b, parentCoreBlk.Bytes()): 216 return parentCoreBlk, nil 217 default: 218 return nil, errUnknownBlock 219 } 220 } 221 222 parentBlk, err := proVM.BuildBlock(context.Background()) 223 require.NoError(err) 224 225 require.NoError(parentBlk.Verify(context.Background())) 226 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 227 228 childCoreBlk := snowmantest.BuildChild(parentCoreBlk) 229 childBlk := postForkBlock{ 230 postForkCommonComponents: postForkCommonComponents{ 231 vm: proVM, 232 innerBlk: childCoreBlk, 233 }, 234 } 235 236 require.NoError(waitForProposerWindow(proVM, parentBlk, parentBlk.(*postForkBlock).PChainHeight())) 237 238 { 239 // child block referring unknown parent does not verify 240 childSlb, err := block.Build( 241 ids.Empty, // refer unknown parent 242 proVM.Time(), 243 pChainHeight, 244 proVM.StakingCertLeaf, 245 childCoreBlk.Bytes(), 246 proVM.ctx.ChainID, 247 proVM.StakingLeafSigner, 248 ) 249 require.NoError(err) 250 childBlk.SignedBlock = childSlb 251 252 err = childBlk.Verify(context.Background()) 253 require.ErrorIs(err, database.ErrNotFound) 254 } 255 256 { 257 // child block referring known parent does verify 258 childSlb, err := block.Build( 259 parentBlk.ID(), 260 proVM.Time(), 261 pChainHeight, 262 proVM.StakingCertLeaf, 263 childCoreBlk.Bytes(), 264 proVM.ctx.ChainID, 265 proVM.StakingLeafSigner, 266 ) 267 268 require.NoError(err) 269 childBlk.SignedBlock = childSlb 270 271 proVM.Set(childSlb.Timestamp()) 272 require.NoError(childBlk.Verify(context.Background())) 273 } 274 } 275 276 func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) { 277 require := require.New(t) 278 279 var ( 280 activationTime = time.Unix(0, 0) 281 durangoTime = mockable.MaxTime 282 ) 283 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 284 defer func() { 285 require.NoError(proVM.Shutdown(context.Background())) 286 }() 287 288 // reduce validator state to allow proVM.ctx.NodeID to be easily selected as proposer 289 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 290 var ( 291 thisNode = proVM.ctx.NodeID 292 nodeID1 = ids.BuildTestNodeID([]byte{1}) 293 ) 294 return map[ids.NodeID]*validators.GetValidatorOutput{ 295 thisNode: { 296 NodeID: thisNode, 297 Weight: 5, 298 }, 299 nodeID1: { 300 NodeID: nodeID1, 301 Weight: 100, 302 }, 303 }, nil 304 } 305 proVM.ctx.ValidatorState = valState 306 307 pChainHeight := uint64(100) 308 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 309 return pChainHeight, nil 310 } 311 312 // create parent block ... 313 parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis) 314 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 315 return parentCoreBlk, nil 316 } 317 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 318 switch blkID { 319 case snowmantest.GenesisID: 320 return snowmantest.Genesis, nil 321 case parentCoreBlk.ID(): 322 return parentCoreBlk, nil 323 default: 324 return nil, database.ErrNotFound 325 } 326 } 327 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 328 switch { 329 case bytes.Equal(b, snowmantest.GenesisBytes): 330 return snowmantest.Genesis, nil 331 case bytes.Equal(b, parentCoreBlk.Bytes()): 332 return parentCoreBlk, nil 333 default: 334 return nil, errUnknownBlock 335 } 336 } 337 338 parentBlk, err := proVM.BuildBlock(context.Background()) 339 require.NoError(err) 340 341 require.NoError(parentBlk.Verify(context.Background())) 342 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 343 344 var ( 345 parentTimestamp = parentBlk.Timestamp() 346 parentPChainHeight = parentBlk.(*postForkBlock).PChainHeight() 347 ) 348 349 childCoreBlk := snowmantest.BuildChild(parentCoreBlk) 350 childBlk := postForkBlock{ 351 postForkCommonComponents: postForkCommonComponents{ 352 vm: proVM, 353 innerBlk: childCoreBlk, 354 }, 355 } 356 357 { 358 // child block timestamp cannot be lower than parent timestamp 359 newTime := parentTimestamp.Add(-1 * time.Second) 360 proVM.Clock.Set(newTime) 361 362 childSlb, err := block.Build( 363 parentBlk.ID(), 364 newTime, 365 pChainHeight, 366 proVM.StakingCertLeaf, 367 childCoreBlk.Bytes(), 368 proVM.ctx.ChainID, 369 proVM.StakingLeafSigner, 370 ) 371 require.NoError(err) 372 childBlk.SignedBlock = childSlb 373 374 err = childBlk.Verify(context.Background()) 375 require.ErrorIs(err, errTimeNotMonotonic) 376 } 377 378 blkWinDelay, err := proVM.Delay(context.Background(), childCoreBlk.Height(), parentPChainHeight, proVM.ctx.NodeID, proposer.MaxVerifyWindows) 379 require.NoError(err) 380 381 { 382 // block cannot arrive before its creator window starts 383 beforeWinStart := parentTimestamp.Add(blkWinDelay).Add(-1 * time.Second) 384 proVM.Clock.Set(beforeWinStart) 385 386 childSlb, err := block.Build( 387 parentBlk.ID(), 388 beforeWinStart, 389 pChainHeight, 390 proVM.StakingCertLeaf, 391 childCoreBlk.Bytes(), 392 proVM.ctx.ChainID, 393 proVM.StakingLeafSigner, 394 ) 395 require.NoError(err) 396 childBlk.SignedBlock = childSlb 397 398 err = childBlk.Verify(context.Background()) 399 require.ErrorIs(err, errProposerWindowNotStarted) 400 } 401 402 { 403 // block can arrive at its creator window starts 404 atWindowStart := parentTimestamp.Add(blkWinDelay) 405 proVM.Clock.Set(atWindowStart) 406 407 childSlb, err := block.Build( 408 parentBlk.ID(), 409 atWindowStart, 410 pChainHeight, 411 proVM.StakingCertLeaf, 412 childCoreBlk.Bytes(), 413 proVM.ctx.ChainID, 414 proVM.StakingLeafSigner, 415 ) 416 require.NoError(err) 417 childBlk.SignedBlock = childSlb 418 419 require.NoError(childBlk.Verify(context.Background())) 420 } 421 422 { 423 // block can arrive after its creator window starts 424 afterWindowStart := parentTimestamp.Add(blkWinDelay).Add(5 * time.Second) 425 proVM.Clock.Set(afterWindowStart) 426 427 childSlb, err := block.Build( 428 parentBlk.ID(), 429 afterWindowStart, 430 pChainHeight, 431 proVM.StakingCertLeaf, 432 childCoreBlk.Bytes(), 433 proVM.ctx.ChainID, 434 proVM.StakingLeafSigner, 435 ) 436 require.NoError(err) 437 childBlk.SignedBlock = childSlb 438 439 require.NoError(childBlk.Verify(context.Background())) 440 } 441 442 { 443 // block can arrive within submission window 444 atSubWindowEnd := proVM.Time().Add(proposer.MaxVerifyDelay) 445 proVM.Clock.Set(atSubWindowEnd) 446 447 childSlb, err := block.BuildUnsigned( 448 parentBlk.ID(), 449 atSubWindowEnd, 450 pChainHeight, 451 childCoreBlk.Bytes(), 452 ) 453 require.NoError(err) 454 childBlk.SignedBlock = childSlb 455 456 require.NoError(childBlk.Verify(context.Background())) 457 } 458 459 { 460 // block timestamp cannot be too much in the future 461 afterSubWinEnd := proVM.Time().Add(maxSkew).Add(time.Second) 462 463 childSlb, err := block.Build( 464 parentBlk.ID(), 465 afterSubWinEnd, 466 pChainHeight, 467 proVM.StakingCertLeaf, 468 childCoreBlk.Bytes(), 469 proVM.ctx.ChainID, 470 proVM.StakingLeafSigner, 471 ) 472 require.NoError(err) 473 childBlk.SignedBlock = childSlb 474 475 err = childBlk.Verify(context.Background()) 476 require.ErrorIs(err, errTimeTooAdvanced) 477 } 478 } 479 480 func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) { 481 require := require.New(t) 482 483 var ( 484 activationTime = time.Unix(0, 0) 485 durangoTime = activationTime 486 ) 487 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 488 defer func() { 489 require.NoError(proVM.Shutdown(context.Background())) 490 }() 491 492 pChainHeight := uint64(100) 493 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 494 return pChainHeight, nil 495 } 496 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 497 return pChainHeight / 50, nil 498 } 499 500 // create parent block ... 501 parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis) 502 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 503 return parentCoreBlk, nil 504 } 505 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 506 switch blkID { 507 case snowmantest.GenesisID: 508 return snowmantest.Genesis, nil 509 case parentCoreBlk.ID(): 510 return parentCoreBlk, nil 511 default: 512 return nil, database.ErrNotFound 513 } 514 } 515 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 516 switch { 517 case bytes.Equal(b, snowmantest.GenesisBytes): 518 return snowmantest.Genesis, nil 519 case bytes.Equal(b, parentCoreBlk.Bytes()): 520 return parentCoreBlk, nil 521 default: 522 return nil, errUnknownBlock 523 } 524 } 525 526 parentBlk, err := proVM.BuildBlock(context.Background()) 527 require.NoError(err) 528 529 require.NoError(parentBlk.Verify(context.Background())) 530 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 531 532 // set VM to be ready to build next block. We set it to generate unsigned blocks 533 // for simplicity. 534 parentBlkPChainHeight := parentBlk.(*postForkBlock).PChainHeight() 535 require.NoError(waitForProposerWindow(proVM, parentBlk, parentBlkPChainHeight)) 536 537 childCoreBlk := snowmantest.BuildChild(parentCoreBlk) 538 childBlk := postForkBlock{ 539 postForkCommonComponents: postForkCommonComponents{ 540 vm: proVM, 541 innerBlk: childCoreBlk, 542 }, 543 } 544 545 { 546 // child P-Chain height must not precede parent P-Chain height 547 childSlb, err := block.Build( 548 parentBlk.ID(), 549 proVM.Time(), 550 parentBlkPChainHeight-1, 551 proVM.StakingCertLeaf, 552 childCoreBlk.Bytes(), 553 proVM.ctx.ChainID, 554 proVM.StakingLeafSigner, 555 ) 556 require.NoError(err) 557 childBlk.SignedBlock = childSlb 558 559 err = childBlk.Verify(context.Background()) 560 require.ErrorIs(err, errPChainHeightNotMonotonic) 561 } 562 563 { 564 // child P-Chain height can be equal to parent P-Chain height 565 childSlb, err := block.Build( 566 parentBlk.ID(), 567 proVM.Time(), 568 parentBlkPChainHeight, 569 proVM.StakingCertLeaf, 570 childCoreBlk.Bytes(), 571 proVM.ctx.ChainID, 572 proVM.StakingLeafSigner, 573 ) 574 require.NoError(err) 575 childBlk.SignedBlock = childSlb 576 577 require.NoError(childBlk.Verify(context.Background())) 578 } 579 580 { 581 // child P-Chain height may follow parent P-Chain height 582 childSlb, err := block.Build( 583 parentBlk.ID(), 584 proVM.Time(), 585 parentBlkPChainHeight, 586 proVM.StakingCertLeaf, 587 childCoreBlk.Bytes(), 588 proVM.ctx.ChainID, 589 proVM.StakingLeafSigner, 590 ) 591 require.NoError(err) 592 childBlk.SignedBlock = childSlb 593 594 require.NoError(childBlk.Verify(context.Background())) 595 } 596 597 currPChainHeight, _ := proVM.ctx.ValidatorState.GetCurrentHeight(context.Background()) 598 { 599 // block P-Chain height can be equal to current P-Chain height 600 childSlb, err := block.Build( 601 parentBlk.ID(), 602 proVM.Time(), 603 currPChainHeight, 604 proVM.StakingCertLeaf, 605 childCoreBlk.Bytes(), 606 proVM.ctx.ChainID, 607 proVM.StakingLeafSigner, 608 ) 609 require.NoError(err) 610 childBlk.SignedBlock = childSlb 611 612 require.NoError(childBlk.Verify(context.Background())) 613 } 614 615 { 616 // block P-Chain height cannot be at higher than current P-Chain height 617 childSlb, err := block.Build( 618 parentBlk.ID(), 619 proVM.Time(), 620 currPChainHeight*2, 621 proVM.StakingCertLeaf, 622 childCoreBlk.Bytes(), 623 proVM.ctx.ChainID, 624 proVM.StakingLeafSigner, 625 ) 626 require.NoError(err) 627 childBlk.SignedBlock = childSlb 628 629 err = childBlk.Verify(context.Background()) 630 require.ErrorIs(err, errPChainHeightNotReached) 631 } 632 } 633 634 func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) { 635 require := require.New(t) 636 637 var ( 638 activationTime = time.Unix(0, 0) 639 durangoTime = mockable.MaxTime 640 ) 641 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 642 defer func() { 643 require.NoError(proVM.Shutdown(context.Background())) 644 }() 645 646 pChainHeight := uint64(100) 647 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 648 return pChainHeight, nil 649 } 650 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 651 return pChainHeight / 50, nil 652 } 653 654 // create post fork oracle block ... 655 innerTestBlock := snowmantest.BuildChild(snowmantest.Genesis) 656 oracleCoreBlk := &TestOptionsBlock{ 657 Block: *innerTestBlock, 658 } 659 preferredOracleBlkChild := snowmantest.BuildChild(innerTestBlock) 660 oracleCoreBlk.opts = [2]*snowmantest.Block{ 661 preferredOracleBlkChild, 662 snowmantest.BuildChild(innerTestBlock), 663 } 664 665 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 666 return oracleCoreBlk, nil 667 } 668 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 669 switch blkID { 670 case snowmantest.GenesisID: 671 return snowmantest.Genesis, nil 672 case oracleCoreBlk.ID(): 673 return oracleCoreBlk, nil 674 case oracleCoreBlk.opts[0].ID(): 675 return oracleCoreBlk.opts[0], nil 676 case oracleCoreBlk.opts[1].ID(): 677 return oracleCoreBlk.opts[1], nil 678 default: 679 return nil, database.ErrNotFound 680 } 681 } 682 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 683 switch { 684 case bytes.Equal(b, snowmantest.GenesisBytes): 685 return snowmantest.Genesis, nil 686 case bytes.Equal(b, oracleCoreBlk.Bytes()): 687 return oracleCoreBlk, nil 688 case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()): 689 return oracleCoreBlk.opts[0], nil 690 case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()): 691 return oracleCoreBlk.opts[1], nil 692 default: 693 return nil, errUnknownBlock 694 } 695 } 696 697 oracleBlk, err := proVM.BuildBlock(context.Background()) 698 require.NoError(err) 699 700 require.NoError(oracleBlk.Verify(context.Background())) 701 require.NoError(proVM.SetPreference(context.Background(), oracleBlk.ID())) 702 703 // retrieve one option and verify block built on it 704 require.IsType(&postForkBlock{}, oracleBlk) 705 postForkOracleBlk := oracleBlk.(*postForkBlock) 706 opts, err := postForkOracleBlk.Options(context.Background()) 707 require.NoError(err) 708 parentBlk := opts[0] 709 710 require.NoError(parentBlk.Verify(context.Background())) 711 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 712 713 // set VM to be ready to build next block. We set it to generate unsigned blocks 714 // for simplicity. 715 nextTime := parentBlk.Timestamp().Add(proposer.MaxVerifyDelay) 716 proVM.Set(nextTime) 717 718 parentBlkPChainHeight := postForkOracleBlk.PChainHeight() // option takes proposal blocks' Pchain height 719 720 childCoreBlk := snowmantest.BuildChild(preferredOracleBlkChild) 721 childBlk := postForkBlock{ 722 postForkCommonComponents: postForkCommonComponents{ 723 vm: proVM, 724 innerBlk: childCoreBlk, 725 }, 726 } 727 728 { 729 // child P-Chain height must not precede parent P-Chain height 730 childSlb, err := block.BuildUnsigned( 731 parentBlk.ID(), 732 nextTime, 733 parentBlkPChainHeight-1, 734 childCoreBlk.Bytes(), 735 ) 736 require.NoError(err) 737 childBlk.SignedBlock = childSlb 738 739 err = childBlk.Verify(context.Background()) 740 require.ErrorIs(err, errPChainHeightNotMonotonic) 741 } 742 743 { 744 // child P-Chain height can be equal to parent P-Chain height 745 childSlb, err := block.BuildUnsigned( 746 parentBlk.ID(), 747 nextTime, 748 parentBlkPChainHeight, 749 childCoreBlk.Bytes(), 750 ) 751 require.NoError(err) 752 childBlk.SignedBlock = childSlb 753 754 require.NoError(childBlk.Verify(context.Background())) 755 } 756 757 { 758 // child P-Chain height may follow parent P-Chain height 759 childSlb, err := block.BuildUnsigned( 760 parentBlk.ID(), 761 nextTime, 762 parentBlkPChainHeight+1, 763 childCoreBlk.Bytes(), 764 ) 765 require.NoError(err) 766 childBlk.SignedBlock = childSlb 767 768 require.NoError(childBlk.Verify(context.Background())) 769 } 770 771 currPChainHeight, _ := proVM.ctx.ValidatorState.GetCurrentHeight(context.Background()) 772 { 773 // block P-Chain height can be equal to current P-Chain height 774 childSlb, err := block.BuildUnsigned( 775 parentBlk.ID(), 776 nextTime, 777 currPChainHeight, 778 childCoreBlk.Bytes(), 779 ) 780 require.NoError(err) 781 childBlk.SignedBlock = childSlb 782 783 require.NoError(childBlk.Verify(context.Background())) 784 } 785 786 { 787 // block P-Chain height cannot be at higher than current P-Chain height 788 childSlb, err := block.BuildUnsigned( 789 parentBlk.ID(), 790 nextTime, 791 currPChainHeight*2, 792 childCoreBlk.Bytes(), 793 ) 794 require.NoError(err) 795 childBlk.SignedBlock = childSlb 796 err = childBlk.Verify(context.Background()) 797 require.ErrorIs(err, errPChainHeightNotReached) 798 } 799 } 800 801 func TestBlockVerify_PostForkBlock_CoreBlockVerifyIsCalledOnce(t *testing.T) { 802 require := require.New(t) 803 804 // Verify a block once (in this test by building it). 805 // Show that other verify call would not call coreBlk.Verify() 806 var ( 807 activationTime = time.Unix(0, 0) 808 durangoTime = activationTime 809 ) 810 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 811 defer func() { 812 require.NoError(proVM.Shutdown(context.Background())) 813 }() 814 815 pChainHeight := uint64(2000) 816 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 817 return pChainHeight, nil 818 } 819 820 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 821 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 822 return coreBlk, nil 823 } 824 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 825 switch blkID { 826 case snowmantest.GenesisID: 827 return snowmantest.Genesis, nil 828 case coreBlk.ID(): 829 return coreBlk, nil 830 default: 831 return nil, database.ErrNotFound 832 } 833 } 834 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 835 switch { 836 case bytes.Equal(b, snowmantest.GenesisBytes): 837 return snowmantest.Genesis, nil 838 case bytes.Equal(b, coreBlk.Bytes()): 839 return coreBlk, nil 840 default: 841 return nil, errUnknownBlock 842 } 843 } 844 845 builtBlk, err := proVM.BuildBlock(context.Background()) 846 require.NoError(err) 847 848 require.NoError(builtBlk.Verify(context.Background())) 849 850 // set error on coreBlock.Verify and recall Verify() 851 coreBlk.VerifyV = errDuplicateVerify 852 require.NoError(builtBlk.Verify(context.Background())) 853 854 // rebuild a block with the same core block 855 pChainHeight++ 856 _, err = proVM.BuildBlock(context.Background()) 857 require.NoError(err) 858 } 859 860 // ProposerBlock.Accept tests section 861 func TestBlockAccept_PostForkBlock_SetsLastAcceptedBlock(t *testing.T) { 862 require := require.New(t) 863 864 // setup 865 var ( 866 activationTime = time.Unix(0, 0) 867 durangoTime = activationTime 868 ) 869 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 870 defer func() { 871 require.NoError(proVM.Shutdown(context.Background())) 872 }() 873 874 pChainHeight := uint64(2000) 875 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 876 return pChainHeight, nil 877 } 878 879 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 880 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 881 return coreBlk, nil 882 } 883 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 884 switch blkID { 885 case snowmantest.GenesisID: 886 return snowmantest.Genesis, nil 887 case coreBlk.ID(): 888 return coreBlk, nil 889 default: 890 return nil, database.ErrNotFound 891 } 892 } 893 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 894 switch { 895 case bytes.Equal(b, snowmantest.GenesisBytes): 896 return snowmantest.Genesis, nil 897 case bytes.Equal(b, coreBlk.Bytes()): 898 return coreBlk, nil 899 default: 900 return nil, errUnknownBlock 901 } 902 } 903 904 builtBlk, err := proVM.BuildBlock(context.Background()) 905 require.NoError(err) 906 907 // test 908 require.NoError(builtBlk.Accept(context.Background())) 909 910 coreVM.LastAcceptedF = snowmantest.MakeLastAcceptedBlockF( 911 []*snowmantest.Block{ 912 snowmantest.Genesis, 913 coreBlk, 914 }, 915 ) 916 acceptedID, err := proVM.LastAccepted(context.Background()) 917 require.NoError(err) 918 require.Equal(builtBlk.ID(), acceptedID) 919 } 920 921 func TestBlockAccept_PostForkBlock_TwoProBlocksWithSameCoreBlock_OneIsAccepted(t *testing.T) { 922 require := require.New(t) 923 924 var ( 925 activationTime = time.Unix(0, 0) 926 durangoTime = activationTime 927 ) 928 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 929 defer func() { 930 require.NoError(proVM.Shutdown(context.Background())) 931 }() 932 933 var minimumHeight uint64 934 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 935 return minimumHeight, nil 936 } 937 938 // generate two blocks with the same core block and store them 939 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 940 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 941 return coreBlk, nil 942 } 943 944 minimumHeight = snowmantest.GenesisHeight 945 946 proBlk1, err := proVM.BuildBlock(context.Background()) 947 require.NoError(err) 948 949 minimumHeight++ 950 proBlk2, err := proVM.BuildBlock(context.Background()) 951 require.NoError(err) 952 require.NotEqual(proBlk2.ID(), proBlk1.ID()) 953 954 // set proBlk1 as preferred 955 require.NoError(proBlk1.Accept(context.Background())) 956 require.Equal(snowtest.Accepted, coreBlk.Status) 957 958 acceptedID, err := proVM.LastAccepted(context.Background()) 959 require.NoError(err) 960 require.Equal(proBlk1.ID(), acceptedID) 961 } 962 963 // ProposerBlock.Reject tests section 964 func TestBlockReject_PostForkBlock_InnerBlockIsNotRejected(t *testing.T) { 965 require := require.New(t) 966 967 var ( 968 activationTime = time.Unix(0, 0) 969 durangoTime = activationTime 970 ) 971 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 972 defer func() { 973 require.NoError(proVM.Shutdown(context.Background())) 974 }() 975 976 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 977 coreBlk.RejectV = errUnexpectedBlockRejection 978 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 979 return coreBlk, nil 980 } 981 982 sb, err := proVM.BuildBlock(context.Background()) 983 require.NoError(err) 984 require.IsType(&postForkBlock{}, sb) 985 proBlk := sb.(*postForkBlock) 986 987 require.NoError(proBlk.Reject(context.Background())) 988 } 989 990 func TestBlockVerify_PostForkBlock_ShouldBePostForkOption(t *testing.T) { 991 require := require.New(t) 992 993 var ( 994 activationTime = time.Unix(0, 0) 995 durangoTime = activationTime 996 ) 997 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 998 defer func() { 999 require.NoError(proVM.Shutdown(context.Background())) 1000 }() 1001 1002 // create post fork oracle block ... 1003 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 1004 oracleCoreBlk := &TestOptionsBlock{ 1005 Block: *coreTestBlk, 1006 opts: [2]*snowmantest.Block{ 1007 snowmantest.BuildChild(coreTestBlk), 1008 snowmantest.BuildChild(coreTestBlk), 1009 }, 1010 } 1011 1012 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1013 return oracleCoreBlk, nil 1014 } 1015 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1016 switch blkID { 1017 case snowmantest.GenesisID: 1018 return snowmantest.Genesis, nil 1019 case oracleCoreBlk.ID(): 1020 return oracleCoreBlk, nil 1021 case oracleCoreBlk.opts[0].ID(): 1022 return oracleCoreBlk.opts[0], nil 1023 case oracleCoreBlk.opts[1].ID(): 1024 return oracleCoreBlk.opts[1], nil 1025 default: 1026 return nil, database.ErrNotFound 1027 } 1028 } 1029 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1030 switch { 1031 case bytes.Equal(b, snowmantest.GenesisBytes): 1032 return snowmantest.Genesis, nil 1033 case bytes.Equal(b, oracleCoreBlk.Bytes()): 1034 return oracleCoreBlk, nil 1035 case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()): 1036 return oracleCoreBlk.opts[0], nil 1037 case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()): 1038 return oracleCoreBlk.opts[1], nil 1039 default: 1040 return nil, errUnknownBlock 1041 } 1042 } 1043 1044 parentBlk, err := proVM.BuildBlock(context.Background()) 1045 require.NoError(err) 1046 1047 require.NoError(parentBlk.Verify(context.Background())) 1048 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 1049 1050 // retrieve options ... 1051 require.IsType(&postForkBlock{}, parentBlk) 1052 postForkOracleBlk := parentBlk.(*postForkBlock) 1053 opts, err := postForkOracleBlk.Options(context.Background()) 1054 require.NoError(err) 1055 require.IsType(&postForkOption{}, opts[0]) 1056 1057 // ... and verify them the first time 1058 require.NoError(opts[0].Verify(context.Background())) 1059 require.NoError(opts[1].Verify(context.Background())) 1060 1061 // Build the child 1062 statelessChild, err := block.Build( 1063 postForkOracleBlk.ID(), 1064 postForkOracleBlk.Timestamp().Add(proposer.WindowDuration), 1065 postForkOracleBlk.PChainHeight(), 1066 proVM.StakingCertLeaf, 1067 oracleCoreBlk.opts[0].Bytes(), 1068 proVM.ctx.ChainID, 1069 proVM.StakingLeafSigner, 1070 ) 1071 require.NoError(err) 1072 1073 invalidChild, err := proVM.ParseBlock(context.Background(), statelessChild.Bytes()) 1074 if err != nil { 1075 // A failure to parse is okay here 1076 return 1077 } 1078 1079 err = invalidChild.Verify(context.Background()) 1080 require.ErrorIs(err, errUnexpectedBlockType) 1081 } 1082 1083 func TestBlockVerify_PostForkBlock_PChainTooLow(t *testing.T) { 1084 require := require.New(t) 1085 1086 var ( 1087 activationTime = time.Unix(0, 0) 1088 durangoTime = activationTime 1089 ) 1090 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 5) 1091 defer func() { 1092 require.NoError(proVM.Shutdown(context.Background())) 1093 }() 1094 1095 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 1096 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1097 switch blkID { 1098 case snowmantest.GenesisID: 1099 return snowmantest.Genesis, nil 1100 case coreBlk.ID(): 1101 return coreBlk, nil 1102 default: 1103 return nil, database.ErrNotFound 1104 } 1105 } 1106 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1107 switch { 1108 case bytes.Equal(b, snowmantest.GenesisBytes): 1109 return snowmantest.Genesis, nil 1110 case bytes.Equal(b, coreBlk.Bytes()): 1111 return coreBlk, nil 1112 default: 1113 return nil, errUnknownBlock 1114 } 1115 } 1116 1117 statelessChild, err := block.BuildUnsigned( 1118 snowmantest.GenesisID, 1119 snowmantest.GenesisTimestamp, 1120 4, 1121 coreBlk.Bytes(), 1122 ) 1123 require.NoError(err) 1124 1125 invalidChild, err := proVM.ParseBlock(context.Background(), statelessChild.Bytes()) 1126 if err != nil { 1127 // A failure to parse is okay here 1128 return 1129 } 1130 1131 err = invalidChild.Verify(context.Background()) 1132 require.ErrorIs(err, errPChainHeightTooLow) 1133 }