github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/vm_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 "crypto" 10 "errors" 11 "fmt" 12 "testing" 13 "time" 14 15 "github.com/prometheus/client_golang/prometheus" 16 "github.com/stretchr/testify/require" 17 "go.uber.org/mock/gomock" 18 19 "github.com/MetalBlockchain/metalgo/database" 20 "github.com/MetalBlockchain/metalgo/database/memdb" 21 "github.com/MetalBlockchain/metalgo/database/prefixdb" 22 "github.com/MetalBlockchain/metalgo/ids" 23 "github.com/MetalBlockchain/metalgo/snow" 24 "github.com/MetalBlockchain/metalgo/snow/choices" 25 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 26 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest" 27 "github.com/MetalBlockchain/metalgo/snow/engine/common" 28 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 29 "github.com/MetalBlockchain/metalgo/snow/snowtest" 30 "github.com/MetalBlockchain/metalgo/snow/validators" 31 "github.com/MetalBlockchain/metalgo/staking" 32 "github.com/MetalBlockchain/metalgo/utils" 33 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 34 "github.com/MetalBlockchain/metalgo/vms/proposervm/proposer" 35 "github.com/MetalBlockchain/metalgo/vms/proposervm/state" 36 37 statelessblock "github.com/MetalBlockchain/metalgo/vms/proposervm/block" 38 ) 39 40 var ( 41 _ block.ChainVM = (*fullVM)(nil) 42 _ block.StateSyncableVM = (*fullVM)(nil) 43 ) 44 45 type fullVM struct { 46 *block.TestVM 47 *block.TestStateSyncableVM 48 } 49 50 var ( 51 pTestSigner crypto.Signer 52 pTestCert *staking.Certificate 53 54 defaultPChainHeight uint64 = 2000 55 56 errUnknownBlock = errors.New("unknown block") 57 errUnverifiedBlock = errors.New("unverified block") 58 errMarshallingFailed = errors.New("marshalling failed") 59 errTooHigh = errors.New("too high") 60 errUnexpectedCall = errors.New("unexpected call") 61 ) 62 63 func init() { 64 tlsCert, err := staking.NewTLSCert() 65 if err != nil { 66 panic(err) 67 } 68 pTestSigner = tlsCert.PrivateKey.(crypto.Signer) 69 pTestCert, err = staking.ParseCertificate(tlsCert.Leaf.Raw) 70 if err != nil { 71 panic(err) 72 } 73 } 74 75 func initTestProposerVM( 76 t *testing.T, 77 proBlkStartTime time.Time, 78 durangoTime time.Time, 79 minPChainHeight uint64, 80 ) ( 81 *fullVM, 82 *validators.TestState, 83 *VM, 84 database.Database, 85 ) { 86 require := require.New(t) 87 88 initialState := []byte("genesis state") 89 coreVM := &fullVM{ 90 TestVM: &block.TestVM{ 91 TestVM: common.TestVM{ 92 T: t, 93 }, 94 }, 95 TestStateSyncableVM: &block.TestStateSyncableVM{ 96 T: t, 97 }, 98 } 99 100 coreVM.InitializeF = func(context.Context, *snow.Context, database.Database, 101 []byte, []byte, []byte, chan<- common.Message, 102 []*common.Fx, common.AppSender, 103 ) error { 104 return nil 105 } 106 coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) { 107 return snowmantest.GenesisID, 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 default: 114 return nil, errUnknownBlock 115 } 116 } 117 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 118 switch { 119 case bytes.Equal(b, snowmantest.GenesisBytes): 120 return snowmantest.Genesis, nil 121 default: 122 return nil, errUnknownBlock 123 } 124 } 125 126 proVM := New( 127 coreVM, 128 Config{ 129 ActivationTime: proBlkStartTime, 130 DurangoTime: durangoTime, 131 MinimumPChainHeight: minPChainHeight, 132 MinBlkDelay: DefaultMinBlockDelay, 133 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 134 StakingLeafSigner: pTestSigner, 135 StakingCertLeaf: pTestCert, 136 Registerer: prometheus.NewRegistry(), 137 }, 138 ) 139 140 valState := &validators.TestState{ 141 T: t, 142 } 143 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 144 return snowmantest.GenesisHeight, nil 145 } 146 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 147 return defaultPChainHeight, nil 148 } 149 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 150 var ( 151 thisNode = proVM.ctx.NodeID 152 nodeID1 = ids.BuildTestNodeID([]byte{1}) 153 nodeID2 = ids.BuildTestNodeID([]byte{2}) 154 nodeID3 = ids.BuildTestNodeID([]byte{3}) 155 ) 156 return map[ids.NodeID]*validators.GetValidatorOutput{ 157 thisNode: { 158 NodeID: thisNode, 159 Weight: 10, 160 }, 161 nodeID1: { 162 NodeID: nodeID1, 163 Weight: 5, 164 }, 165 nodeID2: { 166 NodeID: nodeID2, 167 Weight: 6, 168 }, 169 nodeID3: { 170 NodeID: nodeID3, 171 Weight: 7, 172 }, 173 }, nil 174 } 175 176 ctx := snowtest.Context(t, ids.ID{1}) 177 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 178 ctx.ValidatorState = valState 179 180 db := prefixdb.New([]byte{0}, memdb.New()) 181 182 require.NoError(proVM.Initialize( 183 context.Background(), 184 ctx, 185 db, 186 initialState, 187 nil, 188 nil, 189 nil, 190 nil, 191 nil, 192 )) 193 194 // Initialize shouldn't be called again 195 coreVM.InitializeF = nil 196 197 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 198 require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID)) 199 200 proVM.Set(snowmantest.GenesisTimestamp) 201 202 return coreVM, valState, proVM, db 203 } 204 205 func waitForProposerWindow(vm *VM, chainTip snowman.Block, pchainHeight uint64) error { 206 var ( 207 ctx = context.Background() 208 childBlockHeight = chainTip.Height() + 1 209 parentTimestamp = chainTip.Timestamp() 210 ) 211 212 for { 213 slot := proposer.TimeToSlot(parentTimestamp, vm.Clock.Time().Truncate(time.Second)) 214 delay, err := vm.MinDelayForProposer( 215 ctx, 216 childBlockHeight, 217 pchainHeight, 218 vm.ctx.NodeID, 219 slot, 220 ) 221 if err != nil { 222 return err 223 } 224 225 vm.Clock.Set(parentTimestamp.Add(delay)) 226 if delay < proposer.MaxLookAheadWindow { 227 return nil 228 } 229 } 230 } 231 232 // VM.BuildBlock tests section 233 234 func TestBuildBlockTimestampAreRoundedToSeconds(t *testing.T) { 235 require := require.New(t) 236 237 // given the same core block, BuildBlock returns the same proposer block 238 var ( 239 activationTime = time.Unix(0, 0) 240 durangoTime = activationTime 241 ) 242 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 243 defer func() { 244 require.NoError(proVM.Shutdown(context.Background())) 245 }() 246 247 skewedTimestamp := time.Now().Truncate(time.Second).Add(time.Millisecond) 248 proVM.Set(skewedTimestamp) 249 250 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 251 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 252 return coreBlk, nil 253 } 254 255 // test 256 builtBlk, err := proVM.BuildBlock(context.Background()) 257 require.NoError(err) 258 259 require.Equal(builtBlk.Timestamp().Truncate(time.Second), builtBlk.Timestamp()) 260 } 261 262 func TestBuildBlockIsIdempotent(t *testing.T) { 263 require := require.New(t) 264 265 // given the same core block, BuildBlock returns the same proposer block 266 var ( 267 activationTime = time.Unix(0, 0) 268 durangoTime = activationTime 269 ) 270 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 271 defer func() { 272 require.NoError(proVM.Shutdown(context.Background())) 273 }() 274 275 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 276 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 277 return coreBlk, nil 278 } 279 280 // Mock the clock time to make sure that block timestamps will be equal 281 proVM.Clock.Set(time.Now()) 282 283 builtBlk1, err := proVM.BuildBlock(context.Background()) 284 require.NoError(err) 285 286 builtBlk2, err := proVM.BuildBlock(context.Background()) 287 require.NoError(err) 288 289 require.Equal(builtBlk1.Bytes(), builtBlk2.Bytes()) 290 } 291 292 func TestFirstProposerBlockIsBuiltOnTopOfGenesis(t *testing.T) { 293 require := require.New(t) 294 295 // setup 296 var ( 297 activationTime = time.Unix(0, 0) 298 durangoTime = activationTime 299 ) 300 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 301 defer func() { 302 require.NoError(proVM.Shutdown(context.Background())) 303 }() 304 305 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 306 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 307 return coreBlk, nil 308 } 309 310 // test 311 snowBlock, err := proVM.BuildBlock(context.Background()) 312 require.NoError(err) 313 314 // checks 315 require.IsType(&postForkBlock{}, snowBlock) 316 proBlock := snowBlock.(*postForkBlock) 317 318 require.Equal(coreBlk, proBlock.innerBlk) 319 } 320 321 // both core blocks and pro blocks must be built on preferred 322 func TestProposerBlocksAreBuiltOnPreferredProBlock(t *testing.T) { 323 require := require.New(t) 324 325 var ( 326 activationTime = time.Unix(0, 0) 327 durangoTime = activationTime 328 ) 329 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 330 defer func() { 331 require.NoError(proVM.Shutdown(context.Background())) 332 }() 333 334 // add two proBlks... 335 coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis) 336 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 337 return coreBlk1, nil 338 } 339 proBlk1, err := proVM.BuildBlock(context.Background()) 340 require.NoError(err) 341 342 coreBlk2 := snowmantest.BuildChild(snowmantest.Genesis) 343 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 344 return coreBlk2, nil 345 } 346 proBlk2, err := proVM.BuildBlock(context.Background()) 347 require.NoError(err) 348 require.NotEqual(proBlk2.ID(), proBlk1.ID()) 349 require.NoError(proBlk2.Verify(context.Background())) 350 351 // ...and set one as preferred 352 var prefcoreBlk *snowmantest.Block 353 coreVM.SetPreferenceF = func(_ context.Context, prefID ids.ID) error { 354 switch prefID { 355 case coreBlk1.ID(): 356 prefcoreBlk = coreBlk1 357 return nil 358 case coreBlk2.ID(): 359 prefcoreBlk = coreBlk2 360 return nil 361 default: 362 require.FailNow("prefID does not match coreBlk1 or coreBlk2") 363 return nil 364 } 365 } 366 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 367 switch { 368 case bytes.Equal(b, coreBlk1.Bytes()): 369 return coreBlk1, nil 370 case bytes.Equal(b, coreBlk2.Bytes()): 371 return coreBlk2, nil 372 default: 373 require.FailNow("bytes do not match coreBlk1 or coreBlk2") 374 return nil, nil 375 } 376 } 377 378 require.NoError(proVM.SetPreference(context.Background(), proBlk2.ID())) 379 380 // build block... 381 coreBlk3 := snowmantest.BuildChild(prefcoreBlk) 382 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 383 return coreBlk3, nil 384 } 385 386 require.NoError(waitForProposerWindow(proVM, proBlk2, proBlk2.(*postForkBlock).PChainHeight())) 387 builtBlk, err := proVM.BuildBlock(context.Background()) 388 require.NoError(err) 389 390 // ...show that parent is the preferred one 391 require.Equal(proBlk2.ID(), builtBlk.Parent()) 392 } 393 394 func TestCoreBlocksMustBeBuiltOnPreferredCoreBlock(t *testing.T) { 395 require := require.New(t) 396 397 var ( 398 activationTime = time.Unix(0, 0) 399 durangoTime = activationTime 400 ) 401 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 402 defer func() { 403 require.NoError(proVM.Shutdown(context.Background())) 404 }() 405 406 coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis) 407 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 408 return coreBlk1, nil 409 } 410 proBlk1, err := proVM.BuildBlock(context.Background()) 411 require.NoError(err) 412 413 coreBlk2 := snowmantest.BuildChild(snowmantest.Genesis) 414 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 415 return coreBlk2, nil 416 } 417 proBlk2, err := proVM.BuildBlock(context.Background()) 418 require.NoError(err) 419 require.NotEqual(proBlk1.ID(), proBlk2.ID()) 420 421 require.NoError(proBlk2.Verify(context.Background())) 422 423 // ...and set one as preferred 424 var wronglyPreferredcoreBlk *snowmantest.Block 425 coreVM.SetPreferenceF = func(_ context.Context, prefID ids.ID) error { 426 switch prefID { 427 case coreBlk1.ID(): 428 wronglyPreferredcoreBlk = coreBlk2 429 return nil 430 case coreBlk2.ID(): 431 wronglyPreferredcoreBlk = coreBlk1 432 return nil 433 default: 434 require.FailNow("Unknown core Blocks set as preferred") 435 return nil 436 } 437 } 438 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 439 switch { 440 case bytes.Equal(b, coreBlk1.Bytes()): 441 return coreBlk1, nil 442 case bytes.Equal(b, coreBlk2.Bytes()): 443 return coreBlk2, nil 444 default: 445 require.FailNow("Wrong bytes") 446 return nil, nil 447 } 448 } 449 450 require.NoError(proVM.SetPreference(context.Background(), proBlk2.ID())) 451 452 // build block... 453 coreBlk3 := snowmantest.BuildChild(wronglyPreferredcoreBlk) 454 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 455 return coreBlk3, nil 456 } 457 458 require.NoError(waitForProposerWindow(proVM, proBlk2, proBlk2.(*postForkBlock).PChainHeight())) 459 blk, err := proVM.BuildBlock(context.Background()) 460 require.NoError(err) 461 462 err = blk.Verify(context.Background()) 463 require.ErrorIs(err, errInnerParentMismatch) 464 } 465 466 // VM.ParseBlock tests section 467 func TestCoreBlockFailureCauseProposerBlockParseFailure(t *testing.T) { 468 require := require.New(t) 469 470 var ( 471 activationTime = time.Unix(0, 0) 472 durangoTime = activationTime 473 ) 474 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 475 defer func() { 476 require.NoError(proVM.Shutdown(context.Background())) 477 }() 478 479 coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) { 480 return nil, errMarshallingFailed 481 } 482 483 innerBlk := snowmantest.BuildChild(snowmantest.Genesis) 484 slb, err := statelessblock.Build( 485 proVM.preferred, 486 proVM.Time(), 487 100, // pChainHeight, 488 proVM.StakingCertLeaf, 489 innerBlk.Bytes(), 490 proVM.ctx.ChainID, 491 proVM.StakingLeafSigner, 492 ) 493 require.NoError(err) 494 proBlk := postForkBlock{ 495 SignedBlock: slb, 496 postForkCommonComponents: postForkCommonComponents{ 497 vm: proVM, 498 innerBlk: innerBlk, 499 status: choices.Processing, 500 }, 501 } 502 503 // test 504 _, err = proVM.ParseBlock(context.Background(), proBlk.Bytes()) 505 require.ErrorIs(err, errMarshallingFailed) 506 } 507 508 func TestTwoProBlocksWrappingSameCoreBlockCanBeParsed(t *testing.T) { 509 require := require.New(t) 510 511 var ( 512 activationTime = time.Unix(0, 0) 513 durangoTime = activationTime 514 ) 515 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 516 defer func() { 517 require.NoError(proVM.Shutdown(context.Background())) 518 }() 519 520 // create two Proposer blocks at the same height 521 innerBlk := snowmantest.BuildChild(snowmantest.Genesis) 522 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 523 require.Equal(innerBlk.Bytes(), b) 524 return innerBlk, nil 525 } 526 527 blkTimestamp := proVM.Time() 528 529 slb1, err := statelessblock.Build( 530 proVM.preferred, 531 blkTimestamp, 532 100, // pChainHeight, 533 proVM.StakingCertLeaf, 534 innerBlk.Bytes(), 535 proVM.ctx.ChainID, 536 proVM.StakingLeafSigner, 537 ) 538 require.NoError(err) 539 proBlk1 := postForkBlock{ 540 SignedBlock: slb1, 541 postForkCommonComponents: postForkCommonComponents{ 542 vm: proVM, 543 innerBlk: innerBlk, 544 status: choices.Processing, 545 }, 546 } 547 548 slb2, err := statelessblock.Build( 549 proVM.preferred, 550 blkTimestamp, 551 200, // pChainHeight, 552 proVM.StakingCertLeaf, 553 innerBlk.Bytes(), 554 proVM.ctx.ChainID, 555 proVM.StakingLeafSigner, 556 ) 557 require.NoError(err) 558 proBlk2 := postForkBlock{ 559 SignedBlock: slb2, 560 postForkCommonComponents: postForkCommonComponents{ 561 vm: proVM, 562 innerBlk: innerBlk, 563 status: choices.Processing, 564 }, 565 } 566 567 require.NotEqual(proBlk1.ID(), proBlk2.ID()) 568 569 // Show that both can be parsed and retrieved 570 parsedBlk1, err := proVM.ParseBlock(context.Background(), proBlk1.Bytes()) 571 require.NoError(err) 572 parsedBlk2, err := proVM.ParseBlock(context.Background(), proBlk2.Bytes()) 573 require.NoError(err) 574 575 require.Equal(proBlk1.ID(), parsedBlk1.ID()) 576 require.Equal(proBlk2.ID(), parsedBlk2.ID()) 577 } 578 579 // VM.BuildBlock and VM.ParseBlock interoperability tests section 580 func TestTwoProBlocksWithSameParentCanBothVerify(t *testing.T) { 581 require := require.New(t) 582 583 var ( 584 activationTime = time.Unix(0, 0) 585 durangoTime = activationTime 586 ) 587 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 588 defer func() { 589 require.NoError(proVM.Shutdown(context.Background())) 590 }() 591 592 // one block is built from this proVM 593 localcoreBlk := snowmantest.BuildChild(snowmantest.Genesis) 594 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 595 return localcoreBlk, nil 596 } 597 598 builtBlk, err := proVM.BuildBlock(context.Background()) 599 require.NoError(err) 600 require.NoError(builtBlk.Verify(context.Background())) 601 602 // another block with same parent comes from network and is parsed 603 netcoreBlk := snowmantest.BuildChild(snowmantest.Genesis) 604 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 605 switch { 606 case bytes.Equal(b, snowmantest.GenesisBytes): 607 return snowmantest.Genesis, nil 608 case bytes.Equal(b, localcoreBlk.Bytes()): 609 return localcoreBlk, nil 610 case bytes.Equal(b, netcoreBlk.Bytes()): 611 return netcoreBlk, nil 612 default: 613 require.FailNow("Unknown bytes") 614 return nil, nil 615 } 616 } 617 618 pChainHeight, err := proVM.ctx.ValidatorState.GetCurrentHeight(context.Background()) 619 require.NoError(err) 620 621 netSlb, err := statelessblock.BuildUnsigned( 622 proVM.preferred, 623 proVM.Time(), 624 pChainHeight, 625 netcoreBlk.Bytes(), 626 ) 627 require.NoError(err) 628 netProBlk := postForkBlock{ 629 SignedBlock: netSlb, 630 postForkCommonComponents: postForkCommonComponents{ 631 vm: proVM, 632 innerBlk: netcoreBlk, 633 status: choices.Processing, 634 }, 635 } 636 637 // prove that also block from network verifies 638 require.NoError(netProBlk.Verify(context.Background())) 639 } 640 641 // Pre Fork tests section 642 func TestPreFork_Initialize(t *testing.T) { 643 require := require.New(t) 644 645 var ( 646 activationTime = mockable.MaxTime 647 durangoTime = activationTime 648 ) 649 _, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 650 defer func() { 651 require.NoError(proVM.Shutdown(context.Background())) 652 }() 653 654 // checks 655 blkID, err := proVM.LastAccepted(context.Background()) 656 require.NoError(err) 657 658 rtvdBlk, err := proVM.GetBlock(context.Background(), blkID) 659 require.NoError(err) 660 661 require.IsType(&preForkBlock{}, rtvdBlk) 662 require.Equal(snowmantest.GenesisBytes, rtvdBlk.Bytes()) 663 } 664 665 func TestPreFork_BuildBlock(t *testing.T) { 666 require := require.New(t) 667 668 var ( 669 activationTime = mockable.MaxTime 670 durangoTime = activationTime 671 ) 672 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 673 defer func() { 674 require.NoError(proVM.Shutdown(context.Background())) 675 }() 676 677 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 678 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 679 return coreBlk, nil 680 } 681 682 // test 683 builtBlk, err := proVM.BuildBlock(context.Background()) 684 require.NoError(err) 685 require.IsType(&preForkBlock{}, builtBlk) 686 require.Equal(coreBlk.ID(), builtBlk.ID()) 687 require.Equal(coreBlk.Bytes(), builtBlk.Bytes()) 688 689 // test 690 coreVM.GetBlockF = func(context.Context, ids.ID) (snowman.Block, error) { 691 return coreBlk, nil 692 } 693 storedBlk, err := proVM.GetBlock(context.Background(), builtBlk.ID()) 694 require.NoError(err) 695 require.Equal(builtBlk.ID(), storedBlk.ID()) 696 } 697 698 func TestPreFork_ParseBlock(t *testing.T) { 699 require := require.New(t) 700 701 var ( 702 activationTime = mockable.MaxTime 703 durangoTime = activationTime 704 ) 705 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 706 defer func() { 707 require.NoError(proVM.Shutdown(context.Background())) 708 }() 709 710 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 711 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 712 require.Equal(coreBlk.Bytes(), b) 713 return coreBlk, nil 714 } 715 716 parsedBlk, err := proVM.ParseBlock(context.Background(), coreBlk.Bytes()) 717 require.NoError(err) 718 require.IsType(&preForkBlock{}, parsedBlk) 719 require.Equal(coreBlk.ID(), parsedBlk.ID()) 720 require.Equal(coreBlk.Bytes(), parsedBlk.Bytes()) 721 722 coreVM.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 723 require.Equal(coreBlk.ID(), id) 724 return coreBlk, nil 725 } 726 storedBlk, err := proVM.GetBlock(context.Background(), parsedBlk.ID()) 727 require.NoError(err) 728 require.Equal(parsedBlk.ID(), storedBlk.ID()) 729 } 730 731 func TestPreFork_SetPreference(t *testing.T) { 732 require := require.New(t) 733 734 var ( 735 activationTime = mockable.MaxTime 736 durangoTime = activationTime 737 ) 738 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 739 defer func() { 740 require.NoError(proVM.Shutdown(context.Background())) 741 }() 742 743 coreBlk0 := snowmantest.BuildChild(snowmantest.Genesis) 744 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 745 return coreBlk0, nil 746 } 747 builtBlk, err := proVM.BuildBlock(context.Background()) 748 require.NoError(err) 749 750 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 751 switch blkID { 752 case snowmantest.GenesisID: 753 return snowmantest.Genesis, nil 754 case coreBlk0.ID(): 755 return coreBlk0, nil 756 default: 757 return nil, errUnknownBlock 758 } 759 } 760 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 761 switch { 762 case bytes.Equal(b, snowmantest.GenesisBytes): 763 return snowmantest.Genesis, nil 764 case bytes.Equal(b, coreBlk0.Bytes()): 765 return coreBlk0, nil 766 default: 767 return nil, errUnknownBlock 768 } 769 } 770 require.NoError(proVM.SetPreference(context.Background(), builtBlk.ID())) 771 772 coreBlk1 := snowmantest.BuildChild(coreBlk0) 773 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 774 return coreBlk1, nil 775 } 776 nextBlk, err := proVM.BuildBlock(context.Background()) 777 require.NoError(err) 778 require.Equal(builtBlk.ID(), nextBlk.Parent()) 779 } 780 781 func TestExpiredBuildBlock(t *testing.T) { 782 require := require.New(t) 783 784 coreVM := &block.TestVM{} 785 coreVM.T = t 786 787 coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) { 788 return snowmantest.GenesisID, nil 789 } 790 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 791 switch blkID { 792 case snowmantest.GenesisID: 793 return snowmantest.Genesis, nil 794 default: 795 return nil, errUnknownBlock 796 } 797 } 798 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 799 switch { 800 case bytes.Equal(b, snowmantest.GenesisBytes): 801 return snowmantest.Genesis, nil 802 default: 803 return nil, errUnknownBlock 804 } 805 } 806 807 proVM := New( 808 coreVM, 809 Config{ 810 ActivationTime: time.Time{}, 811 DurangoTime: mockable.MaxTime, 812 MinimumPChainHeight: 0, 813 MinBlkDelay: DefaultMinBlockDelay, 814 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 815 StakingLeafSigner: pTestSigner, 816 StakingCertLeaf: pTestCert, 817 Registerer: prometheus.NewRegistry(), 818 }, 819 ) 820 821 valState := &validators.TestState{ 822 T: t, 823 } 824 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 825 return snowmantest.GenesisHeight, nil 826 } 827 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 828 return defaultPChainHeight, nil 829 } 830 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 831 nodeID := ids.BuildTestNodeID([]byte{1}) 832 return map[ids.NodeID]*validators.GetValidatorOutput{ 833 nodeID: { 834 NodeID: nodeID, 835 Weight: 100, 836 }, 837 }, nil 838 } 839 840 ctx := snowtest.Context(t, snowtest.CChainID) 841 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 842 ctx.ValidatorState = valState 843 844 toEngine := make(chan common.Message, 1) 845 var toScheduler chan<- common.Message 846 847 coreVM.InitializeF = func( 848 _ context.Context, 849 _ *snow.Context, 850 _ database.Database, 851 _ []byte, 852 _ []byte, 853 _ []byte, 854 toEngineChan chan<- common.Message, 855 _ []*common.Fx, 856 _ common.AppSender, 857 ) error { 858 toScheduler = toEngineChan 859 return nil 860 } 861 862 // make sure that DBs are compressed correctly 863 require.NoError(proVM.Initialize( 864 context.Background(), 865 ctx, 866 memdb.New(), 867 nil, 868 nil, 869 nil, 870 toEngine, 871 nil, 872 nil, 873 )) 874 defer func() { 875 require.NoError(proVM.Shutdown(context.Background())) 876 }() 877 878 // Initialize shouldn't be called again 879 coreVM.InitializeF = nil 880 881 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 882 require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID)) 883 884 // Notify the proposer VM of a new block on the inner block side 885 toScheduler <- common.PendingTxs 886 // The first notification will be read from the consensus engine 887 <-toEngine 888 889 // Before calling BuildBlock, verify a remote block and set it as the 890 // preferred block. 891 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 892 statelessBlock, err := statelessblock.BuildUnsigned( 893 snowmantest.GenesisID, 894 proVM.Time(), 895 0, 896 coreBlk.Bytes(), 897 ) 898 require.NoError(err) 899 900 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 901 switch blkID { 902 case snowmantest.GenesisID: 903 return snowmantest.Genesis, nil 904 case coreBlk.ID(): 905 return coreBlk, nil 906 default: 907 return nil, errUnknownBlock 908 } 909 } 910 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 911 switch { 912 case bytes.Equal(b, snowmantest.GenesisBytes): 913 return snowmantest.Genesis, nil 914 case bytes.Equal(b, coreBlk.Bytes()): 915 return coreBlk, nil 916 default: 917 return nil, errUnknownBlock 918 } 919 } 920 921 proVM.Clock.Set(statelessBlock.Timestamp()) 922 923 parsedBlock, err := proVM.ParseBlock(context.Background(), statelessBlock.Bytes()) 924 require.NoError(err) 925 926 require.NoError(parsedBlock.Verify(context.Background())) 927 require.NoError(proVM.SetPreference(context.Background(), parsedBlock.ID())) 928 929 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 930 require.FailNow(fmt.Errorf("%w: BuildBlock", errUnexpectedCall).Error()) 931 return nil, errUnexpectedCall 932 } 933 934 // Because we are now building on a different block, the proposer window 935 // shouldn't have started. 936 _, err = proVM.BuildBlock(context.Background()) 937 require.ErrorIs(err, errProposerWindowNotStarted) 938 939 proVM.Set(statelessBlock.Timestamp().Add(proposer.MaxBuildDelay)) 940 proVM.Scheduler.SetBuildBlockTime(time.Now()) 941 942 // The engine should have been notified to attempt to build a block now that 943 // the window has started again. This is to guarantee that the inner VM has 944 // build block called after it sent a pendingTxs message on its internal 945 // engine channel. 946 <-toEngine 947 } 948 949 type wrappedBlock struct { 950 snowman.Block 951 verified bool 952 } 953 954 func (b *wrappedBlock) Accept(ctx context.Context) error { 955 if !b.verified { 956 return errUnverifiedBlock 957 } 958 return b.Block.Accept(ctx) 959 } 960 961 func (b *wrappedBlock) Verify(ctx context.Context) error { 962 if err := b.Block.Verify(ctx); err != nil { 963 return err 964 } 965 b.verified = true 966 return nil 967 } 968 969 func TestInnerBlockDeduplication(t *testing.T) { 970 require := require.New(t) 971 972 var ( 973 activationTime = time.Unix(0, 0) 974 durangoTime = activationTime 975 ) 976 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 977 defer func() { 978 require.NoError(proVM.Shutdown(context.Background())) 979 }() 980 981 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 982 coreBlk0 := &wrappedBlock{ 983 Block: coreBlk, 984 } 985 coreBlk1 := &wrappedBlock{ 986 Block: coreBlk, 987 } 988 statelessBlock0, err := statelessblock.BuildUnsigned( 989 snowmantest.GenesisID, 990 coreBlk.Timestamp(), 991 0, 992 coreBlk.Bytes(), 993 ) 994 require.NoError(err) 995 statelessBlock1, err := statelessblock.BuildUnsigned( 996 snowmantest.GenesisID, 997 coreBlk.Timestamp(), 998 1, 999 coreBlk.Bytes(), 1000 ) 1001 require.NoError(err) 1002 1003 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1004 switch blkID { 1005 case snowmantest.GenesisID: 1006 return snowmantest.Genesis, nil 1007 case coreBlk0.ID(): 1008 return coreBlk0, nil 1009 default: 1010 return nil, errUnknownBlock 1011 } 1012 } 1013 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1014 switch { 1015 case bytes.Equal(b, snowmantest.GenesisBytes): 1016 return snowmantest.Genesis, nil 1017 case bytes.Equal(b, coreBlk0.Bytes()): 1018 return coreBlk0, nil 1019 default: 1020 return nil, errUnknownBlock 1021 } 1022 } 1023 1024 parsedBlock0, err := proVM.ParseBlock(context.Background(), statelessBlock0.Bytes()) 1025 require.NoError(err) 1026 1027 require.NoError(parsedBlock0.Verify(context.Background())) 1028 1029 require.NoError(proVM.SetPreference(context.Background(), parsedBlock0.ID())) 1030 1031 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1032 switch blkID { 1033 case snowmantest.GenesisID: 1034 return snowmantest.Genesis, nil 1035 case coreBlk1.ID(): 1036 return coreBlk1, nil 1037 default: 1038 return nil, errUnknownBlock 1039 } 1040 } 1041 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1042 switch { 1043 case bytes.Equal(b, snowmantest.GenesisBytes): 1044 return snowmantest.Genesis, nil 1045 case bytes.Equal(b, coreBlk1.Bytes()): 1046 return coreBlk1, nil 1047 default: 1048 return nil, errUnknownBlock 1049 } 1050 } 1051 1052 parsedBlock1, err := proVM.ParseBlock(context.Background(), statelessBlock1.Bytes()) 1053 require.NoError(err) 1054 1055 require.NoError(parsedBlock1.Verify(context.Background())) 1056 1057 require.NoError(proVM.SetPreference(context.Background(), parsedBlock1.ID())) 1058 1059 require.NoError(parsedBlock1.Accept(context.Background())) 1060 } 1061 1062 func TestInnerVMRollback(t *testing.T) { 1063 require := require.New(t) 1064 1065 valState := &validators.TestState{ 1066 T: t, 1067 } 1068 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 1069 return defaultPChainHeight, nil 1070 } 1071 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 1072 nodeID := ids.BuildTestNodeID([]byte{1}) 1073 return map[ids.NodeID]*validators.GetValidatorOutput{ 1074 nodeID: { 1075 NodeID: nodeID, 1076 Weight: 100, 1077 }, 1078 }, nil 1079 } 1080 1081 coreVM := &block.TestVM{} 1082 coreVM.T = t 1083 1084 coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) { 1085 return snowmantest.GenesisID, nil 1086 } 1087 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1088 switch blkID { 1089 case snowmantest.GenesisID: 1090 return snowmantest.Genesis, nil 1091 default: 1092 return nil, errUnknownBlock 1093 } 1094 } 1095 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1096 switch { 1097 case bytes.Equal(b, snowmantest.GenesisBytes): 1098 return snowmantest.Genesis, nil 1099 default: 1100 return nil, errUnknownBlock 1101 } 1102 } 1103 1104 ctx := snowtest.Context(t, snowtest.CChainID) 1105 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 1106 ctx.ValidatorState = valState 1107 1108 coreVM.InitializeF = func( 1109 context.Context, 1110 *snow.Context, 1111 database.Database, 1112 []byte, 1113 []byte, 1114 []byte, 1115 chan<- common.Message, 1116 []*common.Fx, 1117 common.AppSender, 1118 ) error { 1119 return nil 1120 } 1121 1122 db := memdb.New() 1123 1124 proVM := New( 1125 coreVM, 1126 Config{ 1127 ActivationTime: time.Time{}, 1128 DurangoTime: mockable.MaxTime, 1129 MinimumPChainHeight: 0, 1130 MinBlkDelay: DefaultMinBlockDelay, 1131 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 1132 StakingLeafSigner: pTestSigner, 1133 StakingCertLeaf: pTestCert, 1134 Registerer: prometheus.NewRegistry(), 1135 }, 1136 ) 1137 1138 require.NoError(proVM.Initialize( 1139 context.Background(), 1140 ctx, 1141 db, 1142 nil, 1143 nil, 1144 nil, 1145 nil, 1146 nil, 1147 nil, 1148 )) 1149 1150 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 1151 require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID)) 1152 1153 coreBlk := snowmantest.BuildChild(snowmantest.Genesis) 1154 statelessBlock, err := statelessblock.BuildUnsigned( 1155 snowmantest.GenesisID, 1156 coreBlk.Timestamp(), 1157 0, 1158 coreBlk.Bytes(), 1159 ) 1160 require.NoError(err) 1161 1162 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1163 switch blkID { 1164 case snowmantest.GenesisID: 1165 return snowmantest.Genesis, nil 1166 case coreBlk.ID(): 1167 return coreBlk, nil 1168 default: 1169 return nil, errUnknownBlock 1170 } 1171 } 1172 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1173 switch { 1174 case bytes.Equal(b, snowmantest.GenesisBytes): 1175 return snowmantest.Genesis, nil 1176 case bytes.Equal(b, coreBlk.Bytes()): 1177 return coreBlk, nil 1178 default: 1179 return nil, errUnknownBlock 1180 } 1181 } 1182 1183 proVM.Clock.Set(statelessBlock.Timestamp()) 1184 1185 parsedBlock, err := proVM.ParseBlock(context.Background(), statelessBlock.Bytes()) 1186 require.NoError(err) 1187 1188 require.Equal(choices.Processing, parsedBlock.Status()) 1189 1190 require.NoError(parsedBlock.Verify(context.Background())) 1191 require.NoError(proVM.SetPreference(context.Background(), parsedBlock.ID())) 1192 require.NoError(parsedBlock.Accept(context.Background())) 1193 1194 fetchedBlock, err := proVM.GetBlock(context.Background(), parsedBlock.ID()) 1195 require.NoError(err) 1196 1197 require.Equal(choices.Accepted, fetchedBlock.Status()) 1198 1199 // Restart the node and have the inner VM rollback state. 1200 require.NoError(proVM.Shutdown(context.Background())) 1201 coreBlk.StatusV = choices.Processing 1202 1203 proVM = New( 1204 coreVM, 1205 Config{ 1206 ActivationTime: time.Time{}, 1207 DurangoTime: mockable.MaxTime, 1208 MinimumPChainHeight: 0, 1209 MinBlkDelay: DefaultMinBlockDelay, 1210 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 1211 StakingLeafSigner: pTestSigner, 1212 StakingCertLeaf: pTestCert, 1213 Registerer: prometheus.NewRegistry(), 1214 }, 1215 ) 1216 1217 require.NoError(proVM.Initialize( 1218 context.Background(), 1219 ctx, 1220 db, 1221 nil, 1222 nil, 1223 nil, 1224 nil, 1225 nil, 1226 nil, 1227 )) 1228 defer func() { 1229 require.NoError(proVM.Shutdown(context.Background())) 1230 }() 1231 1232 lastAcceptedID, err := proVM.LastAccepted(context.Background()) 1233 require.NoError(err) 1234 1235 require.Equal(snowmantest.GenesisID, lastAcceptedID) 1236 1237 parsedBlock, err = proVM.ParseBlock(context.Background(), statelessBlock.Bytes()) 1238 require.NoError(err) 1239 1240 require.Equal(choices.Processing, parsedBlock.Status()) 1241 } 1242 1243 func TestBuildBlockDuringWindow(t *testing.T) { 1244 require := require.New(t) 1245 1246 var ( 1247 activationTime = time.Unix(0, 0) 1248 durangoTime = mockable.MaxTime 1249 ) 1250 coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 1251 defer func() { 1252 require.NoError(proVM.Shutdown(context.Background())) 1253 }() 1254 1255 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 1256 return map[ids.NodeID]*validators.GetValidatorOutput{ 1257 proVM.ctx.NodeID: { 1258 NodeID: proVM.ctx.NodeID, 1259 Weight: 10, 1260 }, 1261 }, nil 1262 } 1263 1264 coreBlk0 := snowmantest.BuildChild(snowmantest.Genesis) 1265 coreBlk1 := snowmantest.BuildChild(coreBlk0) 1266 statelessBlock0, err := statelessblock.BuildUnsigned( 1267 snowmantest.GenesisID, 1268 proVM.Time(), 1269 0, 1270 coreBlk0.Bytes(), 1271 ) 1272 require.NoError(err) 1273 1274 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1275 switch blkID { 1276 case snowmantest.GenesisID: 1277 return snowmantest.Genesis, nil 1278 case coreBlk0.ID(): 1279 return coreBlk0, nil 1280 case coreBlk1.ID(): 1281 return coreBlk1, nil 1282 default: 1283 return nil, errUnknownBlock 1284 } 1285 } 1286 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1287 switch { 1288 case bytes.Equal(b, snowmantest.GenesisBytes): 1289 return snowmantest.Genesis, nil 1290 case bytes.Equal(b, coreBlk0.Bytes()): 1291 return coreBlk0, nil 1292 case bytes.Equal(b, coreBlk1.Bytes()): 1293 return coreBlk1, nil 1294 default: 1295 return nil, errUnknownBlock 1296 } 1297 } 1298 1299 proVM.Clock.Set(statelessBlock0.Timestamp()) 1300 1301 statefulBlock0, err := proVM.ParseBlock(context.Background(), statelessBlock0.Bytes()) 1302 require.NoError(err) 1303 1304 require.NoError(statefulBlock0.Verify(context.Background())) 1305 1306 require.NoError(proVM.SetPreference(context.Background(), statefulBlock0.ID())) 1307 1308 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1309 return coreBlk1, nil 1310 } 1311 1312 statefulBlock1, err := proVM.BuildBlock(context.Background()) 1313 require.NoError(err) 1314 1315 require.NoError(statefulBlock1.Verify(context.Background())) 1316 1317 require.NoError(proVM.SetPreference(context.Background(), statefulBlock1.ID())) 1318 1319 require.NoError(statefulBlock0.Accept(context.Background())) 1320 1321 require.NoError(statefulBlock1.Accept(context.Background())) 1322 } 1323 1324 // Ensure that Accepting a PostForkBlock (A) containing core block (X) causes 1325 // core block (Y) and (Z) to also be rejected. 1326 // 1327 // G 1328 // / \ 1329 // A(X) B(Y) 1330 // | 1331 // C(Z) 1332 func TestTwoForks_OneIsAccepted(t *testing.T) { 1333 require := require.New(t) 1334 1335 var ( 1336 activationTime = time.Unix(0, 0) 1337 durangoTime = mockable.MaxTime 1338 ) 1339 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 1340 defer func() { 1341 require.NoError(proVM.Shutdown(context.Background())) 1342 }() 1343 1344 // create pre-fork block X and post-fork block A 1345 xBlock := snowmantest.BuildChild(snowmantest.Genesis) 1346 1347 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1348 return xBlock, nil 1349 } 1350 aBlock, err := proVM.BuildBlock(context.Background()) 1351 require.NoError(err) 1352 coreVM.BuildBlockF = nil 1353 require.NoError(aBlock.Verify(context.Background())) 1354 1355 // use a different way to construct pre-fork block Y and post-fork block B 1356 yBlock := snowmantest.BuildChild(snowmantest.Genesis) 1357 1358 ySlb, err := statelessblock.BuildUnsigned( 1359 snowmantest.GenesisID, 1360 proVM.Time(), 1361 defaultPChainHeight, 1362 yBlock.Bytes(), 1363 ) 1364 require.NoError(err) 1365 1366 bBlock := postForkBlock{ 1367 SignedBlock: ySlb, 1368 postForkCommonComponents: postForkCommonComponents{ 1369 vm: proVM, 1370 innerBlk: yBlock, 1371 status: choices.Processing, 1372 }, 1373 } 1374 1375 require.NoError(bBlock.Verify(context.Background())) 1376 1377 // append Z/C to Y/B 1378 zBlock := snowmantest.BuildChild(yBlock) 1379 1380 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1381 return zBlock, nil 1382 } 1383 require.NoError(proVM.SetPreference(context.Background(), bBlock.ID())) 1384 proVM.Set(proVM.Time().Add(proposer.MaxBuildDelay)) 1385 cBlock, err := proVM.BuildBlock(context.Background()) 1386 require.NoError(err) 1387 coreVM.BuildBlockF = nil 1388 1389 require.NoError(cBlock.Verify(context.Background())) 1390 1391 require.Equal(bBlock.Parent(), aBlock.Parent()) 1392 require.Equal(yBlock.ID(), zBlock.Parent()) 1393 require.Equal(bBlock.ID(), cBlock.Parent()) 1394 1395 require.NotEqual(choices.Rejected, yBlock.Status()) 1396 1397 // accept A 1398 require.NoError(aBlock.Accept(context.Background())) 1399 1400 require.Equal(choices.Accepted, xBlock.Status()) 1401 require.Equal(choices.Rejected, yBlock.Status()) 1402 require.Equal(choices.Rejected, zBlock.Status()) 1403 } 1404 1405 func TestTooFarAdvanced(t *testing.T) { 1406 require := require.New(t) 1407 1408 var ( 1409 activationTime = time.Unix(0, 0) 1410 durangoTime = mockable.MaxTime 1411 ) 1412 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 1413 defer func() { 1414 require.NoError(proVM.Shutdown(context.Background())) 1415 }() 1416 1417 xBlock := snowmantest.BuildChild(snowmantest.Genesis) 1418 yBlock := snowmantest.BuildChild(xBlock) 1419 1420 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1421 return xBlock, nil 1422 } 1423 aBlock, err := proVM.BuildBlock(context.Background()) 1424 require.NoError(err) 1425 require.NoError(aBlock.Verify(context.Background())) 1426 1427 ySlb, err := statelessblock.BuildUnsigned( 1428 aBlock.ID(), 1429 aBlock.Timestamp().Add(maxSkew), 1430 defaultPChainHeight, 1431 yBlock.Bytes(), 1432 ) 1433 require.NoError(err) 1434 1435 bBlock := postForkBlock{ 1436 SignedBlock: ySlb, 1437 postForkCommonComponents: postForkCommonComponents{ 1438 vm: proVM, 1439 innerBlk: yBlock, 1440 status: choices.Processing, 1441 }, 1442 } 1443 1444 err = bBlock.Verify(context.Background()) 1445 require.ErrorIs(err, errProposerWindowNotStarted) 1446 1447 ySlb, err = statelessblock.BuildUnsigned( 1448 aBlock.ID(), 1449 aBlock.Timestamp().Add(proposer.MaxVerifyDelay), 1450 defaultPChainHeight, 1451 yBlock.Bytes(), 1452 ) 1453 1454 require.NoError(err) 1455 1456 bBlock = postForkBlock{ 1457 SignedBlock: ySlb, 1458 postForkCommonComponents: postForkCommonComponents{ 1459 vm: proVM, 1460 innerBlk: yBlock, 1461 status: choices.Processing, 1462 }, 1463 } 1464 1465 err = bBlock.Verify(context.Background()) 1466 require.ErrorIs(err, errTimeTooAdvanced) 1467 } 1468 1469 // Ensure that Accepting a PostForkOption (B) causes both the other option and 1470 // the core block in the other option to be rejected. 1471 // 1472 // G 1473 // | 1474 // A(X) 1475 // /====\ 1476 // B(...) C(...) 1477 // 1478 // B(...) is B(X.opts[0]) 1479 // B(...) is C(X.opts[1]) 1480 func TestTwoOptions_OneIsAccepted(t *testing.T) { 1481 require := require.New(t) 1482 1483 var ( 1484 activationTime = time.Unix(0, 0) 1485 durangoTime = mockable.MaxTime 1486 ) 1487 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 1488 defer func() { 1489 require.NoError(proVM.Shutdown(context.Background())) 1490 }() 1491 1492 xTestBlock := snowmantest.BuildChild(snowmantest.Genesis) 1493 xBlock := &TestOptionsBlock{ 1494 Block: *xTestBlock, 1495 opts: [2]snowman.Block{ 1496 snowmantest.BuildChild(xTestBlock), 1497 snowmantest.BuildChild(xTestBlock), 1498 }, 1499 } 1500 1501 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1502 return xBlock, nil 1503 } 1504 aBlockIntf, err := proVM.BuildBlock(context.Background()) 1505 require.NoError(err) 1506 1507 require.IsType(&postForkBlock{}, aBlockIntf) 1508 aBlock := aBlockIntf.(*postForkBlock) 1509 1510 opts, err := aBlock.Options(context.Background()) 1511 require.NoError(err) 1512 1513 require.NoError(aBlock.Verify(context.Background())) 1514 bBlock := opts[0] 1515 require.NoError(bBlock.Verify(context.Background())) 1516 cBlock := opts[1] 1517 require.NoError(cBlock.Verify(context.Background())) 1518 1519 require.NoError(aBlock.Accept(context.Background())) 1520 1521 require.NoError(bBlock.Accept(context.Background())) 1522 1523 // the other pre-fork option should be rejected 1524 require.Equal(choices.Rejected, xBlock.opts[1].Status()) 1525 1526 // the other post-fork option should also be rejected 1527 require.NoError(cBlock.Reject(context.Background())) 1528 1529 require.Equal(choices.Rejected, cBlock.Status()) 1530 } 1531 1532 // Ensure that given the chance, built blocks will reference a lagged P-chain 1533 // height. 1534 func TestLaggedPChainHeight(t *testing.T) { 1535 require := require.New(t) 1536 1537 var ( 1538 activationTime = time.Unix(0, 0) 1539 durangoTime = activationTime 1540 ) 1541 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 1542 defer func() { 1543 require.NoError(proVM.Shutdown(context.Background())) 1544 }() 1545 1546 innerBlock := snowmantest.BuildChild(snowmantest.Genesis) 1547 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1548 return innerBlock, nil 1549 } 1550 blockIntf, err := proVM.BuildBlock(context.Background()) 1551 require.NoError(err) 1552 1553 require.IsType(&postForkBlock{}, blockIntf) 1554 block := blockIntf.(*postForkBlock) 1555 1556 pChainHeight := block.PChainHeight() 1557 require.Equal(snowmantest.GenesisHeight, pChainHeight) 1558 } 1559 1560 // Ensure that rejecting a block does not modify the accepted block ID for the 1561 // rejected height. 1562 func TestRejectedHeightNotIndexed(t *testing.T) { 1563 require := require.New(t) 1564 1565 coreHeights := []ids.ID{snowmantest.GenesisID} 1566 1567 initialState := []byte("genesis state") 1568 coreVM := &block.TestVM{ 1569 TestVM: common.TestVM{ 1570 T: t, 1571 }, 1572 GetBlockIDAtHeightF: func(_ context.Context, height uint64) (ids.ID, error) { 1573 if height >= uint64(len(coreHeights)) { 1574 return ids.Empty, errTooHigh 1575 } 1576 return coreHeights[height], nil 1577 }, 1578 } 1579 1580 coreVM.InitializeF = func(context.Context, *snow.Context, database.Database, 1581 []byte, []byte, []byte, chan<- common.Message, 1582 []*common.Fx, common.AppSender, 1583 ) error { 1584 return nil 1585 } 1586 coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) { 1587 return snowmantest.GenesisID, nil 1588 } 1589 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1590 switch blkID { 1591 case snowmantest.GenesisID: 1592 return snowmantest.Genesis, nil 1593 default: 1594 return nil, errUnknownBlock 1595 } 1596 } 1597 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1598 switch { 1599 case bytes.Equal(b, snowmantest.GenesisBytes): 1600 return snowmantest.Genesis, nil 1601 default: 1602 return nil, errUnknownBlock 1603 } 1604 } 1605 1606 proVM := New( 1607 coreVM, 1608 Config{ 1609 ActivationTime: time.Unix(0, 0), 1610 DurangoTime: time.Unix(0, 0), 1611 MinimumPChainHeight: 0, 1612 MinBlkDelay: DefaultMinBlockDelay, 1613 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 1614 StakingLeafSigner: pTestSigner, 1615 StakingCertLeaf: pTestCert, 1616 Registerer: prometheus.NewRegistry(), 1617 }, 1618 ) 1619 1620 valState := &validators.TestState{ 1621 T: t, 1622 } 1623 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 1624 return snowmantest.GenesisHeight, nil 1625 } 1626 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 1627 return defaultPChainHeight, nil 1628 } 1629 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 1630 var ( 1631 thisNode = proVM.ctx.NodeID 1632 nodeID1 = ids.BuildTestNodeID([]byte{1}) 1633 nodeID2 = ids.BuildTestNodeID([]byte{2}) 1634 nodeID3 = ids.BuildTestNodeID([]byte{3}) 1635 ) 1636 return map[ids.NodeID]*validators.GetValidatorOutput{ 1637 thisNode: { 1638 NodeID: thisNode, 1639 Weight: 10, 1640 }, 1641 nodeID1: { 1642 NodeID: nodeID1, 1643 Weight: 5, 1644 }, 1645 nodeID2: { 1646 NodeID: nodeID2, 1647 Weight: 6, 1648 }, 1649 nodeID3: { 1650 NodeID: nodeID3, 1651 Weight: 7, 1652 }, 1653 }, nil 1654 } 1655 1656 ctx := snowtest.Context(t, snowtest.CChainID) 1657 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 1658 ctx.ValidatorState = valState 1659 1660 require.NoError(proVM.Initialize( 1661 context.Background(), 1662 ctx, 1663 prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly 1664 initialState, 1665 nil, 1666 nil, 1667 nil, 1668 nil, 1669 nil, 1670 )) 1671 defer func() { 1672 require.NoError(proVM.Shutdown(context.Background())) 1673 }() 1674 1675 // Initialize shouldn't be called again 1676 coreVM.InitializeF = nil 1677 1678 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 1679 1680 require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID)) 1681 1682 // create inner block X and outer block A 1683 xBlock := snowmantest.BuildChild(snowmantest.Genesis) 1684 1685 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1686 return xBlock, nil 1687 } 1688 aBlock, err := proVM.BuildBlock(context.Background()) 1689 require.NoError(err) 1690 1691 coreVM.BuildBlockF = nil 1692 require.NoError(aBlock.Verify(context.Background())) 1693 1694 // use a different way to construct inner block Y and outer block B 1695 yBlock := snowmantest.BuildChild(snowmantest.Genesis) 1696 1697 ySlb, err := statelessblock.BuildUnsigned( 1698 snowmantest.GenesisID, 1699 snowmantest.GenesisTimestamp, 1700 defaultPChainHeight, 1701 yBlock.Bytes(), 1702 ) 1703 require.NoError(err) 1704 1705 bBlock := postForkBlock{ 1706 SignedBlock: ySlb, 1707 postForkCommonComponents: postForkCommonComponents{ 1708 vm: proVM, 1709 innerBlk: yBlock, 1710 status: choices.Processing, 1711 }, 1712 } 1713 1714 require.NoError(bBlock.Verify(context.Background())) 1715 1716 // accept A 1717 require.NoError(aBlock.Accept(context.Background())) 1718 coreHeights = append(coreHeights, xBlock.ID()) 1719 1720 blkID, err := proVM.GetBlockIDAtHeight(context.Background(), aBlock.Height()) 1721 require.NoError(err) 1722 require.Equal(aBlock.ID(), blkID) 1723 1724 // reject B 1725 require.NoError(bBlock.Reject(context.Background())) 1726 1727 blkID, err = proVM.GetBlockIDAtHeight(context.Background(), aBlock.Height()) 1728 require.NoError(err) 1729 require.Equal(aBlock.ID(), blkID) 1730 } 1731 1732 // Ensure that rejecting an option block does not modify the accepted block ID 1733 // for the rejected height. 1734 func TestRejectedOptionHeightNotIndexed(t *testing.T) { 1735 require := require.New(t) 1736 1737 coreHeights := []ids.ID{snowmantest.GenesisID} 1738 1739 initialState := []byte("genesis state") 1740 coreVM := &block.TestVM{ 1741 TestVM: common.TestVM{ 1742 T: t, 1743 }, 1744 GetBlockIDAtHeightF: func(_ context.Context, height uint64) (ids.ID, error) { 1745 if height >= uint64(len(coreHeights)) { 1746 return ids.Empty, errTooHigh 1747 } 1748 return coreHeights[height], nil 1749 }, 1750 } 1751 1752 coreVM.InitializeF = func(context.Context, *snow.Context, database.Database, 1753 []byte, []byte, []byte, chan<- common.Message, 1754 []*common.Fx, common.AppSender, 1755 ) error { 1756 return nil 1757 } 1758 coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) { 1759 return snowmantest.GenesisID, nil 1760 } 1761 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1762 switch blkID { 1763 case snowmantest.GenesisID: 1764 return snowmantest.Genesis, nil 1765 default: 1766 return nil, errUnknownBlock 1767 } 1768 } 1769 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1770 switch { 1771 case bytes.Equal(b, snowmantest.GenesisBytes): 1772 return snowmantest.Genesis, nil 1773 default: 1774 return nil, errUnknownBlock 1775 } 1776 } 1777 1778 proVM := New( 1779 coreVM, 1780 Config{ 1781 ActivationTime: time.Unix(0, 0), 1782 DurangoTime: time.Unix(0, 0), 1783 MinimumPChainHeight: 0, 1784 MinBlkDelay: DefaultMinBlockDelay, 1785 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 1786 StakingLeafSigner: pTestSigner, 1787 StakingCertLeaf: pTestCert, 1788 Registerer: prometheus.NewRegistry(), 1789 }, 1790 ) 1791 1792 valState := &validators.TestState{ 1793 T: t, 1794 } 1795 valState.GetMinimumHeightF = func(context.Context) (uint64, error) { 1796 return snowmantest.GenesisHeight, nil 1797 } 1798 valState.GetCurrentHeightF = func(context.Context) (uint64, error) { 1799 return defaultPChainHeight, nil 1800 } 1801 valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 1802 var ( 1803 thisNode = proVM.ctx.NodeID 1804 nodeID1 = ids.BuildTestNodeID([]byte{1}) 1805 nodeID2 = ids.BuildTestNodeID([]byte{2}) 1806 nodeID3 = ids.BuildTestNodeID([]byte{3}) 1807 ) 1808 return map[ids.NodeID]*validators.GetValidatorOutput{ 1809 thisNode: { 1810 NodeID: thisNode, 1811 Weight: 10, 1812 }, 1813 nodeID1: { 1814 NodeID: nodeID1, 1815 Weight: 5, 1816 }, 1817 nodeID2: { 1818 NodeID: nodeID2, 1819 Weight: 6, 1820 }, 1821 nodeID3: { 1822 NodeID: nodeID3, 1823 Weight: 7, 1824 }, 1825 }, nil 1826 } 1827 1828 ctx := snowtest.Context(t, snowtest.CChainID) 1829 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 1830 ctx.ValidatorState = valState 1831 1832 require.NoError(proVM.Initialize( 1833 context.Background(), 1834 ctx, 1835 prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly 1836 initialState, 1837 nil, 1838 nil, 1839 nil, 1840 nil, 1841 nil, 1842 )) 1843 defer func() { 1844 require.NoError(proVM.Shutdown(context.Background())) 1845 }() 1846 1847 // Initialize shouldn't be called again 1848 coreVM.InitializeF = nil 1849 1850 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 1851 1852 require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID)) 1853 1854 xTestBlock := snowmantest.BuildChild(snowmantest.Genesis) 1855 xBlock := &TestOptionsBlock{ 1856 Block: *xTestBlock, 1857 opts: [2]snowman.Block{ 1858 snowmantest.BuildChild(xTestBlock), 1859 snowmantest.BuildChild(xTestBlock), 1860 }, 1861 } 1862 1863 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 1864 return xBlock, nil 1865 } 1866 aBlockIntf, err := proVM.BuildBlock(context.Background()) 1867 require.NoError(err) 1868 1869 require.IsType(&postForkBlock{}, aBlockIntf) 1870 aBlock := aBlockIntf.(*postForkBlock) 1871 1872 opts, err := aBlock.Options(context.Background()) 1873 require.NoError(err) 1874 1875 require.NoError(aBlock.Verify(context.Background())) 1876 1877 bBlock := opts[0] 1878 require.NoError(bBlock.Verify(context.Background())) 1879 1880 cBlock := opts[1] 1881 require.NoError(cBlock.Verify(context.Background())) 1882 1883 // accept A 1884 require.NoError(aBlock.Accept(context.Background())) 1885 coreHeights = append(coreHeights, xBlock.ID()) 1886 1887 blkID, err := proVM.GetBlockIDAtHeight(context.Background(), aBlock.Height()) 1888 require.NoError(err) 1889 require.Equal(aBlock.ID(), blkID) 1890 1891 // accept B 1892 require.NoError(bBlock.Accept(context.Background())) 1893 coreHeights = append(coreHeights, xBlock.opts[0].ID()) 1894 1895 blkID, err = proVM.GetBlockIDAtHeight(context.Background(), bBlock.Height()) 1896 require.NoError(err) 1897 require.Equal(bBlock.ID(), blkID) 1898 1899 // reject C 1900 require.NoError(cBlock.Reject(context.Background())) 1901 1902 blkID, err = proVM.GetBlockIDAtHeight(context.Background(), cBlock.Height()) 1903 require.NoError(err) 1904 require.Equal(bBlock.ID(), blkID) 1905 } 1906 1907 func TestVMInnerBlkCache(t *testing.T) { 1908 require := require.New(t) 1909 ctrl := gomock.NewController(t) 1910 1911 // Create a VM 1912 innerVM := block.NewMockChainVM(ctrl) 1913 vm := New( 1914 innerVM, 1915 Config{ 1916 ActivationTime: time.Unix(0, 0), 1917 DurangoTime: time.Unix(0, 0), 1918 MinimumPChainHeight: 0, 1919 MinBlkDelay: DefaultMinBlockDelay, 1920 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 1921 StakingLeafSigner: pTestSigner, 1922 StakingCertLeaf: pTestCert, 1923 Registerer: prometheus.NewRegistry(), 1924 }, 1925 ) 1926 1927 innerVM.EXPECT().Initialize( 1928 gomock.Any(), 1929 gomock.Any(), 1930 gomock.Any(), 1931 gomock.Any(), 1932 gomock.Any(), 1933 gomock.Any(), 1934 gomock.Any(), 1935 gomock.Any(), 1936 gomock.Any(), 1937 ).Return(nil) 1938 innerVM.EXPECT().Shutdown(gomock.Any()).Return(nil) 1939 1940 { 1941 innerBlk := snowmantest.NewMockBlock(ctrl) 1942 innerBlkID := ids.GenerateTestID() 1943 innerVM.EXPECT().LastAccepted(gomock.Any()).Return(innerBlkID, nil) 1944 innerVM.EXPECT().GetBlock(gomock.Any(), innerBlkID).Return(innerBlk, nil) 1945 } 1946 1947 ctx := snowtest.Context(t, snowtest.CChainID) 1948 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 1949 1950 require.NoError(vm.Initialize( 1951 context.Background(), 1952 ctx, 1953 prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly 1954 nil, 1955 nil, 1956 nil, 1957 nil, 1958 nil, 1959 nil, 1960 )) 1961 defer func() { 1962 require.NoError(vm.Shutdown(context.Background())) 1963 }() 1964 1965 state := state.NewMockState(ctrl) // mock state 1966 vm.State = state 1967 1968 // Create a block near the tip (0). 1969 blkNearTipInnerBytes := []byte{1} 1970 blkNearTip, err := statelessblock.Build( 1971 ids.GenerateTestID(), // parent 1972 time.Time{}, // timestamp 1973 1, // pChainHeight, 1974 vm.StakingCertLeaf, // cert 1975 blkNearTipInnerBytes, // inner blk bytes 1976 vm.ctx.ChainID, // chain ID 1977 vm.StakingLeafSigner, // key 1978 ) 1979 require.NoError(err) 1980 1981 // Parse a block. 1982 // Not in the VM's state so need to parse it. 1983 state.EXPECT().GetBlock(blkNearTip.ID()).Return(blkNearTip, choices.Accepted, nil).Times(2) 1984 // We will ask the inner VM to parse. 1985 mockInnerBlkNearTip := snowmantest.NewMockBlock(ctrl) 1986 mockInnerBlkNearTip.EXPECT().Height().Return(uint64(1)).Times(2) 1987 mockInnerBlkNearTip.EXPECT().Bytes().Return(blkNearTipInnerBytes).Times(1) 1988 1989 innerVM.EXPECT().ParseBlock(gomock.Any(), blkNearTipInnerBytes).Return(mockInnerBlkNearTip, nil).Times(2) 1990 _, err = vm.ParseBlock(context.Background(), blkNearTip.Bytes()) 1991 require.NoError(err) 1992 1993 // Block should now be in cache because it's a post-fork block 1994 // and close to the tip. 1995 gotBlk, ok := vm.innerBlkCache.Get(blkNearTip.ID()) 1996 require.True(ok) 1997 require.Equal(mockInnerBlkNearTip, gotBlk) 1998 require.Zero(vm.lastAcceptedHeight) 1999 2000 // Clear the cache 2001 vm.innerBlkCache.Flush() 2002 2003 // Advance the tip height 2004 vm.lastAcceptedHeight = innerBlkCacheSize + 1 2005 2006 // Parse the block again. This time it shouldn't be cached 2007 // because it's not close to the tip. 2008 _, err = vm.ParseBlock(context.Background(), blkNearTip.Bytes()) 2009 require.NoError(err) 2010 2011 _, ok = vm.innerBlkCache.Get(blkNearTip.ID()) 2012 require.False(ok) 2013 } 2014 2015 func TestVMInnerBlkCacheDeduplicationRegression(t *testing.T) { 2016 require := require.New(t) 2017 var ( 2018 activationTime = time.Unix(0, 0) 2019 durangoTime = activationTime 2020 ) 2021 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 2022 defer func() { 2023 require.NoError(proVM.Shutdown(context.Background())) 2024 }() 2025 2026 // create pre-fork block X and post-fork block A 2027 xBlock := snowmantest.BuildChild(snowmantest.Genesis) 2028 2029 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 2030 return xBlock, nil 2031 } 2032 aBlock, err := proVM.BuildBlock(context.Background()) 2033 require.NoError(err) 2034 coreVM.BuildBlockF = nil 2035 2036 bStatelessBlock, err := statelessblock.BuildUnsigned( 2037 snowmantest.GenesisID, 2038 snowmantest.GenesisTimestamp, 2039 defaultPChainHeight, 2040 xBlock.Bytes(), 2041 ) 2042 require.NoError(err) 2043 2044 xBlockCopy := *xBlock 2045 coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) { 2046 return &xBlockCopy, nil 2047 } 2048 2049 bBlockBytes := bStatelessBlock.Bytes() 2050 bBlock, err := proVM.ParseBlock(context.Background(), bBlockBytes) 2051 require.NoError(err) 2052 2053 require.NoError(aBlock.Verify(context.Background())) 2054 require.NoError(bBlock.Verify(context.Background())) 2055 require.NoError(aBlock.Accept(context.Background())) 2056 require.NoError(bBlock.Reject(context.Background())) 2057 2058 require.Equal( 2059 choices.Accepted, 2060 aBlock.(*postForkBlock).innerBlk.Status(), 2061 ) 2062 2063 require.Equal( 2064 choices.Accepted, 2065 bBlock.(*postForkBlock).innerBlk.Status(), 2066 ) 2067 2068 cachedXBlock, ok := proVM.innerBlkCache.Get(bBlock.ID()) 2069 require.True(ok) 2070 require.Equal( 2071 choices.Accepted, 2072 cachedXBlock.Status(), 2073 ) 2074 } 2075 2076 func TestVMInnerBlkMarkedAcceptedRegression(t *testing.T) { 2077 require := require.New(t) 2078 var ( 2079 activationTime = time.Unix(0, 0) 2080 durangoTime = activationTime 2081 ) 2082 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 2083 defer func() { 2084 require.NoError(proVM.Shutdown(context.Background())) 2085 }() 2086 2087 // create an inner block and wrap it in an postForkBlock. 2088 innerBlock := snowmantest.BuildChild(snowmantest.Genesis) 2089 2090 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 2091 return innerBlock, nil 2092 } 2093 outerBlock, err := proVM.BuildBlock(context.Background()) 2094 require.NoError(err) 2095 coreVM.BuildBlockF = nil 2096 2097 require.NoError(outerBlock.Verify(context.Background())) 2098 require.NoError(outerBlock.Accept(context.Background())) 2099 2100 coreVM.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 2101 require.Equal(innerBlock.ID(), id) 2102 return innerBlock, nil 2103 } 2104 2105 wrappedInnerBlock, err := proVM.GetBlock(context.Background(), innerBlock.ID()) 2106 require.NoError(err) 2107 require.Equal(choices.Rejected, wrappedInnerBlock.Status()) 2108 } 2109 2110 type blockWithVerifyContext struct { 2111 *snowmantest.MockBlock 2112 *block.MockWithVerifyContext 2113 } 2114 2115 // Ensures that we call [VerifyWithContext] rather than [Verify] on blocks that 2116 // implement [block.WithVerifyContext] and that returns true for 2117 // [ShouldVerifyWithContext]. 2118 func TestVM_VerifyBlockWithContext(t *testing.T) { 2119 require := require.New(t) 2120 ctrl := gomock.NewController(t) 2121 2122 // Create a VM 2123 innerVM := block.NewMockChainVM(ctrl) 2124 vm := New( 2125 innerVM, 2126 Config{ 2127 ActivationTime: time.Unix(0, 0), 2128 DurangoTime: time.Unix(0, 0), 2129 MinimumPChainHeight: 0, 2130 MinBlkDelay: DefaultMinBlockDelay, 2131 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 2132 StakingLeafSigner: pTestSigner, 2133 StakingCertLeaf: pTestCert, 2134 Registerer: prometheus.NewRegistry(), 2135 }, 2136 ) 2137 2138 // make sure that DBs are compressed correctly 2139 db := prefixdb.New([]byte{}, memdb.New()) 2140 2141 innerVM.EXPECT().Initialize( 2142 gomock.Any(), 2143 gomock.Any(), 2144 gomock.Any(), 2145 gomock.Any(), 2146 gomock.Any(), 2147 gomock.Any(), 2148 gomock.Any(), 2149 gomock.Any(), 2150 gomock.Any(), 2151 ).Return(nil) 2152 innerVM.EXPECT().Shutdown(gomock.Any()).Return(nil) 2153 2154 { 2155 innerBlk := snowmantest.NewMockBlock(ctrl) 2156 innerBlkID := ids.GenerateTestID() 2157 innerVM.EXPECT().LastAccepted(gomock.Any()).Return(innerBlkID, nil) 2158 innerVM.EXPECT().GetBlock(gomock.Any(), innerBlkID).Return(innerBlk, nil) 2159 } 2160 2161 snowCtx := snowtest.Context(t, snowtest.CChainID) 2162 snowCtx.NodeID = ids.NodeIDFromCert(pTestCert) 2163 2164 require.NoError(vm.Initialize( 2165 context.Background(), 2166 snowCtx, 2167 db, 2168 nil, 2169 nil, 2170 nil, 2171 nil, 2172 nil, 2173 nil, 2174 )) 2175 defer func() { 2176 require.NoError(vm.Shutdown(context.Background())) 2177 }() 2178 2179 { 2180 pChainHeight := uint64(0) 2181 innerBlk := blockWithVerifyContext{ 2182 MockBlock: snowmantest.NewMockBlock(ctrl), 2183 MockWithVerifyContext: block.NewMockWithVerifyContext(ctrl), 2184 } 2185 innerBlk.MockWithVerifyContext.EXPECT().ShouldVerifyWithContext(gomock.Any()).Return(true, nil).Times(2) 2186 innerBlk.MockWithVerifyContext.EXPECT().VerifyWithContext(context.Background(), 2187 &block.Context{ 2188 PChainHeight: pChainHeight, 2189 }, 2190 ).Return(nil) 2191 innerBlk.MockBlock.EXPECT().Parent().Return(ids.GenerateTestID()).AnyTimes() 2192 innerBlk.MockBlock.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes() 2193 innerBlk.MockBlock.EXPECT().Bytes().Return(utils.RandomBytes(1024)).AnyTimes() 2194 2195 blk := NewMockPostForkBlock(ctrl) 2196 blk.EXPECT().getInnerBlk().Return(innerBlk).AnyTimes() 2197 blkID := ids.GenerateTestID() 2198 blk.EXPECT().ID().Return(blkID).AnyTimes() 2199 2200 require.NoError(vm.verifyAndRecordInnerBlk( 2201 context.Background(), 2202 &block.Context{ 2203 PChainHeight: pChainHeight, 2204 }, 2205 blk, 2206 )) 2207 2208 // Call VerifyWithContext again but with a different P-Chain height 2209 blk.EXPECT().setInnerBlk(innerBlk).AnyTimes() 2210 pChainHeight++ 2211 innerBlk.MockWithVerifyContext.EXPECT().VerifyWithContext(context.Background(), 2212 &block.Context{ 2213 PChainHeight: pChainHeight, 2214 }, 2215 ).Return(nil) 2216 2217 require.NoError(vm.verifyAndRecordInnerBlk( 2218 context.Background(), 2219 &block.Context{ 2220 PChainHeight: pChainHeight, 2221 }, 2222 blk, 2223 )) 2224 } 2225 2226 { 2227 // Ensure we call Verify on a block that returns 2228 // false for ShouldVerifyWithContext 2229 innerBlk := blockWithVerifyContext{ 2230 MockBlock: snowmantest.NewMockBlock(ctrl), 2231 MockWithVerifyContext: block.NewMockWithVerifyContext(ctrl), 2232 } 2233 innerBlk.MockWithVerifyContext.EXPECT().ShouldVerifyWithContext(gomock.Any()).Return(false, nil) 2234 innerBlk.MockBlock.EXPECT().Verify(gomock.Any()).Return(nil) 2235 innerBlk.MockBlock.EXPECT().Parent().Return(ids.GenerateTestID()).AnyTimes() 2236 innerBlk.MockBlock.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes() 2237 blk := NewMockPostForkBlock(ctrl) 2238 blk.EXPECT().getInnerBlk().Return(innerBlk).AnyTimes() 2239 blkID := ids.GenerateTestID() 2240 blk.EXPECT().ID().Return(blkID).AnyTimes() 2241 require.NoError(vm.verifyAndRecordInnerBlk( 2242 context.Background(), 2243 &block.Context{ 2244 PChainHeight: 1, 2245 }, 2246 blk, 2247 )) 2248 } 2249 2250 { 2251 // Ensure we call Verify on a block that doesn't have a valid context 2252 innerBlk := blockWithVerifyContext{ 2253 MockBlock: snowmantest.NewMockBlock(ctrl), 2254 MockWithVerifyContext: block.NewMockWithVerifyContext(ctrl), 2255 } 2256 innerBlk.MockBlock.EXPECT().Verify(gomock.Any()).Return(nil) 2257 innerBlk.MockBlock.EXPECT().Parent().Return(ids.GenerateTestID()).AnyTimes() 2258 innerBlk.MockBlock.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes() 2259 blk := NewMockPostForkBlock(ctrl) 2260 blk.EXPECT().getInnerBlk().Return(innerBlk).AnyTimes() 2261 blkID := ids.GenerateTestID() 2262 blk.EXPECT().ID().Return(blkID).AnyTimes() 2263 require.NoError(vm.verifyAndRecordInnerBlk(context.Background(), nil, blk)) 2264 } 2265 } 2266 2267 func TestHistoricalBlockDeletion(t *testing.T) { 2268 require := require.New(t) 2269 2270 acceptedBlocks := []*snowmantest.Block{snowmantest.Genesis} 2271 currentHeight := uint64(0) 2272 2273 initialState := []byte("genesis state") 2274 coreVM := &block.TestVM{ 2275 TestVM: common.TestVM{ 2276 T: t, 2277 InitializeF: func(context.Context, *snow.Context, database.Database, []byte, []byte, []byte, chan<- common.Message, []*common.Fx, common.AppSender) error { 2278 return nil 2279 }, 2280 }, 2281 LastAcceptedF: func(context.Context) (ids.ID, error) { 2282 return acceptedBlocks[currentHeight].ID(), nil 2283 }, 2284 GetBlockF: func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 2285 for _, blk := range acceptedBlocks { 2286 if blkID == blk.ID() { 2287 return blk, nil 2288 } 2289 } 2290 return nil, errUnknownBlock 2291 }, 2292 ParseBlockF: func(_ context.Context, b []byte) (snowman.Block, error) { 2293 for _, blk := range acceptedBlocks { 2294 if bytes.Equal(b, blk.Bytes()) { 2295 return blk, nil 2296 } 2297 } 2298 return nil, errUnknownBlock 2299 }, 2300 GetBlockIDAtHeightF: func(_ context.Context, height uint64) (ids.ID, error) { 2301 if height >= uint64(len(acceptedBlocks)) { 2302 return ids.Empty, errTooHigh 2303 } 2304 return acceptedBlocks[height].ID(), nil 2305 }, 2306 } 2307 2308 ctx := snowtest.Context(t, snowtest.CChainID) 2309 ctx.NodeID = ids.NodeIDFromCert(pTestCert) 2310 ctx.ValidatorState = &validators.TestState{ 2311 T: t, 2312 GetMinimumHeightF: func(context.Context) (uint64, error) { 2313 return snowmantest.GenesisHeight, nil 2314 }, 2315 GetCurrentHeightF: func(context.Context) (uint64, error) { 2316 return defaultPChainHeight, nil 2317 }, 2318 GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { 2319 return nil, nil 2320 }, 2321 } 2322 2323 // make sure that DBs are compressed correctly 2324 db := prefixdb.New([]byte{}, memdb.New()) 2325 2326 proVM := New( 2327 coreVM, 2328 Config{ 2329 ActivationTime: time.Unix(0, 0), 2330 DurangoTime: mockable.MaxTime, 2331 MinimumPChainHeight: 0, 2332 MinBlkDelay: DefaultMinBlockDelay, 2333 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 2334 StakingLeafSigner: pTestSigner, 2335 StakingCertLeaf: pTestCert, 2336 Registerer: prometheus.NewRegistry(), 2337 }, 2338 ) 2339 2340 require.NoError(proVM.Initialize( 2341 context.Background(), 2342 ctx, 2343 db, 2344 initialState, 2345 nil, 2346 nil, 2347 nil, 2348 nil, 2349 nil, 2350 )) 2351 2352 lastAcceptedID, err := proVM.LastAccepted(context.Background()) 2353 require.NoError(err) 2354 2355 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 2356 require.NoError(proVM.SetPreference(context.Background(), lastAcceptedID)) 2357 2358 issueBlock := func() { 2359 lastAcceptedBlock := acceptedBlocks[currentHeight] 2360 innerBlock := snowmantest.BuildChild(lastAcceptedBlock) 2361 2362 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 2363 return innerBlock, nil 2364 } 2365 proBlock, err := proVM.BuildBlock(context.Background()) 2366 require.NoError(err) 2367 2368 require.NoError(proBlock.Verify(context.Background())) 2369 require.NoError(proVM.SetPreference(context.Background(), proBlock.ID())) 2370 require.NoError(proBlock.Accept(context.Background())) 2371 2372 acceptedBlocks = append(acceptedBlocks, innerBlock) 2373 currentHeight++ 2374 } 2375 2376 requireHeights := func(start, end uint64) { 2377 for i := start; i <= end; i++ { 2378 _, err := proVM.GetBlockIDAtHeight(context.Background(), i) 2379 require.NoError(err) 2380 } 2381 } 2382 2383 requireMissingHeights := func(start, end uint64) { 2384 for i := start; i <= end; i++ { 2385 _, err := proVM.GetBlockIDAtHeight(context.Background(), i) 2386 require.ErrorIs(err, database.ErrNotFound) 2387 } 2388 } 2389 2390 requireNumHeights := func(numIndexed uint64) { 2391 requireHeights(0, 0) 2392 requireMissingHeights(1, currentHeight-numIndexed-1) 2393 requireHeights(currentHeight-numIndexed, currentHeight) 2394 } 2395 2396 // Because block pruning is disabled by default, the heights should be 2397 // populated for every accepted block. 2398 requireHeights(0, currentHeight) 2399 2400 issueBlock() 2401 requireHeights(0, currentHeight) 2402 2403 issueBlock() 2404 requireHeights(0, currentHeight) 2405 2406 issueBlock() 2407 requireHeights(0, currentHeight) 2408 2409 issueBlock() 2410 requireHeights(0, currentHeight) 2411 2412 issueBlock() 2413 requireHeights(0, currentHeight) 2414 2415 require.NoError(proVM.Shutdown(context.Background())) 2416 2417 numHistoricalBlocks := uint64(2) 2418 proVM = New( 2419 coreVM, 2420 Config{ 2421 ActivationTime: time.Time{}, 2422 DurangoTime: mockable.MaxTime, 2423 MinimumPChainHeight: 0, 2424 MinBlkDelay: DefaultMinBlockDelay, 2425 NumHistoricalBlocks: numHistoricalBlocks, 2426 StakingLeafSigner: pTestSigner, 2427 StakingCertLeaf: pTestCert, 2428 Registerer: prometheus.NewRegistry(), 2429 }, 2430 ) 2431 2432 require.NoError(proVM.Initialize( 2433 context.Background(), 2434 ctx, 2435 db, 2436 initialState, 2437 nil, 2438 nil, 2439 nil, 2440 nil, 2441 nil, 2442 )) 2443 2444 lastAcceptedID, err = proVM.LastAccepted(context.Background()) 2445 require.NoError(err) 2446 2447 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 2448 require.NoError(proVM.SetPreference(context.Background(), lastAcceptedID)) 2449 2450 // Verify that old blocks were pruned during startup 2451 requireNumHeights(numHistoricalBlocks) 2452 2453 // As we issue new blocks, the oldest indexed height should be pruned. 2454 issueBlock() 2455 requireNumHeights(numHistoricalBlocks) 2456 2457 issueBlock() 2458 requireNumHeights(numHistoricalBlocks) 2459 2460 require.NoError(proVM.Shutdown(context.Background())) 2461 2462 newNumHistoricalBlocks := numHistoricalBlocks + 2 2463 proVM = New( 2464 coreVM, 2465 Config{ 2466 ActivationTime: time.Time{}, 2467 DurangoTime: mockable.MaxTime, 2468 MinimumPChainHeight: 0, 2469 MinBlkDelay: DefaultMinBlockDelay, 2470 NumHistoricalBlocks: newNumHistoricalBlocks, 2471 StakingLeafSigner: pTestSigner, 2472 StakingCertLeaf: pTestCert, 2473 Registerer: prometheus.NewRegistry(), 2474 }, 2475 ) 2476 2477 require.NoError(proVM.Initialize( 2478 context.Background(), 2479 ctx, 2480 db, 2481 initialState, 2482 nil, 2483 nil, 2484 nil, 2485 nil, 2486 nil, 2487 )) 2488 defer func() { 2489 require.NoError(proVM.Shutdown(context.Background())) 2490 }() 2491 2492 lastAcceptedID, err = proVM.LastAccepted(context.Background()) 2493 require.NoError(err) 2494 2495 require.NoError(proVM.SetState(context.Background(), snow.NormalOp)) 2496 require.NoError(proVM.SetPreference(context.Background(), lastAcceptedID)) 2497 2498 // The height index shouldn't be modified at this point 2499 requireNumHeights(numHistoricalBlocks) 2500 2501 // As we issue new blocks, the number of indexed blocks should increase 2502 // until we hit our target again. 2503 issueBlock() 2504 requireNumHeights(numHistoricalBlocks + 1) 2505 2506 issueBlock() 2507 requireNumHeights(newNumHistoricalBlocks) 2508 2509 issueBlock() 2510 requireNumHeights(newNumHistoricalBlocks) 2511 }