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