github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/post_fork_option_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 "testing" 10 "time" 11 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/require" 14 15 "github.com/ava-labs/avalanchego/database" 16 "github.com/ava-labs/avalanchego/ids" 17 "github.com/ava-labs/avalanchego/snow" 18 "github.com/ava-labs/avalanchego/snow/consensus/snowman" 19 "github.com/ava-labs/avalanchego/snow/consensus/snowman/snowmantest" 20 "github.com/ava-labs/avalanchego/snow/engine/common" 21 "github.com/ava-labs/avalanchego/snow/snowtest" 22 "github.com/ava-labs/avalanchego/upgrade/upgradetest" 23 "github.com/ava-labs/avalanchego/vms/proposervm/block" 24 ) 25 26 var _ snowman.OracleBlock = (*TestOptionsBlock)(nil) 27 28 type TestOptionsBlock struct { 29 snowmantest.Block 30 opts [2]*snowmantest.Block 31 optsErr error 32 } 33 34 func (tob TestOptionsBlock) Options(context.Context) ([2]snowman.Block, error) { 35 return [2]snowman.Block{tob.opts[0], tob.opts[1]}, tob.optsErr 36 } 37 38 // ProposerBlock.Verify tests section 39 func TestBlockVerify_PostForkOption_ParentChecks(t *testing.T) { 40 require := require.New(t) 41 42 var ( 43 activationTime = time.Unix(0, 0) 44 durangoTime = activationTime 45 ) 46 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 47 defer func() { 48 require.NoError(proVM.Shutdown(context.Background())) 49 }() 50 51 // create post fork oracle block ... 52 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 53 preferredBlk := snowmantest.BuildChild(coreTestBlk) 54 oracleCoreBlk := &TestOptionsBlock{ 55 Block: *coreTestBlk, 56 opts: [2]*snowmantest.Block{ 57 preferredBlk, 58 snowmantest.BuildChild(coreTestBlk), 59 }, 60 } 61 62 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 63 return oracleCoreBlk, nil 64 } 65 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 66 switch blkID { 67 case snowmantest.GenesisID: 68 return snowmantest.Genesis, nil 69 case oracleCoreBlk.ID(): 70 return oracleCoreBlk, nil 71 case oracleCoreBlk.opts[0].ID(): 72 return oracleCoreBlk.opts[0], nil 73 case oracleCoreBlk.opts[1].ID(): 74 return oracleCoreBlk.opts[1], nil 75 default: 76 return nil, database.ErrNotFound 77 } 78 } 79 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 80 switch { 81 case bytes.Equal(b, snowmantest.GenesisBytes): 82 return snowmantest.Genesis, nil 83 case bytes.Equal(b, oracleCoreBlk.Bytes()): 84 return oracleCoreBlk, nil 85 case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()): 86 return oracleCoreBlk.opts[0], nil 87 case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()): 88 return oracleCoreBlk.opts[1], nil 89 default: 90 return nil, errUnknownBlock 91 } 92 } 93 94 parentBlk, err := proVM.BuildBlock(context.Background()) 95 require.NoError(err) 96 97 require.NoError(parentBlk.Verify(context.Background())) 98 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 99 100 // retrieve options ... 101 require.IsType(&postForkBlock{}, parentBlk) 102 postForkOracleBlk := parentBlk.(*postForkBlock) 103 opts, err := postForkOracleBlk.Options(context.Background()) 104 require.NoError(err) 105 require.IsType(&postForkOption{}, opts[0]) 106 107 // ... and verify them 108 require.NoError(opts[0].Verify(context.Background())) 109 require.NoError(opts[1].Verify(context.Background())) 110 111 // show we can build on options 112 require.NoError(proVM.SetPreference(context.Background(), opts[0].ID())) 113 114 childCoreBlk := snowmantest.BuildChild(preferredBlk) 115 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 116 return childCoreBlk, nil 117 } 118 require.NoError(waitForProposerWindow(proVM, opts[0], postForkOracleBlk.PChainHeight())) 119 120 proChild, err := proVM.BuildBlock(context.Background()) 121 require.NoError(err) 122 require.IsType(&postForkBlock{}, proChild) 123 require.NoError(proChild.Verify(context.Background())) 124 } 125 126 // ProposerBlock.Accept tests section 127 func TestBlockVerify_PostForkOption_CoreBlockVerifyIsCalledOnce(t *testing.T) { 128 require := require.New(t) 129 130 // Verify an option once; then show that another verify call would not call coreBlk.Verify() 131 var ( 132 activationTime = time.Unix(0, 0) 133 durangoTime = activationTime 134 ) 135 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 136 defer func() { 137 require.NoError(proVM.Shutdown(context.Background())) 138 }() 139 140 // create post fork oracle block ... 141 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 142 coreOpt0 := snowmantest.BuildChild(coreTestBlk) 143 coreOpt1 := snowmantest.BuildChild(coreTestBlk) 144 oracleCoreBlk := &TestOptionsBlock{ 145 Block: *coreTestBlk, 146 opts: [2]*snowmantest.Block{ 147 coreOpt0, 148 coreOpt1, 149 }, 150 } 151 152 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 153 return oracleCoreBlk, nil 154 } 155 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 156 switch blkID { 157 case snowmantest.GenesisID: 158 return snowmantest.Genesis, nil 159 case oracleCoreBlk.ID(): 160 return oracleCoreBlk, nil 161 case oracleCoreBlk.opts[0].ID(): 162 return oracleCoreBlk.opts[0], nil 163 case oracleCoreBlk.opts[1].ID(): 164 return oracleCoreBlk.opts[1], nil 165 default: 166 return nil, database.ErrNotFound 167 } 168 } 169 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 170 switch { 171 case bytes.Equal(b, snowmantest.GenesisBytes): 172 return snowmantest.Genesis, nil 173 case bytes.Equal(b, oracleCoreBlk.Bytes()): 174 return oracleCoreBlk, nil 175 case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()): 176 return oracleCoreBlk.opts[0], nil 177 case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()): 178 return oracleCoreBlk.opts[1], nil 179 default: 180 return nil, errUnknownBlock 181 } 182 } 183 184 parentBlk, err := proVM.BuildBlock(context.Background()) 185 require.NoError(err) 186 187 require.NoError(parentBlk.Verify(context.Background())) 188 require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID())) 189 190 // retrieve options ... 191 require.IsType(&postForkBlock{}, parentBlk) 192 postForkOracleBlk := parentBlk.(*postForkBlock) 193 opts, err := postForkOracleBlk.Options(context.Background()) 194 require.NoError(err) 195 require.IsType(&postForkOption{}, opts[0]) 196 197 // ... and verify them the first time 198 require.NoError(opts[0].Verify(context.Background())) 199 require.NoError(opts[1].Verify(context.Background())) 200 201 // set error on coreBlock.Verify and recall Verify() 202 coreOpt0.VerifyV = errDuplicateVerify 203 coreOpt1.VerifyV = errDuplicateVerify 204 205 // ... and verify them again. They verify without call to innerBlk 206 require.NoError(opts[0].Verify(context.Background())) 207 require.NoError(opts[1].Verify(context.Background())) 208 } 209 210 func TestBlockAccept_PostForkOption_SetsLastAcceptedBlock(t *testing.T) { 211 require := require.New(t) 212 213 var ( 214 activationTime = time.Unix(0, 0) 215 durangoTime = activationTime 216 ) 217 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 218 defer func() { 219 require.NoError(proVM.Shutdown(context.Background())) 220 }() 221 222 // create post fork oracle block ... 223 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 224 oracleCoreBlk := &TestOptionsBlock{ 225 Block: *coreTestBlk, 226 opts: [2]*snowmantest.Block{ 227 snowmantest.BuildChild(coreTestBlk), 228 snowmantest.BuildChild(coreTestBlk), 229 }, 230 } 231 232 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 233 return oracleCoreBlk, nil 234 } 235 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 236 switch blkID { 237 case snowmantest.GenesisID: 238 return snowmantest.Genesis, nil 239 case oracleCoreBlk.ID(): 240 return oracleCoreBlk, nil 241 case oracleCoreBlk.opts[0].ID(): 242 return oracleCoreBlk.opts[0], nil 243 case oracleCoreBlk.opts[1].ID(): 244 return oracleCoreBlk.opts[1], nil 245 default: 246 return nil, database.ErrNotFound 247 } 248 } 249 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 250 switch { 251 case bytes.Equal(b, snowmantest.GenesisBytes): 252 return snowmantest.Genesis, nil 253 case bytes.Equal(b, oracleCoreBlk.Bytes()): 254 return oracleCoreBlk, nil 255 case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()): 256 return oracleCoreBlk.opts[0], nil 257 case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()): 258 return oracleCoreBlk.opts[1], nil 259 default: 260 return nil, errUnknownBlock 261 } 262 } 263 264 parentBlk, err := proVM.BuildBlock(context.Background()) 265 require.NoError(err) 266 267 // accept oracle block 268 require.NoError(parentBlk.Accept(context.Background())) 269 270 coreVM.LastAcceptedF = snowmantest.MakeLastAcceptedBlockF( 271 []*snowmantest.Block{ 272 snowmantest.Genesis, 273 &oracleCoreBlk.Block, 274 }, 275 oracleCoreBlk.opts[:], 276 ) 277 acceptedID, err := proVM.LastAccepted(context.Background()) 278 require.NoError(err) 279 require.Equal(parentBlk.ID(), acceptedID) 280 281 // accept one of the options 282 require.IsType(&postForkBlock{}, parentBlk) 283 postForkOracleBlk := parentBlk.(*postForkBlock) 284 opts, err := postForkOracleBlk.Options(context.Background()) 285 require.NoError(err) 286 287 require.NoError(opts[0].Accept(context.Background())) 288 289 acceptedID, err = proVM.LastAccepted(context.Background()) 290 require.NoError(err) 291 require.Equal(opts[0].ID(), acceptedID) 292 } 293 294 // ProposerBlock.Reject tests section 295 func TestBlockReject_InnerBlockIsNotRejected(t *testing.T) { 296 require := require.New(t) 297 298 var ( 299 activationTime = time.Unix(0, 0) 300 durangoTime = activationTime 301 ) 302 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 303 defer func() { 304 require.NoError(proVM.Shutdown(context.Background())) 305 }() 306 307 // create post fork oracle block ... 308 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 309 oracleCoreBlk := &TestOptionsBlock{ 310 Block: *coreTestBlk, 311 opts: [2]*snowmantest.Block{ 312 snowmantest.BuildChild(coreTestBlk), 313 snowmantest.BuildChild(coreTestBlk), 314 }, 315 } 316 317 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 318 return oracleCoreBlk, nil 319 } 320 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 321 switch blkID { 322 case snowmantest.GenesisID: 323 return snowmantest.Genesis, nil 324 case oracleCoreBlk.ID(): 325 return oracleCoreBlk, nil 326 case oracleCoreBlk.opts[0].ID(): 327 return oracleCoreBlk.opts[0], nil 328 case oracleCoreBlk.opts[1].ID(): 329 return oracleCoreBlk.opts[1], nil 330 default: 331 return nil, database.ErrNotFound 332 } 333 } 334 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 335 switch { 336 case bytes.Equal(b, snowmantest.GenesisBytes): 337 return snowmantest.Genesis, nil 338 case bytes.Equal(b, oracleCoreBlk.Bytes()): 339 return oracleCoreBlk, nil 340 case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()): 341 return oracleCoreBlk.opts[0], nil 342 case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()): 343 return oracleCoreBlk.opts[1], nil 344 default: 345 return nil, errUnknownBlock 346 } 347 } 348 349 builtBlk, err := proVM.BuildBlock(context.Background()) 350 require.NoError(err) 351 352 // reject oracle block 353 require.NoError(builtBlk.Reject(context.Background())) 354 require.NotEqual(snowtest.Rejected, oracleCoreBlk.Status) 355 356 // reject an option 357 require.IsType(&postForkBlock{}, builtBlk) 358 postForkOracleBlk := builtBlk.(*postForkBlock) 359 opts, err := postForkOracleBlk.Options(context.Background()) 360 require.NoError(err) 361 362 require.NoError(opts[0].Reject(context.Background())) 363 require.NotEqual(snowtest.Rejected, oracleCoreBlk.opts[0].Status) 364 } 365 366 func TestBlockVerify_PostForkOption_ParentIsNotOracleWithError(t *testing.T) { 367 require := require.New(t) 368 369 // Verify an option once; then show that another verify call would not call coreBlk.Verify() 370 var ( 371 activationTime = time.Unix(0, 0) 372 durangoTime = activationTime 373 ) 374 coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0) 375 defer func() { 376 require.NoError(proVM.Shutdown(context.Background())) 377 }() 378 379 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 380 coreBlk := &TestOptionsBlock{ 381 Block: *coreTestBlk, 382 optsErr: snowman.ErrNotOracle, 383 } 384 385 coreChildBlk := snowmantest.BuildChild(coreTestBlk) 386 387 coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) { 388 return coreBlk, nil 389 } 390 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 391 switch blkID { 392 case snowmantest.GenesisID: 393 return snowmantest.Genesis, nil 394 case coreBlk.ID(): 395 return coreBlk, nil 396 case coreChildBlk.ID(): 397 return coreChildBlk, nil 398 default: 399 return nil, database.ErrNotFound 400 } 401 } 402 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 403 switch { 404 case bytes.Equal(b, snowmantest.GenesisBytes): 405 return snowmantest.Genesis, nil 406 case bytes.Equal(b, coreBlk.Bytes()): 407 return coreBlk, nil 408 case bytes.Equal(b, coreChildBlk.Bytes()): 409 return coreChildBlk, nil 410 default: 411 return nil, errUnknownBlock 412 } 413 } 414 415 parentBlk, err := proVM.BuildBlock(context.Background()) 416 require.NoError(err) 417 418 require.IsType(&postForkBlock{}, parentBlk) 419 postForkBlk := parentBlk.(*postForkBlock) 420 _, err = postForkBlk.Options(context.Background()) 421 require.Equal(snowman.ErrNotOracle, err) 422 423 // Build the child 424 statelessChild, err := block.BuildOption( 425 postForkBlk.ID(), 426 coreChildBlk.Bytes(), 427 ) 428 require.NoError(err) 429 430 invalidChild, err := proVM.ParseBlock(context.Background(), statelessChild.Bytes()) 431 if err != nil { 432 // A failure to parse is okay here 433 return 434 } 435 436 err = invalidChild.Verify(context.Background()) 437 require.ErrorIs(err, database.ErrNotFound) 438 } 439 440 func TestOptionTimestampValidity(t *testing.T) { 441 require := require.New(t) 442 443 var ( 444 activationTime = time.Unix(0, 0) 445 durangoTime = activationTime 446 ) 447 coreVM, _, proVM, db := initTestProposerVM(t, activationTime, durangoTime, 0) 448 449 coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis) 450 coreOracleBlk := &TestOptionsBlock{ 451 Block: *coreTestBlk, 452 opts: [2]*snowmantest.Block{ 453 snowmantest.BuildChild(coreTestBlk), 454 snowmantest.BuildChild(coreTestBlk), 455 }, 456 } 457 458 oracleBlkTime := proVM.Time().Truncate(time.Second) 459 statelessBlock, err := block.BuildUnsigned( 460 snowmantest.GenesisID, 461 oracleBlkTime, 462 0, 463 coreOracleBlk.Bytes(), 464 ) 465 require.NoError(err) 466 467 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 468 switch blkID { 469 case snowmantest.GenesisID: 470 return snowmantest.Genesis, nil 471 case coreOracleBlk.ID(): 472 return coreOracleBlk, nil 473 case coreOracleBlk.opts[0].ID(): 474 return coreOracleBlk.opts[0], nil 475 case coreOracleBlk.opts[1].ID(): 476 return coreOracleBlk.opts[1], nil 477 default: 478 return nil, errUnknownBlock 479 } 480 } 481 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 482 switch { 483 case bytes.Equal(b, snowmantest.GenesisBytes): 484 return snowmantest.Genesis, nil 485 case bytes.Equal(b, coreOracleBlk.Bytes()): 486 return coreOracleBlk, nil 487 case bytes.Equal(b, coreOracleBlk.opts[0].Bytes()): 488 return coreOracleBlk.opts[0], nil 489 case bytes.Equal(b, coreOracleBlk.opts[1].Bytes()): 490 return coreOracleBlk.opts[1], nil 491 default: 492 return nil, errUnknownBlock 493 } 494 } 495 496 statefulBlock, err := proVM.ParseBlock(context.Background(), statelessBlock.Bytes()) 497 require.NoError(err) 498 499 require.NoError(statefulBlock.Verify(context.Background())) 500 501 statefulOracleBlock, ok := statefulBlock.(snowman.OracleBlock) 502 require.True(ok) 503 504 options, err := statefulOracleBlock.Options(context.Background()) 505 require.NoError(err) 506 507 option := options[0] 508 require.NoError(option.Verify(context.Background())) 509 510 require.NoError(statefulBlock.Accept(context.Background())) 511 512 coreVM.GetBlockF = func(context.Context, ids.ID) (snowman.Block, error) { 513 require.FailNow("called GetBlock when unable to handle the error") 514 return nil, nil 515 } 516 coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) { 517 require.FailNow("called ParseBlock when unable to handle the error") 518 return nil, nil 519 } 520 521 require.Equal(oracleBlkTime, option.Timestamp()) 522 523 require.NoError(option.Accept(context.Background())) 524 require.NoError(proVM.Shutdown(context.Background())) 525 526 // Restart the node. 527 ctx := proVM.ctx 528 proVM = New( 529 coreVM, 530 Config{ 531 Upgrades: upgradetest.GetConfig(upgradetest.Latest), 532 MinBlkDelay: DefaultMinBlockDelay, 533 NumHistoricalBlocks: DefaultNumHistoricalBlocks, 534 StakingLeafSigner: pTestSigner, 535 StakingCertLeaf: pTestCert, 536 Registerer: prometheus.NewRegistry(), 537 }, 538 ) 539 540 coreVM.InitializeF = func( 541 context.Context, 542 *snow.Context, 543 database.Database, 544 []byte, 545 []byte, 546 []byte, 547 chan<- common.Message, 548 []*common.Fx, 549 common.AppSender, 550 ) error { 551 return nil 552 } 553 coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) { 554 return coreOracleBlk.opts[0].ID(), nil 555 } 556 557 coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 558 switch blkID { 559 case snowmantest.GenesisID: 560 return snowmantest.Genesis, nil 561 case coreOracleBlk.ID(): 562 return coreOracleBlk, nil 563 case coreOracleBlk.opts[0].ID(): 564 return coreOracleBlk.opts[0], nil 565 case coreOracleBlk.opts[1].ID(): 566 return coreOracleBlk.opts[1], nil 567 default: 568 return nil, errUnknownBlock 569 } 570 } 571 coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 572 switch { 573 case bytes.Equal(b, snowmantest.GenesisBytes): 574 return snowmantest.Genesis, nil 575 case bytes.Equal(b, coreOracleBlk.Bytes()): 576 return coreOracleBlk, nil 577 case bytes.Equal(b, coreOracleBlk.opts[0].Bytes()): 578 return coreOracleBlk.opts[0], nil 579 case bytes.Equal(b, coreOracleBlk.opts[1].Bytes()): 580 return coreOracleBlk.opts[1], nil 581 default: 582 return nil, errUnknownBlock 583 } 584 } 585 586 require.NoError(proVM.Initialize( 587 context.Background(), 588 ctx, 589 db, 590 nil, 591 nil, 592 nil, 593 nil, 594 nil, 595 nil, 596 )) 597 defer func() { 598 require.NoError(proVM.Shutdown(context.Background())) 599 }() 600 601 statefulOptionBlock, err := proVM.ParseBlock(context.Background(), option.Bytes()) 602 require.NoError(err) 603 604 require.LessOrEqual(statefulOptionBlock.Height(), proVM.lastAcceptedHeight) 605 606 coreVM.GetBlockF = func(context.Context, ids.ID) (snowman.Block, error) { 607 require.FailNow("called GetBlock when unable to handle the error") 608 return nil, nil 609 } 610 coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) { 611 require.FailNow("called ParseBlock when unable to handle the error") 612 return nil, nil 613 } 614 615 require.Equal(oracleBlkTime, statefulOptionBlock.Timestamp()) 616 }