github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/snowman/transitive_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 snowman 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "fmt" 11 "testing" 12 "time" 13 14 "github.com/prometheus/client_golang/prometheus" 15 "github.com/stretchr/testify/require" 16 17 "github.com/MetalBlockchain/metalgo/cache" 18 "github.com/MetalBlockchain/metalgo/database" 19 "github.com/MetalBlockchain/metalgo/ids" 20 "github.com/MetalBlockchain/metalgo/snow" 21 "github.com/MetalBlockchain/metalgo/snow/choices" 22 "github.com/MetalBlockchain/metalgo/snow/consensus/snowball" 23 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 24 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest" 25 "github.com/MetalBlockchain/metalgo/snow/engine/common" 26 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/ancestor" 27 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 28 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/getter" 29 "github.com/MetalBlockchain/metalgo/snow/snowtest" 30 "github.com/MetalBlockchain/metalgo/snow/validators" 31 "github.com/MetalBlockchain/metalgo/utils/set" 32 "github.com/MetalBlockchain/metalgo/version" 33 ) 34 35 var ( 36 errUnknownBlock = errors.New("unknown block") 37 errUnknownBytes = errors.New("unknown bytes") 38 errInvalid = errors.New("invalid") 39 errTest = errors.New("non-nil test") 40 ) 41 42 func MakeGetBlockF(blks ...[]*snowmantest.Block) func(context.Context, ids.ID) (snowman.Block, error) { 43 return func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 44 for _, blkSet := range blks { 45 for _, blk := range blkSet { 46 if blkID == blk.ID() { 47 return blk, nil 48 } 49 } 50 } 51 return nil, errUnknownBlock 52 } 53 } 54 55 func MakeParseBlockF(blks ...[]*snowmantest.Block) func(context.Context, []byte) (snowman.Block, error) { 56 return func(_ context.Context, blkBytes []byte) (snowman.Block, error) { 57 for _, blkSet := range blks { 58 for _, blk := range blkSet { 59 if bytes.Equal(blkBytes, blk.Bytes()) { 60 return blk, nil 61 } 62 } 63 } 64 return nil, errUnknownBlock 65 } 66 } 67 68 func MakeLastAcceptedBlockF(defaultBlk *snowmantest.Block, blks ...[]*snowmantest.Block) func(context.Context) (ids.ID, error) { 69 return func(_ context.Context) (ids.ID, error) { 70 highestHeight := defaultBlk.Height() 71 highestID := defaultBlk.ID() 72 for _, blkSet := range blks { 73 for _, blk := range blkSet { 74 if blk.Status() == choices.Accepted && blk.Height() > highestHeight { 75 highestHeight = blk.Height() 76 highestID = blk.ID() 77 } 78 } 79 } 80 return highestID, nil 81 } 82 } 83 84 func setup(t *testing.T, config Config) (ids.NodeID, validators.Manager, *common.SenderTest, *block.TestVM, *Transitive) { 85 require := require.New(t) 86 87 vdr := ids.GenerateTestNodeID() 88 require.NoError(config.Validators.AddStaker(config.Ctx.SubnetID, vdr, nil, ids.Empty, 1)) 89 require.NoError(config.ConnectedValidators.Connected(context.Background(), vdr, version.CurrentApp)) 90 config.Validators.RegisterSetCallbackListener(config.Ctx.SubnetID, config.ConnectedValidators) 91 92 sender := &common.SenderTest{T: t} 93 config.Sender = sender 94 sender.Default(true) 95 96 vm := &block.TestVM{} 97 vm.T = t 98 config.VM = vm 99 100 snowGetHandler, err := getter.New( 101 vm, 102 sender, 103 config.Ctx.Log, 104 time.Second, 105 2000, 106 config.Ctx.Registerer, 107 ) 108 require.NoError(err) 109 config.AllGetsServer = snowGetHandler 110 111 vm.Default(true) 112 vm.CantSetState = false 113 vm.CantSetPreference = false 114 115 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 116 return snowmantest.GenesisID, nil 117 } 118 119 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 120 switch blkID { 121 case snowmantest.GenesisID: 122 return snowmantest.Genesis, nil 123 default: 124 return nil, errUnknownBlock 125 } 126 } 127 128 te, err := New(config) 129 require.NoError(err) 130 131 require.NoError(te.Start(context.Background(), 0)) 132 133 vm.GetBlockF = nil 134 vm.LastAcceptedF = nil 135 return vdr, config.Validators, sender, vm, te 136 } 137 138 func TestEngineDropsAttemptToIssueBlockAfterFailedRequest(t *testing.T) { 139 require := require.New(t) 140 141 peerID, _, sender, vm, engine := setup(t, DefaultConfig(t)) 142 143 parent := snowmantest.BuildChild(snowmantest.Genesis) 144 child := snowmantest.BuildChild(parent) 145 146 var request *common.Request 147 sender.SendGetF = func(_ context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) { 148 require.Nil(request) 149 request = &common.Request{ 150 NodeID: nodeID, 151 RequestID: requestID, 152 } 153 require.Equal(parent.ID(), blkID) 154 } 155 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 156 require.Equal(child.Bytes(), b) 157 return child, nil 158 } 159 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 160 switch blkID { 161 case snowmantest.GenesisID: 162 return snowmantest.Genesis, nil 163 default: 164 return nil, errUnknownBlock 165 } 166 } 167 168 // Attempting to add [child] will cause [parent] to be requested. While the 169 // request for [parent] is outstanding, [child] will be registered into a 170 // job blocked on [parent]'s issuance. 171 require.NoError(engine.Put(context.Background(), peerID, 0, child.Bytes())) 172 require.NotNil(request) 173 require.Equal(1, engine.blocked.NumDependencies()) 174 175 vm.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) { 176 return nil, errUnknownBytes 177 } 178 179 // Because this request doesn't provide [parent], the [child] job should be 180 // cancelled. 181 require.NoError(engine.Put(context.Background(), request.NodeID, request.RequestID, nil)) 182 require.Zero(engine.blocked.NumDependencies()) 183 } 184 185 func TestEngineQuery(t *testing.T) { 186 require := require.New(t) 187 188 peerID, _, sender, vm, engine := setup(t, DefaultConfig(t)) 189 190 parent := snowmantest.BuildChild(snowmantest.Genesis) 191 child := snowmantest.BuildChild(parent) 192 193 var sendChitsCalled bool 194 sender.SendChitsF = func(_ context.Context, _ ids.NodeID, requestID uint32, preferredID ids.ID, preferredIDByHeight ids.ID, accepted ids.ID) { 195 require.False(sendChitsCalled) 196 sendChitsCalled = true 197 require.Equal(uint32(15), requestID) 198 require.Equal(snowmantest.GenesisID, preferredID) 199 require.Equal(snowmantest.GenesisID, preferredIDByHeight) 200 require.Equal(snowmantest.GenesisID, accepted) 201 } 202 203 var getBlockCalled bool 204 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 205 getBlockCalled = true 206 207 switch blkID { 208 case snowmantest.GenesisID: 209 return snowmantest.Genesis, nil 210 default: 211 return nil, errUnknownBlock 212 } 213 } 214 215 var getRequest *common.Request 216 sender.SendGetF = func(_ context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) { 217 require.Nil(getRequest) 218 getRequest = &common.Request{ 219 NodeID: nodeID, 220 RequestID: requestID, 221 } 222 require.Equal(peerID, nodeID) 223 require.Contains([]ids.ID{ 224 parent.ID(), 225 snowmantest.GenesisID, 226 }, blkID) 227 } 228 229 // Handling a pull query for [parent] should result in immediately 230 // responding with chits for [Genesis] along with a request for [parent]. 231 require.NoError(engine.PullQuery(context.Background(), peerID, 15, parent.ID(), 1)) 232 require.True(sendChitsCalled) 233 require.True(getBlockCalled) 234 require.NotNil(getRequest) 235 236 var queryRequest *common.Request 237 sender.SendPullQueryF = func(_ context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, blockID ids.ID, requestedHeight uint64) { 238 require.Nil(queryRequest) 239 require.Equal(set.Of(peerID), nodeIDs) 240 queryRequest = &common.Request{ 241 NodeID: peerID, 242 RequestID: requestID, 243 } 244 require.Equal(parent.ID(), blockID) 245 require.Equal(uint64(1), requestedHeight) 246 } 247 248 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 249 require.Equal(parent.Bytes(), b) 250 return parent, nil 251 } 252 253 // After receiving [parent], the engine will parse it, issue it, and then 254 // send a pull query. 255 require.NoError(engine.Put(context.Background(), getRequest.NodeID, getRequest.RequestID, parent.Bytes())) 256 require.NotNil(queryRequest) 257 258 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 259 switch blkID { 260 case parent.ID(), child.ID(): 261 return nil, errUnknownBlock 262 } 263 require.FailNow(errUnknownBlock.Error()) 264 return nil, errUnknownBlock 265 } 266 vm.ParseBlockF = nil 267 268 getRequest = nil 269 sender.SendGetF = func(_ context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) { 270 require.Nil(getRequest) 271 getRequest = &common.Request{ 272 NodeID: nodeID, 273 RequestID: requestID, 274 } 275 require.Equal(peerID, nodeID) 276 require.Equal(child.ID(), blkID) 277 } 278 279 // Handling chits for [child] register a voter job blocking on [child]'s 280 // issuance and send a request for [child]. 281 require.NoError(engine.Chits(context.Background(), queryRequest.NodeID, queryRequest.RequestID, child.ID(), child.ID(), child.ID())) 282 283 queryRequest = nil 284 sender.SendPullQueryF = func(_ context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, blockID ids.ID, requestedHeight uint64) { 285 require.Nil(queryRequest) 286 require.Equal(set.Of(peerID), nodeIDs) 287 queryRequest = &common.Request{ 288 NodeID: peerID, 289 RequestID: requestID, 290 } 291 require.Equal(child.ID(), blockID) 292 require.Equal(uint64(1), requestedHeight) 293 } 294 295 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 296 require.Equal(child.Bytes(), b) 297 298 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 299 switch blkID { 300 case parent.ID(): 301 return parent, nil 302 case child.ID(): 303 return child, nil 304 } 305 require.FailNow(errUnknownBlock.Error()) 306 return nil, errUnknownBlock 307 } 308 309 return child, nil 310 } 311 312 // After receiving [child], the engine will parse it, issue it, and then 313 // apply the votes received during the poll for [parent]. Applying the votes 314 // should cause both [parent] and [child] to be accepted. 315 require.NoError(engine.Put(context.Background(), getRequest.NodeID, getRequest.RequestID, child.Bytes())) 316 require.Equal(choices.Accepted, parent.Status()) 317 require.Equal(choices.Accepted, child.Status()) 318 require.Zero(engine.blocked.NumDependencies()) 319 } 320 321 func TestEngineMultipleQuery(t *testing.T) { 322 require := require.New(t) 323 324 engCfg := DefaultConfig(t) 325 engCfg.Params = snowball.Parameters{ 326 K: 3, 327 AlphaPreference: 2, 328 AlphaConfidence: 2, 329 Beta: 1, 330 ConcurrentRepolls: 1, 331 OptimalProcessing: 1, 332 MaxOutstandingItems: 1, 333 MaxItemProcessingTime: 1, 334 } 335 336 vals := validators.NewManager() 337 engCfg.Validators = vals 338 339 vdr0 := ids.GenerateTestNodeID() 340 vdr1 := ids.GenerateTestNodeID() 341 vdr2 := ids.GenerateTestNodeID() 342 343 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr0, nil, ids.Empty, 1)) 344 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr1, nil, ids.Empty, 1)) 345 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr2, nil, ids.Empty, 1)) 346 347 sender := &common.SenderTest{T: t} 348 engCfg.Sender = sender 349 sender.Default(true) 350 351 vm := &block.TestVM{} 352 vm.T = t 353 engCfg.VM = vm 354 355 vm.Default(true) 356 vm.CantSetState = false 357 vm.CantSetPreference = false 358 359 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 360 return snowmantest.GenesisID, nil 361 } 362 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 363 require.Equal(snowmantest.GenesisID, blkID) 364 return snowmantest.Genesis, nil 365 } 366 367 te, err := New(engCfg) 368 require.NoError(err) 369 370 require.NoError(te.Start(context.Background(), 0)) 371 372 vm.GetBlockF = nil 373 vm.LastAcceptedF = nil 374 375 blk0 := snowmantest.BuildChild(snowmantest.Genesis) 376 blk1 := snowmantest.BuildChild(blk0) 377 378 queried := new(bool) 379 queryRequestID := new(uint32) 380 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 381 require.False(*queried) 382 *queried = true 383 *queryRequestID = requestID 384 vdrSet := set.Of(vdr0, vdr1, vdr2) 385 require.Equal(vdrSet, inVdrs) 386 require.Equal(blk0.ID(), blkID) 387 require.Equal(uint64(1), requestedHeight) 388 } 389 390 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 391 switch blkID { 392 case snowmantest.GenesisID: 393 return snowmantest.Genesis, nil 394 default: 395 return nil, errUnknownBlock 396 } 397 } 398 399 require.NoError(te.issue( 400 context.Background(), 401 te.Ctx.NodeID, 402 blk0, 403 false, 404 te.metrics.issued.WithLabelValues(unknownSource), 405 )) 406 407 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 408 switch id { 409 case snowmantest.GenesisID: 410 return snowmantest.Genesis, nil 411 case blk0.ID(): 412 return blk0, nil 413 case blk1.ID(): 414 return nil, errUnknownBlock 415 } 416 require.FailNow(errUnknownBlock.Error()) 417 return nil, errUnknownBlock 418 } 419 420 asked := new(bool) 421 getRequestID := new(uint32) 422 sender.SendGetF = func(_ context.Context, inVdr ids.NodeID, requestID uint32, blkID ids.ID) { 423 require.False(*asked) 424 *asked = true 425 *getRequestID = requestID 426 require.Equal(vdr0, inVdr) 427 require.Equal(blk1.ID(), blkID) 428 } 429 require.NoError(te.Chits(context.Background(), vdr0, *queryRequestID, blk1.ID(), blk1.ID(), blk1.ID())) 430 require.NoError(te.Chits(context.Background(), vdr1, *queryRequestID, blk1.ID(), blk1.ID(), blk1.ID())) 431 432 vm.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) { 433 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 434 switch { 435 case blkID == blk0.ID(): 436 return blk0, nil 437 case blkID == blk1.ID(): 438 return blk1, nil 439 } 440 require.FailNow(errUnknownBlock.Error()) 441 return nil, errUnknownBlock 442 } 443 444 return blk1, nil 445 } 446 447 *queried = false 448 secondQueryRequestID := new(uint32) 449 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 450 require.False(*queried) 451 *queried = true 452 *secondQueryRequestID = requestID 453 vdrSet := set.Of(vdr0, vdr1, vdr2) 454 require.Equal(vdrSet, inVdrs) 455 require.Equal(blk1.ID(), blkID) 456 require.Equal(uint64(1), requestedHeight) 457 } 458 require.NoError(te.Put(context.Background(), vdr0, *getRequestID, blk1.Bytes())) 459 460 // Should be dropped because the query was already filled 461 require.NoError(te.Chits(context.Background(), vdr2, *queryRequestID, blk0.ID(), blk0.ID(), blk0.ID())) 462 463 require.Equal(choices.Accepted, blk1.Status()) 464 require.Zero(te.blocked.NumDependencies()) 465 } 466 467 func TestEngineBlockedIssue(t *testing.T) { 468 require := require.New(t) 469 470 _, _, sender, vm, te := setup(t, DefaultConfig(t)) 471 472 sender.Default(false) 473 474 blk0 := snowmantest.BuildChild(snowmantest.Genesis) 475 blk1 := snowmantest.BuildChild(blk0) 476 477 sender.SendGetF = func(context.Context, ids.NodeID, uint32, ids.ID) {} 478 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 479 switch blkID { 480 case snowmantest.GenesisID: 481 return snowmantest.Genesis, nil 482 case blk0.ID(): 483 return blk0, nil 484 default: 485 return nil, errUnknownBlock 486 } 487 } 488 489 require.NoError(te.issue( 490 context.Background(), 491 te.Ctx.NodeID, 492 blk1, 493 false, 494 te.metrics.issued.WithLabelValues(unknownSource), 495 )) 496 497 require.NoError(te.issue( 498 context.Background(), 499 te.Ctx.NodeID, 500 blk0, 501 false, 502 te.metrics.issued.WithLabelValues(unknownSource), 503 )) 504 505 require.Equal(blk1.ID(), te.Consensus.Preference()) 506 } 507 508 func TestEngineRespondsToGetRequest(t *testing.T) { 509 require := require.New(t) 510 511 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 512 513 sender.Default(false) 514 515 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 516 require.Equal(snowmantest.GenesisID, id) 517 return snowmantest.Genesis, nil 518 } 519 520 var sentPut bool 521 sender.SendPutF = func(_ context.Context, nodeID ids.NodeID, requestID uint32, blk []byte) { 522 require.False(sentPut) 523 sentPut = true 524 525 require.Equal(vdr, nodeID) 526 require.Equal(uint32(123), requestID) 527 require.Equal(snowmantest.GenesisBytes, blk) 528 } 529 530 require.NoError(te.Get(context.Background(), vdr, 123, snowmantest.GenesisID)) 531 require.True(sentPut) 532 } 533 534 func TestEnginePushQuery(t *testing.T) { 535 require := require.New(t) 536 537 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 538 539 sender.Default(true) 540 541 blk := snowmantest.BuildChild(snowmantest.Genesis) 542 543 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 544 if bytes.Equal(b, blk.Bytes()) { 545 return blk, nil 546 } 547 return nil, errUnknownBytes 548 } 549 550 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 551 switch blkID { 552 case snowmantest.GenesisID: 553 return snowmantest.Genesis, nil 554 case blk.ID(): 555 return blk, nil 556 default: 557 return nil, errUnknownBlock 558 } 559 } 560 561 chitted := new(bool) 562 sender.SendChitsF = func(_ context.Context, inVdr ids.NodeID, requestID uint32, preferredID ids.ID, preferredIDByHeight ids.ID, acceptedID ids.ID) { 563 require.False(*chitted) 564 *chitted = true 565 require.Equal(vdr, inVdr) 566 require.Equal(uint32(20), requestID) 567 require.Equal(snowmantest.GenesisID, preferredID) 568 require.Equal(snowmantest.GenesisID, preferredIDByHeight) 569 require.Equal(snowmantest.GenesisID, acceptedID) 570 } 571 572 queried := new(bool) 573 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], _ uint32, blkID ids.ID, requestedHeight uint64) { 574 require.False(*queried) 575 *queried = true 576 vdrSet := set.Of(vdr) 577 require.True(inVdrs.Equals(vdrSet)) 578 require.Equal(blk.ID(), blkID) 579 require.Equal(uint64(1), requestedHeight) 580 } 581 582 require.NoError(te.PushQuery(context.Background(), vdr, 20, blk.Bytes(), 1)) 583 584 require.True(*chitted) 585 require.True(*queried) 586 } 587 588 func TestEngineBuildBlock(t *testing.T) { 589 require := require.New(t) 590 591 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 592 593 sender.Default(true) 594 595 blk := snowmantest.BuildChild(snowmantest.Genesis) 596 597 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 598 switch blkID { 599 case snowmantest.GenesisID: 600 return snowmantest.Genesis, nil 601 default: 602 return nil, errUnknownBlock 603 } 604 } 605 606 sender.SendPullQueryF = func(context.Context, set.Set[ids.NodeID], uint32, ids.ID, uint64) { 607 require.FailNow("should not be sending pulls when we are the block producer") 608 } 609 610 pushSent := new(bool) 611 sender.SendPushQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], _ uint32, _ []byte, _ uint64) { 612 require.False(*pushSent) 613 *pushSent = true 614 vdrSet := set.Of(vdr) 615 require.Equal(vdrSet, inVdrs) 616 } 617 618 vm.BuildBlockF = func(context.Context) (snowman.Block, error) { 619 return blk, nil 620 } 621 require.NoError(te.Notify(context.Background(), common.PendingTxs)) 622 623 require.True(*pushSent) 624 } 625 626 func TestEngineRepoll(t *testing.T) { 627 require := require.New(t) 628 vdr, _, sender, _, te := setup(t, DefaultConfig(t)) 629 630 sender.Default(true) 631 632 queried := new(bool) 633 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], _ uint32, _ ids.ID, _ uint64) { 634 require.False(*queried) 635 *queried = true 636 vdrSet := set.Of(vdr) 637 require.Equal(vdrSet, inVdrs) 638 } 639 640 te.repoll(context.Background()) 641 642 require.True(*queried) 643 } 644 645 func TestVoteCanceling(t *testing.T) { 646 require := require.New(t) 647 648 engCfg := DefaultConfig(t) 649 engCfg.Params = snowball.Parameters{ 650 K: 3, 651 AlphaPreference: 2, 652 AlphaConfidence: 2, 653 Beta: 1, 654 ConcurrentRepolls: 1, 655 OptimalProcessing: 1, 656 MaxOutstandingItems: 1, 657 MaxItemProcessingTime: 1, 658 } 659 660 vals := validators.NewManager() 661 engCfg.Validators = vals 662 663 vdr0 := ids.GenerateTestNodeID() 664 vdr1 := ids.GenerateTestNodeID() 665 vdr2 := ids.GenerateTestNodeID() 666 667 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr0, nil, ids.Empty, 1)) 668 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr1, nil, ids.Empty, 1)) 669 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr2, nil, ids.Empty, 1)) 670 671 sender := &common.SenderTest{T: t} 672 engCfg.Sender = sender 673 sender.Default(true) 674 675 vm := &block.TestVM{} 676 vm.T = t 677 engCfg.VM = vm 678 679 vm.Default(true) 680 vm.CantSetState = false 681 vm.CantSetPreference = false 682 683 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 684 return snowmantest.GenesisID, nil 685 } 686 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 687 require.Equal(snowmantest.GenesisID, id) 688 return snowmantest.Genesis, nil 689 } 690 691 te, err := New(engCfg) 692 require.NoError(err) 693 694 require.NoError(te.Start(context.Background(), 0)) 695 696 vm.LastAcceptedF = nil 697 698 blk := snowmantest.BuildChild(snowmantest.Genesis) 699 700 queried := new(bool) 701 queryRequestID := new(uint32) 702 sender.SendPushQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkBytes []byte, requestedHeight uint64) { 703 require.False(*queried) 704 *queried = true 705 *queryRequestID = requestID 706 vdrSet := set.Of(vdr0, vdr1, vdr2) 707 require.Equal(vdrSet, inVdrs) 708 require.Equal(blk.Bytes(), blkBytes) 709 require.Equal(uint64(1), requestedHeight) 710 } 711 712 require.NoError(te.issue( 713 context.Background(), 714 te.Ctx.NodeID, 715 blk, 716 true, 717 te.metrics.issued.WithLabelValues(unknownSource), 718 )) 719 720 require.Equal(1, te.polls.Len()) 721 722 require.NoError(te.QueryFailed(context.Background(), vdr0, *queryRequestID)) 723 724 require.Equal(1, te.polls.Len()) 725 726 repolled := new(bool) 727 sender.SendPullQueryF = func(context.Context, set.Set[ids.NodeID], uint32, ids.ID, uint64) { 728 *repolled = true 729 } 730 require.NoError(te.QueryFailed(context.Background(), vdr1, *queryRequestID)) 731 732 require.True(*repolled) 733 } 734 735 func TestEngineNoQuery(t *testing.T) { 736 require := require.New(t) 737 738 engCfg := DefaultConfig(t) 739 740 sender := &common.SenderTest{T: t} 741 engCfg.Sender = sender 742 sender.Default(true) 743 744 vm := &block.TestVM{} 745 vm.T = t 746 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 747 return snowmantest.GenesisID, nil 748 } 749 750 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 751 if blkID == snowmantest.GenesisID { 752 return snowmantest.Genesis, nil 753 } 754 return nil, errUnknownBlock 755 } 756 757 engCfg.VM = vm 758 759 te, err := New(engCfg) 760 require.NoError(err) 761 762 require.NoError(te.Start(context.Background(), 0)) 763 764 blk := snowmantest.BuildChild(snowmantest.Genesis) 765 766 require.NoError(te.issue( 767 context.Background(), 768 te.Ctx.NodeID, 769 blk, 770 false, 771 te.metrics.issued.WithLabelValues(unknownSource), 772 )) 773 } 774 775 func TestEngineNoRepollQuery(t *testing.T) { 776 require := require.New(t) 777 778 engCfg := DefaultConfig(t) 779 780 sender := &common.SenderTest{T: t} 781 engCfg.Sender = sender 782 sender.Default(true) 783 784 vm := &block.TestVM{} 785 vm.T = t 786 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 787 return snowmantest.GenesisID, nil 788 } 789 790 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 791 if blkID == snowmantest.GenesisID { 792 return snowmantest.Genesis, nil 793 } 794 return nil, errUnknownBlock 795 } 796 797 engCfg.VM = vm 798 799 te, err := New(engCfg) 800 require.NoError(err) 801 802 require.NoError(te.Start(context.Background(), 0)) 803 804 te.repoll(context.Background()) 805 } 806 807 func TestEngineAbandonQuery(t *testing.T) { 808 require := require.New(t) 809 810 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 811 812 sender.Default(true) 813 814 blkID := ids.GenerateTestID() 815 816 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 817 require.Equal(blkID, id) 818 return nil, errUnknownBlock 819 } 820 821 reqID := new(uint32) 822 sender.SendGetF = func(_ context.Context, _ ids.NodeID, requestID uint32, _ ids.ID) { 823 *reqID = requestID 824 } 825 826 sender.CantSendChits = false 827 828 require.NoError(te.PullQuery(context.Background(), vdr, 0, blkID, 0)) 829 830 require.Equal(1, te.blkReqs.Len()) 831 832 require.NoError(te.GetFailed(context.Background(), vdr, *reqID)) 833 834 require.Zero(te.blkReqs.Len()) 835 } 836 837 func TestEngineAbandonChit(t *testing.T) { 838 require := require.New(t) 839 840 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 841 842 sender.Default(true) 843 844 blk := snowmantest.BuildChild(snowmantest.Genesis) 845 846 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 847 switch blkID { 848 case snowmantest.GenesisID: 849 return snowmantest.Genesis, nil 850 case blk.ID(): 851 return nil, errUnknownBlock 852 } 853 require.FailNow(errUnknownBlock.Error()) 854 return nil, errUnknownBlock 855 } 856 857 var reqID uint32 858 sender.SendPullQueryF = func(_ context.Context, _ set.Set[ids.NodeID], requestID uint32, _ ids.ID, _ uint64) { 859 reqID = requestID 860 } 861 862 require.NoError(te.issue( 863 context.Background(), 864 te.Ctx.NodeID, 865 blk, 866 false, 867 te.metrics.issued.WithLabelValues(unknownSource), 868 )) 869 870 fakeBlkID := ids.GenerateTestID() 871 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 872 require.Equal(fakeBlkID, id) 873 return nil, errUnknownBlock 874 } 875 876 sender.SendGetF = func(_ context.Context, _ ids.NodeID, requestID uint32, _ ids.ID) { 877 reqID = requestID 878 } 879 880 // Register a voter dependency on an unknown block. 881 require.NoError(te.Chits(context.Background(), vdr, reqID, fakeBlkID, fakeBlkID, fakeBlkID)) 882 require.Equal(1, te.blocked.NumDependencies()) 883 884 sender.CantSendPullQuery = false 885 886 require.NoError(te.GetFailed(context.Background(), vdr, reqID)) 887 require.Zero(te.blocked.NumDependencies()) 888 } 889 890 func TestEngineAbandonChitWithUnexpectedPutBlock(t *testing.T) { 891 require := require.New(t) 892 893 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 894 895 sender.Default(true) 896 897 blk := snowmantest.BuildChild(snowmantest.Genesis) 898 899 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 900 switch blkID { 901 case snowmantest.GenesisID: 902 return snowmantest.Genesis, nil 903 case blk.ID(): 904 return nil, errUnknownBlock 905 } 906 require.FailNow(errUnknownBlock.Error()) 907 return nil, errUnknownBlock 908 } 909 910 var reqID uint32 911 sender.SendPushQueryF = func(_ context.Context, _ set.Set[ids.NodeID], requestID uint32, _ []byte, _ uint64) { 912 reqID = requestID 913 } 914 915 require.NoError(te.issue( 916 context.Background(), 917 te.Ctx.NodeID, 918 blk, 919 true, 920 te.metrics.issued.WithLabelValues(unknownSource), 921 )) 922 923 fakeBlkID := ids.GenerateTestID() 924 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 925 require.Equal(fakeBlkID, id) 926 return nil, errUnknownBlock 927 } 928 929 sender.SendGetF = func(_ context.Context, _ ids.NodeID, requestID uint32, _ ids.ID) { 930 reqID = requestID 931 } 932 933 // Register a voter dependency on an unknown block. 934 require.NoError(te.Chits(context.Background(), vdr, reqID, fakeBlkID, fakeBlkID, fakeBlkID)) 935 require.Equal(1, te.blocked.NumDependencies()) 936 937 sender.CantSendPullQuery = false 938 939 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 940 require.Equal(snowmantest.GenesisBytes, b) 941 return snowmantest.Genesis, nil 942 } 943 944 // Respond with an unexpected block and verify that the request is correctly 945 // cleared. 946 require.NoError(te.Put(context.Background(), vdr, reqID, snowmantest.GenesisBytes)) 947 require.Zero(te.blocked.NumDependencies()) 948 } 949 950 func TestEngineBlockingChitRequest(t *testing.T) { 951 require := require.New(t) 952 953 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 954 955 sender.Default(true) 956 957 missingBlk := snowmantest.BuildChild(snowmantest.Genesis) 958 parentBlk := snowmantest.BuildChild(missingBlk) 959 blockingBlk := snowmantest.BuildChild(parentBlk) 960 961 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 962 switch blkID { 963 case snowmantest.GenesisID: 964 return snowmantest.Genesis, nil 965 case blockingBlk.ID(): 966 return blockingBlk, nil 967 default: 968 return nil, errUnknownBlock 969 } 970 } 971 972 sender.SendGetF = func(context.Context, ids.NodeID, uint32, ids.ID) {} 973 974 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 975 require.Equal(blockingBlk.Bytes(), b) 976 return blockingBlk, nil 977 } 978 979 require.NoError(te.issue( 980 context.Background(), 981 te.Ctx.NodeID, 982 parentBlk, 983 false, 984 te.metrics.issued.WithLabelValues(unknownSource), 985 )) 986 987 sender.CantSendChits = false 988 989 require.NoError(te.PushQuery(context.Background(), vdr, 0, blockingBlk.Bytes(), 0)) 990 991 require.Equal(2, te.blocked.NumDependencies()) 992 993 sender.CantSendPullQuery = false 994 995 require.NoError(te.issue( 996 context.Background(), 997 te.Ctx.NodeID, 998 missingBlk, 999 false, 1000 te.metrics.issued.WithLabelValues(unknownSource), 1001 )) 1002 1003 require.Zero(te.blocked.NumDependencies()) 1004 } 1005 1006 func TestEngineBlockingChitResponse(t *testing.T) { 1007 require := require.New(t) 1008 1009 config := DefaultConfig(t) 1010 1011 peerID, _, sender, vm, te := setup(t, config) 1012 1013 sender.Default(true) 1014 1015 issuedBlk := snowmantest.BuildChild(snowmantest.Genesis) 1016 1017 missingBlk := snowmantest.BuildChild(snowmantest.Genesis) 1018 blockingBlk := snowmantest.BuildChild(missingBlk) 1019 1020 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1021 switch blkID { 1022 case snowmantest.GenesisID: 1023 return snowmantest.Genesis, nil 1024 case issuedBlk.ID(): 1025 return issuedBlk, nil 1026 case blockingBlk.ID(): 1027 return blockingBlk, nil 1028 default: 1029 return nil, errUnknownBlock 1030 } 1031 } 1032 vm.ParseBlockF = func(_ context.Context, blkBytes []byte) (snowman.Block, error) { 1033 switch { 1034 case bytes.Equal(snowmantest.GenesisBytes, blkBytes): 1035 return snowmantest.Genesis, nil 1036 case bytes.Equal(issuedBlk.Bytes(), blkBytes): 1037 return issuedBlk, nil 1038 case bytes.Equal(missingBlk.Bytes(), blkBytes): 1039 return missingBlk, nil 1040 case bytes.Equal(blockingBlk.Bytes(), blkBytes): 1041 return blockingBlk, nil 1042 default: 1043 return nil, errUnknownBlock 1044 } 1045 } 1046 1047 var getRequest *common.Request 1048 sender.SendGetF = func(_ context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) { 1049 require.Nil(getRequest) 1050 getRequest = &common.Request{ 1051 NodeID: nodeID, 1052 RequestID: requestID, 1053 } 1054 require.Equal(missingBlk.ID(), blkID) 1055 } 1056 1057 // Issuing [blockingBlk] will register an issuer job for [blockingBlk] 1058 // awaiting on [missingBlk]. It will also send a request for [missingBlk]. 1059 require.NoError(te.Put( 1060 context.Background(), 1061 peerID, 1062 0, 1063 blockingBlk.Bytes(), 1064 )) 1065 1066 var queryRequest *common.Request 1067 sender.SendPullQueryF = func(_ context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 1068 require.Nil(queryRequest) 1069 require.Equal(set.Of(peerID), nodeIDs) 1070 queryRequest = &common.Request{ 1071 NodeID: peerID, 1072 RequestID: requestID, 1073 } 1074 require.Equal(issuedBlk.ID(), blkID) 1075 require.Equal(uint64(1), requestedHeight) 1076 } 1077 1078 // Issuing [issuedBlk] will immediately adds [issuedBlk] to consensus, sets 1079 // it as the preferred block, and sends a query for [issuedBlk]. 1080 require.NoError(te.Put( 1081 context.Background(), 1082 peerID, 1083 0, 1084 issuedBlk.Bytes(), 1085 )) 1086 1087 sender.SendPullQueryF = nil 1088 1089 // In response to the query for [issuedBlk], the peer is responding with, 1090 // the currently pending issuance, [blockingBlk]. The direct conflict of 1091 // [issuedBlk] is [missingBlk]. This registers a voter job dependent on 1092 // [blockingBlk] and [missingBlk]. 1093 require.NoError(te.Chits( 1094 context.Background(), 1095 queryRequest.NodeID, 1096 queryRequest.RequestID, 1097 blockingBlk.ID(), 1098 missingBlk.ID(), 1099 blockingBlk.ID(), 1100 )) 1101 require.Equal(2, te.blocked.NumDependencies()) 1102 1103 queryRequest = nil 1104 sender.SendPullQueryF = func(_ context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 1105 require.Nil(queryRequest) 1106 require.Equal(set.Of(peerID), nodeIDs) 1107 queryRequest = &common.Request{ 1108 NodeID: peerID, 1109 RequestID: requestID, 1110 } 1111 require.Equal(blockingBlk.ID(), blkID) 1112 require.Equal(uint64(1), requestedHeight) 1113 } 1114 1115 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1116 switch blkID { 1117 case snowmantest.GenesisID: 1118 return snowmantest.Genesis, nil 1119 case issuedBlk.ID(): 1120 return issuedBlk, nil 1121 case missingBlk.ID(): 1122 return missingBlk, nil 1123 case blockingBlk.ID(): 1124 return blockingBlk, nil 1125 default: 1126 return nil, errUnknownBlock 1127 } 1128 } 1129 1130 // Issuing [missingBlk] will add the block into consensus. However, it will 1131 // not send a query for it as it is not the preferred block. 1132 require.NoError(te.Put( 1133 context.Background(), 1134 getRequest.NodeID, 1135 getRequest.RequestID, 1136 missingBlk.Bytes(), 1137 )) 1138 require.Equal(choices.Accepted, missingBlk.Status()) 1139 require.Equal(choices.Accepted, blockingBlk.Status()) 1140 require.Equal(choices.Rejected, issuedBlk.Status()) 1141 } 1142 1143 func TestEngineRetryFetch(t *testing.T) { 1144 require := require.New(t) 1145 1146 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 1147 1148 sender.Default(true) 1149 1150 missingBlk := snowmantest.BuildChild(snowmantest.Genesis) 1151 1152 vm.CantGetBlock = false 1153 1154 reqID := new(uint32) 1155 sender.SendGetF = func(_ context.Context, _ ids.NodeID, requestID uint32, _ ids.ID) { 1156 *reqID = requestID 1157 } 1158 sender.CantSendChits = false 1159 1160 require.NoError(te.PullQuery(context.Background(), vdr, 0, missingBlk.ID(), 0)) 1161 1162 vm.CantGetBlock = true 1163 sender.SendGetF = nil 1164 1165 require.NoError(te.GetFailed(context.Background(), vdr, *reqID)) 1166 1167 vm.CantGetBlock = false 1168 1169 called := new(bool) 1170 sender.SendGetF = func(context.Context, ids.NodeID, uint32, ids.ID) { 1171 *called = true 1172 } 1173 1174 require.NoError(te.PullQuery(context.Background(), vdr, 0, missingBlk.ID(), 0)) 1175 1176 vm.CantGetBlock = true 1177 sender.SendGetF = nil 1178 1179 require.True(*called) 1180 } 1181 1182 func TestEngineUndeclaredDependencyDeadlock(t *testing.T) { 1183 require := require.New(t) 1184 1185 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 1186 1187 sender.Default(true) 1188 1189 validBlk := snowmantest.BuildChild(snowmantest.Genesis) 1190 invalidBlk := snowmantest.BuildChild(validBlk) 1191 invalidBlk.VerifyV = errTest 1192 1193 invalidBlkID := invalidBlk.ID() 1194 1195 reqID := new(uint32) 1196 sender.SendPullQueryF = func(_ context.Context, _ set.Set[ids.NodeID], requestID uint32, _ ids.ID, _ uint64) { 1197 *reqID = requestID 1198 } 1199 1200 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1201 switch blkID { 1202 case snowmantest.GenesisID: 1203 return snowmantest.Genesis, nil 1204 case validBlk.ID(): 1205 return validBlk, nil 1206 case invalidBlk.ID(): 1207 return invalidBlk, nil 1208 default: 1209 return nil, errUnknownBlock 1210 } 1211 } 1212 require.NoError(te.issue( 1213 context.Background(), 1214 te.Ctx.NodeID, 1215 validBlk, 1216 false, 1217 te.metrics.issued.WithLabelValues(unknownSource), 1218 )) 1219 sender.SendPushQueryF = nil 1220 require.NoError(te.issue( 1221 context.Background(), 1222 te.Ctx.NodeID, 1223 invalidBlk, 1224 false, 1225 te.metrics.issued.WithLabelValues(unknownSource), 1226 )) 1227 require.NoError(te.Chits(context.Background(), vdr, *reqID, invalidBlkID, invalidBlkID, invalidBlkID)) 1228 1229 require.Equal(choices.Accepted, validBlk.Status()) 1230 } 1231 1232 func TestEngineGossip(t *testing.T) { 1233 require := require.New(t) 1234 1235 nodeID, _, sender, vm, te := setup(t, DefaultConfig(t)) 1236 1237 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 1238 return snowmantest.GenesisID, nil 1239 } 1240 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1241 require.Equal(snowmantest.GenesisID, blkID) 1242 return snowmantest.Genesis, nil 1243 } 1244 1245 var calledSendPullQuery bool 1246 sender.SendPullQueryF = func(_ context.Context, nodeIDs set.Set[ids.NodeID], _ uint32, _ ids.ID, _ uint64) { 1247 calledSendPullQuery = true 1248 require.Equal(set.Of(nodeID), nodeIDs) 1249 } 1250 1251 require.NoError(te.Gossip(context.Background())) 1252 1253 require.True(calledSendPullQuery) 1254 } 1255 1256 func TestEngineInvalidBlockIgnoredFromUnexpectedPeer(t *testing.T) { 1257 require := require.New(t) 1258 1259 vdr, vdrs, sender, vm, te := setup(t, DefaultConfig(t)) 1260 1261 secondVdr := ids.GenerateTestNodeID() 1262 require.NoError(vdrs.AddStaker(te.Ctx.SubnetID, secondVdr, nil, ids.Empty, 1)) 1263 1264 sender.Default(true) 1265 1266 missingBlk := snowmantest.BuildChild(snowmantest.Genesis) 1267 pendingBlk := snowmantest.BuildChild(missingBlk) 1268 1269 parsed := new(bool) 1270 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1271 if bytes.Equal(b, pendingBlk.Bytes()) { 1272 *parsed = true 1273 return pendingBlk, nil 1274 } 1275 return nil, errUnknownBlock 1276 } 1277 1278 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1279 switch blkID { 1280 case snowmantest.GenesisID: 1281 return snowmantest.Genesis, nil 1282 case pendingBlk.ID(): 1283 if !*parsed { 1284 return nil, errUnknownBlock 1285 } 1286 return pendingBlk, nil 1287 default: 1288 return nil, errUnknownBlock 1289 } 1290 } 1291 1292 reqID := new(uint32) 1293 sender.SendGetF = func(_ context.Context, reqVdr ids.NodeID, requestID uint32, blkID ids.ID) { 1294 *reqID = requestID 1295 require.Equal(vdr, reqVdr) 1296 require.Equal(missingBlk.ID(), blkID) 1297 } 1298 sender.CantSendChits = false 1299 1300 require.NoError(te.PushQuery(context.Background(), vdr, 0, pendingBlk.Bytes(), 0)) 1301 1302 require.NoError(te.Put(context.Background(), secondVdr, *reqID, []byte{3})) 1303 1304 *parsed = false 1305 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1306 if bytes.Equal(b, missingBlk.Bytes()) { 1307 *parsed = true 1308 return missingBlk, nil 1309 } 1310 return nil, errUnknownBlock 1311 } 1312 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1313 switch blkID { 1314 case snowmantest.GenesisID: 1315 return snowmantest.Genesis, nil 1316 case missingBlk.ID(): 1317 if !*parsed { 1318 return nil, errUnknownBlock 1319 } 1320 return missingBlk, nil 1321 default: 1322 return nil, errUnknownBlock 1323 } 1324 } 1325 sender.CantSendPullQuery = false 1326 1327 require.NoError(te.Put(context.Background(), vdr, *reqID, missingBlk.Bytes())) 1328 1329 require.Equal(pendingBlk.ID(), te.Consensus.Preference()) 1330 } 1331 1332 func TestEnginePushQueryRequestIDConflict(t *testing.T) { 1333 require := require.New(t) 1334 1335 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 1336 1337 sender.Default(true) 1338 1339 missingBlk := snowmantest.BuildChild(snowmantest.Genesis) 1340 pendingBlk := snowmantest.BuildChild(missingBlk) 1341 1342 parsed := new(bool) 1343 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1344 if bytes.Equal(b, pendingBlk.Bytes()) { 1345 *parsed = true 1346 return pendingBlk, nil 1347 } 1348 return nil, errUnknownBlock 1349 } 1350 1351 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1352 switch blkID { 1353 case snowmantest.GenesisID: 1354 return snowmantest.Genesis, nil 1355 case pendingBlk.ID(): 1356 if !*parsed { 1357 return nil, errUnknownBlock 1358 } 1359 return pendingBlk, nil 1360 default: 1361 return nil, errUnknownBlock 1362 } 1363 } 1364 1365 reqID := new(uint32) 1366 sender.SendGetF = func(_ context.Context, reqVdr ids.NodeID, requestID uint32, blkID ids.ID) { 1367 *reqID = requestID 1368 require.Equal(vdr, reqVdr) 1369 require.Equal(missingBlk.ID(), blkID) 1370 } 1371 sender.CantSendChits = false 1372 1373 require.NoError(te.PushQuery(context.Background(), vdr, 0, pendingBlk.Bytes(), 0)) 1374 1375 sender.SendGetF = nil 1376 sender.CantSendGet = false 1377 1378 require.NoError(te.PushQuery(context.Background(), vdr, *reqID, []byte{3}, 0)) 1379 1380 *parsed = false 1381 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1382 if bytes.Equal(b, missingBlk.Bytes()) { 1383 *parsed = true 1384 return missingBlk, nil 1385 } 1386 return nil, errUnknownBlock 1387 } 1388 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1389 switch blkID { 1390 case snowmantest.GenesisID: 1391 return snowmantest.Genesis, nil 1392 case missingBlk.ID(): 1393 if !*parsed { 1394 return nil, errUnknownBlock 1395 } 1396 return missingBlk, nil 1397 default: 1398 return nil, errUnknownBlock 1399 } 1400 } 1401 sender.CantSendPullQuery = false 1402 1403 require.NoError(te.Put(context.Background(), vdr, *reqID, missingBlk.Bytes())) 1404 1405 require.Equal(pendingBlk.ID(), te.Consensus.Preference()) 1406 } 1407 1408 func TestEngineAggressivePolling(t *testing.T) { 1409 require := require.New(t) 1410 1411 engCfg := DefaultConfig(t) 1412 engCfg.Params.ConcurrentRepolls = 2 1413 engCfg.Params.Beta = 2 1414 1415 vals := validators.NewManager() 1416 engCfg.Validators = vals 1417 1418 vdr := ids.GenerateTestNodeID() 1419 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr, nil, ids.Empty, 1)) 1420 1421 sender := &common.SenderTest{T: t} 1422 engCfg.Sender = sender 1423 sender.Default(true) 1424 1425 vm := &block.TestVM{} 1426 vm.T = t 1427 engCfg.VM = vm 1428 1429 vm.Default(true) 1430 vm.CantSetState = false 1431 vm.CantSetPreference = false 1432 1433 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 1434 return snowmantest.GenesisID, nil 1435 } 1436 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1437 require.Equal(snowmantest.GenesisID, blkID) 1438 return snowmantest.Genesis, nil 1439 } 1440 1441 te, err := New(engCfg) 1442 require.NoError(err) 1443 1444 require.NoError(te.Start(context.Background(), 0)) 1445 1446 vm.GetBlockF = nil 1447 vm.LastAcceptedF = nil 1448 1449 pendingBlk := snowmantest.BuildChild(snowmantest.Genesis) 1450 1451 parsed := new(bool) 1452 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1453 if bytes.Equal(b, pendingBlk.Bytes()) { 1454 *parsed = true 1455 return pendingBlk, nil 1456 } 1457 return nil, errUnknownBlock 1458 } 1459 1460 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1461 switch blkID { 1462 case snowmantest.GenesisID: 1463 return snowmantest.Genesis, nil 1464 case pendingBlk.ID(): 1465 if !*parsed { 1466 return nil, errUnknownBlock 1467 } 1468 return pendingBlk, nil 1469 default: 1470 return nil, errUnknownBlock 1471 } 1472 } 1473 1474 numPulled := new(int) 1475 sender.SendPullQueryF = func(context.Context, set.Set[ids.NodeID], uint32, ids.ID, uint64) { 1476 *numPulled++ 1477 } 1478 1479 require.NoError(te.Put(context.Background(), vdr, 0, pendingBlk.Bytes())) 1480 1481 require.Equal(2, *numPulled) 1482 } 1483 1484 func TestEngineDoubleChit(t *testing.T) { 1485 require := require.New(t) 1486 1487 engCfg := DefaultConfig(t) 1488 engCfg.Params = snowball.Parameters{ 1489 K: 2, 1490 AlphaPreference: 2, 1491 AlphaConfidence: 2, 1492 Beta: 1, 1493 ConcurrentRepolls: 1, 1494 OptimalProcessing: 1, 1495 MaxOutstandingItems: 1, 1496 MaxItemProcessingTime: 1, 1497 } 1498 1499 vals := validators.NewManager() 1500 engCfg.Validators = vals 1501 1502 vdr0 := ids.GenerateTestNodeID() 1503 vdr1 := ids.GenerateTestNodeID() 1504 1505 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr0, nil, ids.Empty, 1)) 1506 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr1, nil, ids.Empty, 1)) 1507 1508 sender := &common.SenderTest{T: t} 1509 engCfg.Sender = sender 1510 1511 sender.Default(true) 1512 1513 vm := &block.TestVM{} 1514 vm.T = t 1515 engCfg.VM = vm 1516 1517 vm.Default(true) 1518 vm.CantSetState = false 1519 vm.CantSetPreference = false 1520 1521 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 1522 return snowmantest.GenesisID, nil 1523 } 1524 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 1525 require.Equal(snowmantest.GenesisID, id) 1526 return snowmantest.Genesis, nil 1527 } 1528 1529 te, err := New(engCfg) 1530 require.NoError(err) 1531 1532 require.NoError(te.Start(context.Background(), 0)) 1533 1534 vm.LastAcceptedF = nil 1535 1536 blk := snowmantest.BuildChild(snowmantest.Genesis) 1537 1538 queried := new(bool) 1539 queryRequestID := new(uint32) 1540 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 1541 require.False((*queried)) 1542 *queried = true 1543 *queryRequestID = requestID 1544 vdrSet := set.Of(vdr0, vdr1) 1545 require.Equal(vdrSet, inVdrs) 1546 require.Equal(blk.ID(), blkID) 1547 require.Equal(uint64(1), requestedHeight) 1548 } 1549 require.NoError(te.issue( 1550 context.Background(), 1551 te.Ctx.NodeID, 1552 blk, 1553 false, 1554 te.metrics.issued.WithLabelValues(unknownSource), 1555 )) 1556 1557 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 1558 switch id { 1559 case snowmantest.GenesisID: 1560 return snowmantest.Genesis, nil 1561 case blk.ID(): 1562 return blk, nil 1563 } 1564 require.FailNow(errUnknownBlock.Error()) 1565 return nil, errUnknownBlock 1566 } 1567 1568 require.Equal(choices.Processing, blk.Status()) 1569 1570 require.NoError(te.Chits(context.Background(), vdr0, *queryRequestID, blk.ID(), blk.ID(), blk.ID())) 1571 require.Equal(choices.Processing, blk.Status()) 1572 1573 require.NoError(te.Chits(context.Background(), vdr0, *queryRequestID, blk.ID(), blk.ID(), blk.ID())) 1574 require.Equal(choices.Processing, blk.Status()) 1575 1576 require.NoError(te.Chits(context.Background(), vdr1, *queryRequestID, blk.ID(), blk.ID(), blk.ID())) 1577 require.Equal(choices.Accepted, blk.Status()) 1578 } 1579 1580 func TestEngineBuildBlockLimit(t *testing.T) { 1581 require := require.New(t) 1582 1583 engCfg := DefaultConfig(t) 1584 engCfg.Params.K = 1 1585 engCfg.Params.AlphaPreference = 1 1586 engCfg.Params.AlphaConfidence = 1 1587 engCfg.Params.OptimalProcessing = 1 1588 1589 vals := validators.NewManager() 1590 engCfg.Validators = vals 1591 1592 vdr := ids.GenerateTestNodeID() 1593 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr, nil, ids.Empty, 1)) 1594 1595 sender := &common.SenderTest{T: t} 1596 engCfg.Sender = sender 1597 sender.Default(true) 1598 1599 vm := &block.TestVM{} 1600 vm.T = t 1601 engCfg.VM = vm 1602 1603 vm.Default(true) 1604 vm.CantSetState = false 1605 vm.CantSetPreference = false 1606 1607 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 1608 return snowmantest.GenesisID, nil 1609 } 1610 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1611 require.Equal(snowmantest.GenesisID, blkID) 1612 return snowmantest.Genesis, nil 1613 } 1614 1615 te, err := New(engCfg) 1616 require.NoError(err) 1617 1618 require.NoError(te.Start(context.Background(), 0)) 1619 1620 vm.GetBlockF = nil 1621 vm.LastAcceptedF = nil 1622 1623 blks := snowmantest.BuildDescendants(snowmantest.Genesis, 2) 1624 blk0 := blks[0] 1625 1626 var ( 1627 queried bool 1628 reqID uint32 1629 ) 1630 sender.SendPushQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], rID uint32, _ []byte, _ uint64) { 1631 reqID = rID 1632 require.False(queried) 1633 queried = true 1634 vdrSet := set.Of(vdr) 1635 require.Equal(vdrSet, inVdrs) 1636 } 1637 1638 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1639 switch blkID { 1640 case snowmantest.GenesisID: 1641 return snowmantest.Genesis, nil 1642 default: 1643 return nil, errUnknownBlock 1644 } 1645 } 1646 1647 blkToReturn := 0 1648 vm.BuildBlockF = func(context.Context) (snowman.Block, error) { 1649 require.Less(blkToReturn, len(blks)) 1650 blk := blks[blkToReturn] 1651 blkToReturn++ 1652 return blk, nil 1653 } 1654 require.NoError(te.Notify(context.Background(), common.PendingTxs)) 1655 1656 require.True(queried) 1657 1658 queried = false 1659 require.NoError(te.Notify(context.Background(), common.PendingTxs)) 1660 1661 require.False(queried) 1662 1663 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1664 switch blkID { 1665 case snowmantest.GenesisID: 1666 return snowmantest.Genesis, nil 1667 case blk0.ID(): 1668 return blk0, nil 1669 default: 1670 return nil, errUnknownBlock 1671 } 1672 } 1673 1674 require.NoError(te.Chits(context.Background(), vdr, reqID, blk0.ID(), blk0.ID(), blk0.ID())) 1675 1676 require.True(queried) 1677 } 1678 1679 func TestEngineDropRejectedBlockOnReceipt(t *testing.T) { 1680 require := require.New(t) 1681 1682 nodeID, _, sender, vm, te := setup(t, DefaultConfig(t)) 1683 1684 // Ignore outbound chits 1685 sender.SendChitsF = func(context.Context, ids.NodeID, uint32, ids.ID, ids.ID, ids.ID) {} 1686 1687 acceptedBlk := snowmantest.BuildChild(snowmantest.Genesis) 1688 rejectedChain := snowmantest.BuildDescendants(snowmantest.Genesis, 2) 1689 vm.ParseBlockF = MakeParseBlockF( 1690 []*snowmantest.Block{ 1691 snowmantest.Genesis, 1692 acceptedBlk, 1693 }, 1694 rejectedChain, 1695 ) 1696 vm.GetBlockF = MakeGetBlockF([]*snowmantest.Block{ 1697 snowmantest.Genesis, 1698 acceptedBlk, 1699 }) 1700 1701 // Track outbound queries 1702 var queryRequestIDs []uint32 1703 sender.SendPullQueryF = func(_ context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, _ ids.ID, _ uint64) { 1704 require.Equal(set.Of(nodeID), nodeIDs) 1705 queryRequestIDs = append(queryRequestIDs, requestID) 1706 } 1707 1708 // Issue [acceptedBlk] to the engine. This 1709 require.NoError(te.PushQuery(context.Background(), nodeID, 0, acceptedBlk.Bytes(), acceptedBlk.Height())) 1710 require.Len(queryRequestIDs, 1) 1711 1712 // Vote for [acceptedBlk] and cause it to be accepted. 1713 require.NoError(te.Chits(context.Background(), nodeID, queryRequestIDs[0], acceptedBlk.ID(), acceptedBlk.ID(), acceptedBlk.ID())) 1714 require.Len(queryRequestIDs, 1) // Shouldn't have caused another query 1715 require.Equal(choices.Accepted, acceptedBlk.Status()) 1716 1717 // Attempt to issue rejectedChain[1] to the engine. This should be dropped 1718 // because the engine knows it has rejected it's parent rejectedChain[0]. 1719 require.NoError(te.PushQuery(context.Background(), nodeID, 0, rejectedChain[1].Bytes(), acceptedBlk.Height())) 1720 require.Len(queryRequestIDs, 1) // Shouldn't have caused another query 1721 require.Zero(te.blkReqs.Len()) 1722 } 1723 1724 // Test that the node will not gossip a block that isn't preferred. 1725 func TestEngineNonPreferredAmplification(t *testing.T) { 1726 require := require.New(t) 1727 1728 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 1729 1730 preferredBlk := snowmantest.BuildChild(snowmantest.Genesis) 1731 nonPreferredBlk := snowmantest.BuildChild(snowmantest.Genesis) 1732 1733 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1734 switch { 1735 case bytes.Equal(b, preferredBlk.Bytes()): 1736 return preferredBlk, nil 1737 case bytes.Equal(b, nonPreferredBlk.Bytes()): 1738 return nonPreferredBlk, nil 1739 default: 1740 require.FailNow(errUnknownBlock.Error()) 1741 return nil, errUnknownBlock 1742 } 1743 } 1744 1745 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1746 switch blkID { 1747 case snowmantest.GenesisID: 1748 return snowmantest.Genesis, nil 1749 default: 1750 return nil, errUnknownBlock 1751 } 1752 } 1753 1754 sender.SendPushQueryF = func(_ context.Context, _ set.Set[ids.NodeID], _ uint32, blkBytes []byte, requestedHeight uint64) { 1755 require.NotEqual(nonPreferredBlk.Bytes(), blkBytes) 1756 require.Equal(uint64(1), requestedHeight) 1757 } 1758 sender.SendPullQueryF = func(_ context.Context, _ set.Set[ids.NodeID], _ uint32, blkID ids.ID, requestedHeight uint64) { 1759 require.NotEqual(nonPreferredBlk.ID(), blkID) 1760 require.Equal(uint64(1), requestedHeight) 1761 } 1762 1763 require.NoError(te.Put(context.Background(), vdr, 0, preferredBlk.Bytes())) 1764 1765 require.NoError(te.Put(context.Background(), vdr, 0, nonPreferredBlk.Bytes())) 1766 } 1767 1768 // Test that in the following scenario, if block B fails verification, votes 1769 // will still be bubbled through to the valid block A. This is a regression test 1770 // to ensure that the consensus engine correctly handles the case that votes can 1771 // be bubbled correctly through a block that cannot pass verification until one 1772 // of its ancestors has been marked as accepted. 1773 // 1774 // G 1775 // | 1776 // A 1777 // | 1778 // B 1779 func TestEngineBubbleVotesThroughInvalidBlock(t *testing.T) { 1780 require := require.New(t) 1781 1782 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 1783 expectedVdrSet := set.Of(vdr) 1784 1785 blk1 := snowmantest.BuildChild(snowmantest.Genesis) 1786 // blk2 cannot pass verification until [blk1] has been marked as accepted. 1787 blk2 := snowmantest.BuildChild(blk1) 1788 blk2.VerifyV = errInvalid 1789 1790 // The VM should be able to parse [blk1] and [blk2] 1791 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1792 switch { 1793 case bytes.Equal(b, blk1.Bytes()): 1794 return blk1, nil 1795 case bytes.Equal(b, blk2.Bytes()): 1796 return blk2, nil 1797 default: 1798 require.FailNow(errUnknownBlock.Error()) 1799 return nil, errUnknownBlock 1800 } 1801 } 1802 1803 // for now, this VM should only be able to retrieve [Genesis] from storage 1804 // this "GetBlockF" will be updated after blocks are verified/accepted 1805 // in the following tests 1806 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1807 switch blkID { 1808 case snowmantest.GenesisID: 1809 return snowmantest.Genesis, nil 1810 default: 1811 return nil, errUnknownBlock 1812 } 1813 } 1814 1815 asked := new(bool) 1816 reqID := new(uint32) 1817 sender.SendGetF = func(_ context.Context, inVdr ids.NodeID, requestID uint32, blkID ids.ID) { 1818 *reqID = requestID 1819 require.False(*asked) 1820 require.Equal(blk1.ID(), blkID) 1821 require.Equal(vdr, inVdr) 1822 *asked = true 1823 } 1824 sender.CantSendChits = false 1825 1826 // This engine receives a Gossip message for [blk2] which was "unknown" in this engine. 1827 // The engine thus learns about its ancestor [blk1] and should send a Get request for it. 1828 // (see above for expected "Get" request) 1829 require.NoError(te.PushQuery(context.Background(), vdr, 0, blk2.Bytes(), 0)) 1830 require.True(*asked) 1831 1832 // Prepare to PullQuery [blk1] after our Get request is fulfilled. We should not PullQuery 1833 // [blk2] since it currently fails verification. 1834 queried := new(bool) 1835 queryRequestID := new(uint32) 1836 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 1837 require.False(*queried) 1838 *queried = true 1839 1840 *queryRequestID = requestID 1841 vdrSet := set.Of(vdr) 1842 require.Equal(vdrSet, inVdrs) 1843 require.Equal(blk1.ID(), blkID) 1844 require.Equal(uint64(1), requestedHeight) 1845 } 1846 // This engine now handles the response to the "Get" request. This should cause [blk1] to be issued 1847 // which will result in attempting to issue [blk2]. However, [blk2] should fail verification and be dropped. 1848 // By issuing [blk1], this node should fire a "PullQuery" request for [blk1]. 1849 // (see above for expected "PullQuery" request) 1850 require.NoError(te.Put(context.Background(), vdr, *reqID, blk1.Bytes())) 1851 require.True(*asked) 1852 require.True(*queried, "Didn't query the newly issued blk1") 1853 1854 // now [blk1] is verified, vm can return it 1855 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1856 switch blkID { 1857 case snowmantest.GenesisID: 1858 return snowmantest.Genesis, nil 1859 case blk1.ID(): 1860 return blk1, nil 1861 default: 1862 return nil, errUnknownBlock 1863 } 1864 } 1865 1866 sendReqID := new(uint32) 1867 reqVdr := new(ids.NodeID) 1868 // Update GetF to produce a more detailed error message in the case that receiving a Chits 1869 // message causes us to send another Get request. 1870 sender.SendGetF = func(_ context.Context, inVdr ids.NodeID, requestID uint32, blkID ids.ID) { 1871 require.Equal(blk2.ID(), blkID) 1872 1873 *sendReqID = requestID 1874 *reqVdr = inVdr 1875 } 1876 1877 // Now we are expecting a Chits message, and we receive it for [blk2] 1878 // instead of [blk1]. This will cause the node to again request [blk2]. 1879 require.NoError(te.Chits(context.Background(), vdr, *queryRequestID, blk2.ID(), blk1.ID(), blk2.ID())) 1880 1881 // The votes should be bubbled through [blk2] despite the fact that it is 1882 // failing verification. 1883 require.NoError(te.Put(context.Background(), *reqVdr, *sendReqID, blk2.Bytes())) 1884 1885 // The vote should be bubbled through [blk2], such that [blk1] gets marked as Accepted. 1886 require.Equal(choices.Accepted, blk1.Status()) 1887 require.Equal(choices.Processing, blk2.Status()) 1888 1889 // Now that [blk1] has been marked as Accepted, [blk2] can pass verification. 1890 blk2.VerifyV = nil 1891 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1892 switch blkID { 1893 case snowmantest.GenesisID: 1894 return snowmantest.Genesis, nil 1895 case blk1.ID(): 1896 return blk1, nil 1897 case blk2.ID(): 1898 return blk2, nil 1899 default: 1900 return nil, errUnknownBlock 1901 } 1902 } 1903 1904 *queried = false 1905 // Prepare to PullQuery [blk2] after receiving a Gossip message with [blk2]. 1906 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 1907 require.False(*queried) 1908 *queried = true 1909 *queryRequestID = requestID 1910 require.Equal(expectedVdrSet, inVdrs) 1911 require.Equal(blk2.ID(), blkID) 1912 require.Equal(uint64(2), requestedHeight) 1913 } 1914 // Expect that the Engine will send a PullQuery after receiving this Gossip message for [blk2]. 1915 require.NoError(te.PushQuery(context.Background(), vdr, 0, blk2.Bytes(), 0)) 1916 require.True(*queried) 1917 1918 // After a single vote for [blk2], it should be marked as accepted. 1919 require.NoError(te.Chits(context.Background(), vdr, *queryRequestID, blk2.ID(), blk1.ID(), blk2.ID())) 1920 require.Equal(choices.Accepted, blk2.Status()) 1921 } 1922 1923 // Test that in the following scenario, if block B fails verification, votes 1924 // will still be bubbled through from block C to the valid block A. This is a 1925 // regression test to ensure that the consensus engine correctly handles the 1926 // case that votes can be bubbled correctly through a chain that cannot pass 1927 // verification until one of its ancestors has been marked as accepted. 1928 // 1929 // G 1930 // | 1931 // A 1932 // | 1933 // B 1934 // | 1935 // C 1936 func TestEngineBubbleVotesThroughInvalidChain(t *testing.T) { 1937 require := require.New(t) 1938 1939 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 1940 expectedVdrSet := set.Of(vdr) 1941 1942 blk1 := snowmantest.BuildChild(snowmantest.Genesis) 1943 // blk2 cannot pass verification until [blk1] has been marked as accepted. 1944 blk2 := snowmantest.BuildChild(blk1) 1945 blk2.VerifyV = errInvalid 1946 blk3 := snowmantest.BuildChild(blk2) 1947 1948 // The VM should be able to parse [blk1], [blk2], and [blk3] 1949 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 1950 switch { 1951 case bytes.Equal(b, blk1.Bytes()): 1952 return blk1, nil 1953 case bytes.Equal(b, blk2.Bytes()): 1954 return blk2, nil 1955 case bytes.Equal(b, blk3.Bytes()): 1956 return blk3, nil 1957 default: 1958 require.FailNow(errUnknownBlock.Error()) 1959 return nil, errUnknownBlock 1960 } 1961 } 1962 1963 // The VM should be able to retrieve [Genesis] and [blk1] from storage 1964 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 1965 switch blkID { 1966 case snowmantest.GenesisID: 1967 return snowmantest.Genesis, nil 1968 case blk1.ID(): 1969 return blk1, nil 1970 default: 1971 return nil, errUnknownBlock 1972 } 1973 } 1974 1975 asked := new(bool) 1976 reqID := new(uint32) 1977 sender.SendGetF = func(_ context.Context, inVdr ids.NodeID, requestID uint32, blkID ids.ID) { 1978 *reqID = requestID 1979 require.False(*asked) 1980 require.Equal(blk2.ID(), blkID) 1981 require.Equal(vdr, inVdr) 1982 *asked = true 1983 } 1984 sender.CantSendChits = false 1985 1986 // Receive Gossip message for [blk3] first and expect the sender to issue a 1987 // Get request for its ancestor: [blk2]. 1988 require.NoError(te.PushQuery(context.Background(), vdr, 0, blk3.Bytes(), 0)) 1989 require.True(*asked) 1990 1991 // Prepare to PullQuery [blk1] after our request for [blk2] is fulfilled. 1992 // We should not PullQuery [blk2] since it currently fails verification. 1993 // We should not PullQuery [blk3] because [blk2] wasn't issued. 1994 queried := new(bool) 1995 queryRequestID := new(uint32) 1996 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 1997 require.False(*queried) 1998 *queried = true 1999 *queryRequestID = requestID 2000 require.Equal(expectedVdrSet, inVdrs) 2001 require.Equal(blk1.ID(), blkID) 2002 require.Equal(uint64(1), requestedHeight) 2003 } 2004 2005 // Answer the request, this should result in [blk1] being issued as well. 2006 require.NoError(te.Put(context.Background(), vdr, *reqID, blk2.Bytes())) 2007 require.True(*queried) 2008 2009 sendReqID := new(uint32) 2010 reqVdr := new(ids.NodeID) 2011 // Update GetF to produce a more detailed error message in the case that receiving a Chits 2012 // message causes us to send another Get request. 2013 sender.SendGetF = func(_ context.Context, inVdr ids.NodeID, requestID uint32, blkID ids.ID) { 2014 switch blkID { 2015 case blk1.ID(): 2016 require.FailNow("Unexpectedly sent a Get request for blk1") 2017 case blk2.ID(): 2018 t.Logf("sending get for blk2 with %d", requestID) 2019 *sendReqID = requestID 2020 *reqVdr = inVdr 2021 return 2022 case blk3.ID(): 2023 t.Logf("sending get for blk3 with %d", requestID) 2024 *sendReqID = requestID 2025 *reqVdr = inVdr 2026 return 2027 default: 2028 require.FailNow("Unexpectedly sent a Get request for unknown block") 2029 } 2030 } 2031 2032 // Now we are expecting a Chits message and we receive it for [blk3]. 2033 // This will cause the node to again request [blk3]. 2034 require.NoError(te.Chits(context.Background(), vdr, *queryRequestID, blk3.ID(), blk1.ID(), blk3.ID())) 2035 2036 // Drop the re-request for [blk3] to cause the poll to terminate. The votes 2037 // should be bubbled through [blk3] despite the fact that it hasn't been 2038 // issued. 2039 require.NoError(te.GetFailed(context.Background(), *reqVdr, *sendReqID)) 2040 2041 // The vote should be bubbled through [blk3] and [blk2] such that [blk1] 2042 // gets marked as Accepted. 2043 require.Equal(choices.Accepted, blk1.Status()) 2044 } 2045 2046 func TestEngineBuildBlockWithCachedNonVerifiedParent(t *testing.T) { 2047 require := require.New(t) 2048 2049 vdr, _, sender, vm, te := setup(t, DefaultConfig(t)) 2050 2051 sender.Default(true) 2052 2053 grandParentBlk := snowmantest.BuildChild(snowmantest.Genesis) 2054 2055 parentBlkA := snowmantest.BuildChild(grandParentBlk) 2056 parentBlkA.VerifyV = errInvalid 2057 2058 // Note that [parentBlkB] has the same [ID()] as [parentBlkA]; 2059 // it's a different instantiation of the same block. 2060 parentBlkB := *parentBlkA 2061 parentBlkB.VerifyV = nil 2062 2063 // Child of [parentBlkA]/[parentBlkB] 2064 childBlk := snowmantest.BuildChild(parentBlkA) 2065 2066 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 2067 require.Equal(grandParentBlk.BytesV, b) 2068 return grandParentBlk, nil 2069 } 2070 2071 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 2072 switch blkID { 2073 case snowmantest.GenesisID: 2074 return snowmantest.Genesis, nil 2075 case grandParentBlk.IDV: 2076 return grandParentBlk, nil 2077 default: 2078 return nil, errUnknownBlock 2079 } 2080 } 2081 2082 queryRequestGPID := new(uint32) 2083 sender.SendPullQueryF = func(_ context.Context, _ set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 2084 *queryRequestGPID = requestID 2085 require.Equal(grandParentBlk.ID(), blkID) 2086 require.Equal(uint64(1), requestedHeight) 2087 } 2088 2089 // Give the engine the grandparent 2090 require.NoError(te.Put(context.Background(), vdr, 0, grandParentBlk.BytesV)) 2091 2092 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 2093 require.Equal(parentBlkA.BytesV, b) 2094 return parentBlkA, nil 2095 } 2096 2097 // Give the node [parentBlkA]/[parentBlkB]. 2098 // When it's parsed we get [parentBlkA] (not [parentBlkB]). 2099 // [parentBlkA] fails verification and gets put into [te.nonVerifiedCache]. 2100 require.NoError(te.Put(context.Background(), vdr, 0, parentBlkA.BytesV)) 2101 2102 vm.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) { 2103 require.Equal(parentBlkB.BytesV, b) 2104 return &parentBlkB, nil 2105 } 2106 2107 vm.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) { 2108 switch blkID { 2109 case snowmantest.GenesisID: 2110 return snowmantest.Genesis, nil 2111 case grandParentBlk.IDV: 2112 return grandParentBlk, nil 2113 case parentBlkB.IDV: 2114 return &parentBlkB, nil 2115 default: 2116 return nil, errUnknownBlock 2117 } 2118 } 2119 2120 queryRequestAID := new(uint32) 2121 sender.SendPullQueryF = func(_ context.Context, _ set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 2122 *queryRequestAID = requestID 2123 require.Equal(parentBlkA.ID(), blkID) 2124 require.Equal(uint64(1), requestedHeight) 2125 } 2126 sender.CantSendPullQuery = false 2127 2128 // Give the engine [parentBlkA]/[parentBlkB] again. 2129 // This time when we parse it we get [parentBlkB] (not [parentBlkA]). 2130 // When we fetch it using [GetBlockF] we get [parentBlkB]. 2131 // Note that [parentBlkB] doesn't fail verification and is issued into consensus. 2132 // This evicts [parentBlkA] from [te.nonVerifiedCache]. 2133 require.NoError(te.Put(context.Background(), vdr, 0, parentBlkA.BytesV)) 2134 2135 // Give 2 chits for [parentBlkA]/[parentBlkB] 2136 require.NoError(te.Chits(context.Background(), vdr, *queryRequestAID, parentBlkB.IDV, grandParentBlk.IDV, parentBlkB.IDV)) 2137 require.NoError(te.Chits(context.Background(), vdr, *queryRequestGPID, parentBlkB.IDV, grandParentBlk.IDV, parentBlkB.IDV)) 2138 2139 // Assert that the blocks' statuses are correct. 2140 // The evicted [parentBlkA] shouldn't be changed. 2141 require.Equal(choices.Processing, parentBlkA.Status()) 2142 require.Equal(choices.Accepted, parentBlkB.Status()) 2143 2144 vm.BuildBlockF = func(context.Context) (snowman.Block, error) { 2145 return childBlk, nil 2146 } 2147 2148 sentQuery := new(bool) 2149 sender.SendPushQueryF = func(context.Context, set.Set[ids.NodeID], uint32, []byte, uint64) { 2150 *sentQuery = true 2151 } 2152 2153 // Should issue a new block and send a query for it. 2154 require.NoError(te.Notify(context.Background(), common.PendingTxs)) 2155 require.True(*sentQuery) 2156 } 2157 2158 func TestEngineApplyAcceptedFrontierInQueryFailed(t *testing.T) { 2159 require := require.New(t) 2160 2161 engCfg := DefaultConfig(t) 2162 engCfg.Params = snowball.Parameters{ 2163 K: 1, 2164 AlphaPreference: 1, 2165 AlphaConfidence: 1, 2166 Beta: 2, 2167 ConcurrentRepolls: 1, 2168 OptimalProcessing: 1, 2169 MaxOutstandingItems: 1, 2170 MaxItemProcessingTime: 1, 2171 } 2172 2173 vals := validators.NewManager() 2174 engCfg.Validators = vals 2175 2176 vdr := ids.GenerateTestNodeID() 2177 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr, nil, ids.Empty, 1)) 2178 2179 sender := &common.SenderTest{T: t} 2180 engCfg.Sender = sender 2181 2182 sender.Default(true) 2183 2184 vm := &block.TestVM{} 2185 vm.T = t 2186 engCfg.VM = vm 2187 2188 vm.Default(true) 2189 vm.CantSetState = false 2190 vm.CantSetPreference = false 2191 2192 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 2193 return snowmantest.GenesisID, nil 2194 } 2195 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 2196 require.Equal(snowmantest.GenesisID, id) 2197 return snowmantest.Genesis, nil 2198 } 2199 2200 te, err := New(engCfg) 2201 require.NoError(err) 2202 require.NoError(te.Start(context.Background(), 0)) 2203 2204 vm.LastAcceptedF = nil 2205 2206 blk := snowmantest.BuildChild(snowmantest.Genesis) 2207 2208 queryRequestID := new(uint32) 2209 sender.SendPushQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkBytes []byte, requestedHeight uint64) { 2210 *queryRequestID = requestID 2211 require.Contains(inVdrs, vdr) 2212 require.Equal(blk.Bytes(), blkBytes) 2213 require.Equal(uint64(1), requestedHeight) 2214 } 2215 2216 require.NoError(te.issue( 2217 context.Background(), 2218 te.Ctx.NodeID, 2219 blk, 2220 true, 2221 te.metrics.issued.WithLabelValues(unknownSource), 2222 )) 2223 2224 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 2225 switch id { 2226 case snowmantest.GenesisID: 2227 return snowmantest.Genesis, nil 2228 case blk.ID(): 2229 return blk, nil 2230 } 2231 require.FailNow(errUnknownBlock.Error()) 2232 return nil, errUnknownBlock 2233 } 2234 2235 require.Equal(choices.Processing, blk.Status()) 2236 2237 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 2238 *queryRequestID = requestID 2239 require.Contains(inVdrs, vdr) 2240 require.Equal(blk.ID(), blkID) 2241 require.Equal(uint64(1), requestedHeight) 2242 } 2243 2244 require.NoError(te.Chits(context.Background(), vdr, *queryRequestID, blk.ID(), blk.ID(), blk.ID())) 2245 2246 require.Equal(choices.Processing, blk.Status()) 2247 2248 require.NoError(te.QueryFailed(context.Background(), vdr, *queryRequestID)) 2249 2250 require.Equal(choices.Accepted, blk.Status()) 2251 } 2252 2253 func TestEngineRepollsMisconfiguredSubnet(t *testing.T) { 2254 require := require.New(t) 2255 2256 engCfg := DefaultConfig(t) 2257 engCfg.Params = snowball.Parameters{ 2258 K: 1, 2259 AlphaPreference: 1, 2260 AlphaConfidence: 1, 2261 Beta: 1, 2262 ConcurrentRepolls: 1, 2263 OptimalProcessing: 1, 2264 MaxOutstandingItems: 1, 2265 MaxItemProcessingTime: 1, 2266 } 2267 2268 // Setup the engine with no validators. When a block is issued, the poll 2269 // should fail to be created because there is nobody to poll. 2270 vals := validators.NewManager() 2271 engCfg.Validators = vals 2272 2273 sender := &common.SenderTest{T: t} 2274 engCfg.Sender = sender 2275 2276 sender.Default(true) 2277 2278 vm := &block.TestVM{} 2279 vm.T = t 2280 engCfg.VM = vm 2281 2282 vm.Default(true) 2283 vm.CantSetState = false 2284 vm.CantSetPreference = false 2285 2286 vm.LastAcceptedF = func(context.Context) (ids.ID, error) { 2287 return snowmantest.GenesisID, nil 2288 } 2289 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 2290 require.Equal(snowmantest.GenesisID, id) 2291 return snowmantest.Genesis, nil 2292 } 2293 2294 te, err := New(engCfg) 2295 require.NoError(err) 2296 require.NoError(te.Start(context.Background(), 0)) 2297 2298 vm.LastAcceptedF = nil 2299 2300 blk := snowmantest.BuildChild(snowmantest.Genesis) 2301 2302 // Issue the block. This shouldn't call the sender, because creating the 2303 // poll should fail. 2304 require.NoError(te.issue( 2305 context.Background(), 2306 te.Ctx.NodeID, 2307 blk, 2308 true, 2309 te.metrics.issued.WithLabelValues(unknownSource), 2310 )) 2311 2312 // The block should have successfully been added into consensus. 2313 require.Equal(1, te.Consensus.NumProcessing()) 2314 2315 // Fix the subnet configuration by adding a validator. 2316 vdr := ids.GenerateTestNodeID() 2317 require.NoError(vals.AddStaker(engCfg.Ctx.SubnetID, vdr, nil, ids.Empty, 1)) 2318 2319 var ( 2320 queryRequestID uint32 2321 queried bool 2322 ) 2323 sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { 2324 queryRequestID = requestID 2325 require.Contains(inVdrs, vdr) 2326 require.Equal(blk.ID(), blkID) 2327 require.Equal(uint64(1), requestedHeight) 2328 queried = true 2329 } 2330 2331 // Because there is now a validator that can be queried, gossip should 2332 // trigger creation of the poll. 2333 require.NoError(te.Gossip(context.Background())) 2334 require.True(queried) 2335 2336 vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { 2337 switch id { 2338 case snowmantest.GenesisID: 2339 return snowmantest.Genesis, nil 2340 case blk.ID(): 2341 return blk, nil 2342 } 2343 require.FailNow(errUnknownBlock.Error()) 2344 return nil, errUnknownBlock 2345 } 2346 2347 // Voting for the block that was issued during the period when the validator 2348 // set was misconfigured should result in it being accepted successfully. 2349 require.NoError(te.Chits(context.Background(), vdr, queryRequestID, blk.ID(), blk.ID(), blk.ID())) 2350 require.Equal(choices.Accepted, blk.Status()) 2351 } 2352 2353 // Full blockchain structure: 2354 // 2355 // G 2356 // / \ 2357 // 0 3 2358 // | | 2359 // 1 4 2360 // | 2361 // 2 2362 // 2363 // K = 3, Alpha = 2, Beta = 1, ConcurrentRepolls = 1 2364 // 2365 // Initial configuration: 2366 // 2367 // G 2368 // | 2369 // 0 2370 // | 2371 // 1 2372 // | 2373 // 2 2374 // 2375 // The following is a regression test for a bug where the engine would stall. 2376 // 2377 // 1. Poll = 0: Handle a chit for block 1. 2378 // 2. Poll = 0: Handle a chit for block 2. 2379 // 3. Poll = 0: Handle a chit for block 3. This will issue a Get request for block 3. This will block on the issuance of block 3. 2380 // 4. Attempt to issue block 4. This will block on the issuance of block 3. 2381 // 5. Poll = 1: Handle a chit for block 1. 2382 // 6. Poll = 1: Handle a chit for block 2. 2383 // 7. Poll = 1: Handle a chit for block 4. This will block on the issuance of block 4. 2384 // 8. Issue block 3. 2385 // Poll = 0 terminates. This will accept blocks 0 and 1. This will also reject block 3. 2386 // Block = 4 will attempt to be delivered, but because it is effectively rejected due to the acceptance of block 1, it will be dropped. 2387 // Poll = 1 should terminate and block 2 should be repolled. 2388 func TestEngineVoteStallRegression(t *testing.T) { 2389 require := require.New(t) 2390 2391 config := DefaultConfig(t) 2392 config.Params = snowball.Parameters{ 2393 K: 3, 2394 AlphaPreference: 2, 2395 AlphaConfidence: 2, 2396 Beta: 1, 2397 ConcurrentRepolls: 1, 2398 OptimalProcessing: 1, 2399 MaxOutstandingItems: 1, 2400 MaxItemProcessingTime: 1, 2401 } 2402 2403 nodeID0 := ids.GenerateTestNodeID() 2404 nodeID1 := ids.GenerateTestNodeID() 2405 nodeID2 := ids.GenerateTestNodeID() 2406 nodeIDs := []ids.NodeID{nodeID0, nodeID1, nodeID2} 2407 2408 require.NoError(config.Validators.AddStaker(config.Ctx.SubnetID, nodeID0, nil, ids.Empty, 1)) 2409 require.NoError(config.Validators.AddStaker(config.Ctx.SubnetID, nodeID1, nil, ids.Empty, 1)) 2410 require.NoError(config.Validators.AddStaker(config.Ctx.SubnetID, nodeID2, nil, ids.Empty, 1)) 2411 2412 sender := &common.SenderTest{ 2413 T: t, 2414 SendChitsF: func(context.Context, ids.NodeID, uint32, ids.ID, ids.ID, ids.ID) {}, 2415 } 2416 sender.Default(true) 2417 config.Sender = sender 2418 2419 acceptedChain := snowmantest.BuildDescendants(snowmantest.Genesis, 3) 2420 rejectedChain := snowmantest.BuildDescendants(snowmantest.Genesis, 2) 2421 2422 vm := &block.TestVM{ 2423 TestVM: common.TestVM{ 2424 T: t, 2425 InitializeF: func( 2426 context.Context, 2427 *snow.Context, 2428 database.Database, 2429 []byte, 2430 []byte, 2431 []byte, 2432 chan<- common.Message, 2433 []*common.Fx, 2434 common.AppSender, 2435 ) error { 2436 return nil 2437 }, 2438 SetStateF: func(context.Context, snow.State) error { 2439 return nil 2440 }, 2441 }, 2442 ParseBlockF: MakeParseBlockF( 2443 []*snowmantest.Block{snowmantest.Genesis}, 2444 acceptedChain, 2445 rejectedChain, 2446 ), 2447 GetBlockF: MakeGetBlockF( 2448 []*snowmantest.Block{snowmantest.Genesis}, 2449 acceptedChain, 2450 ), 2451 SetPreferenceF: func(context.Context, ids.ID) error { 2452 return nil 2453 }, 2454 LastAcceptedF: MakeLastAcceptedBlockF( 2455 snowmantest.Genesis, 2456 acceptedChain, 2457 ), 2458 } 2459 vm.Default(true) 2460 config.VM = vm 2461 2462 engine, err := New(config) 2463 require.NoError(err) 2464 require.NoError(engine.Start(context.Background(), 0)) 2465 2466 var pollRequestIDs []uint32 2467 sender.SendPullQueryF = func(_ context.Context, polledNodeIDs set.Set[ids.NodeID], requestID uint32, _ ids.ID, _ uint64) { 2468 require.Equal(set.Of(nodeIDs...), polledNodeIDs) 2469 pollRequestIDs = append(pollRequestIDs, requestID) 2470 } 2471 2472 // Issue block 0. 2473 require.NoError(engine.PushQuery( 2474 context.Background(), 2475 nodeID0, 2476 0, 2477 acceptedChain[0].Bytes(), 2478 0, 2479 )) 2480 require.Len(pollRequestIDs, 1) 2481 2482 // Issue block 1. 2483 require.NoError(engine.PushQuery( 2484 context.Background(), 2485 nodeID0, 2486 0, 2487 acceptedChain[1].Bytes(), 2488 0, 2489 )) 2490 require.Len(pollRequestIDs, 2) 2491 2492 // Issue block 2. 2493 require.NoError(engine.PushQuery( 2494 context.Background(), 2495 nodeID0, 2496 0, 2497 acceptedChain[2].Bytes(), 2498 0, 2499 )) 2500 require.Len(pollRequestIDs, 3) 2501 2502 // Apply votes in poll 0 to the blocks that will be accepted. 2503 require.NoError(engine.Chits( 2504 context.Background(), 2505 nodeID0, 2506 pollRequestIDs[0], 2507 acceptedChain[1].ID(), 2508 acceptedChain[1].ID(), 2509 acceptedChain[1].ID(), 2510 )) 2511 require.NoError(engine.Chits( 2512 context.Background(), 2513 nodeID1, 2514 pollRequestIDs[0], 2515 acceptedChain[2].ID(), 2516 acceptedChain[2].ID(), 2517 acceptedChain[2].ID(), 2518 )) 2519 2520 // Attempt to apply votes in poll 0 for block 3. This will send a Get 2521 // request for block 3 and register the chits as a dependency on block 3. 2522 var getBlock3Request *common.Request 2523 sender.SendGetF = func(_ context.Context, nodeID ids.NodeID, requestID uint32, blkID ids.ID) { 2524 require.Nil(getBlock3Request) 2525 require.Equal(nodeID2, nodeID) 2526 getBlock3Request = &common.Request{ 2527 NodeID: nodeID, 2528 RequestID: requestID, 2529 } 2530 require.Equal(rejectedChain[0].ID(), blkID) 2531 } 2532 2533 require.NoError(engine.Chits( 2534 context.Background(), 2535 nodeID2, 2536 pollRequestIDs[0], 2537 rejectedChain[0].ID(), 2538 rejectedChain[0].ID(), 2539 rejectedChain[0].ID(), 2540 )) 2541 require.NotNil(getBlock3Request) 2542 2543 // Attempt to issue block 4. This will register a dependency on block 3 for 2544 // the issuance of block 4. 2545 require.NoError(engine.PushQuery( 2546 context.Background(), 2547 nodeID0, 2548 0, 2549 rejectedChain[1].Bytes(), 2550 0, 2551 )) 2552 require.Len(pollRequestIDs, 3) 2553 2554 // Apply votes in poll 1 that will cause blocks 3 and 4 to be rejected once 2555 // poll 0 finishes. 2556 require.NoError(engine.Chits( 2557 context.Background(), 2558 nodeID0, 2559 pollRequestIDs[1], 2560 acceptedChain[1].ID(), 2561 acceptedChain[1].ID(), 2562 acceptedChain[1].ID(), 2563 )) 2564 require.NoError(engine.Chits( 2565 context.Background(), 2566 nodeID1, 2567 pollRequestIDs[1], 2568 acceptedChain[2].ID(), 2569 acceptedChain[2].ID(), 2570 acceptedChain[2].ID(), 2571 )) 2572 require.NoError(engine.Chits( 2573 context.Background(), 2574 nodeID2, 2575 pollRequestIDs[1], 2576 rejectedChain[1].ID(), 2577 rejectedChain[1].ID(), 2578 rejectedChain[1].ID(), 2579 )) 2580 2581 // Provide block 3. 2582 // This will cause poll 0 to terminate and accept blocks 0 and 1. 2583 // Then the engine will attempt to deliver block 4, but because block 1 is 2584 // accepted, block 4 will be dropped. 2585 // Then poll 1 should terminate because block 4 was dropped. 2586 vm.GetBlockF = MakeGetBlockF( 2587 []*snowmantest.Block{snowmantest.Genesis}, 2588 acceptedChain, 2589 rejectedChain, 2590 ) 2591 2592 require.NoError(engine.Put( 2593 context.Background(), 2594 getBlock3Request.NodeID, 2595 getBlock3Request.RequestID, 2596 rejectedChain[0].Bytes(), 2597 )) 2598 require.Equal(choices.Accepted, acceptedChain[0].Status()) 2599 require.Equal(choices.Accepted, acceptedChain[1].Status()) 2600 require.Equal(choices.Processing, acceptedChain[2].Status()) 2601 require.Equal(choices.Rejected, rejectedChain[0].Status()) 2602 2603 // Then engine should issue as many queries as needed to confirm block 2. 2604 for i := 2; i < len(pollRequestIDs); i++ { 2605 for _, nodeID := range nodeIDs { 2606 require.NoError(engine.Chits( 2607 context.Background(), 2608 nodeID, 2609 pollRequestIDs[i], 2610 acceptedChain[2].ID(), 2611 acceptedChain[2].ID(), 2612 acceptedChain[2].ID(), 2613 )) 2614 } 2615 } 2616 require.Equal(choices.Accepted, acceptedChain[0].Status()) 2617 require.Equal(choices.Accepted, acceptedChain[1].Status()) 2618 require.Equal(choices.Accepted, acceptedChain[2].Status()) 2619 require.Equal(choices.Rejected, rejectedChain[0].Status()) 2620 } 2621 2622 // When a voter is registered with multiple dependencies, the engine must not 2623 // execute the voter until all of the dependencies have been resolved; even if 2624 // one of the dependencies has been abandoned. 2625 func TestEngineEarlyTerminateVoterRegression(t *testing.T) { 2626 require := require.New(t) 2627 2628 config := DefaultConfig(t) 2629 nodeID := ids.GenerateTestNodeID() 2630 require.NoError(config.Validators.AddStaker(config.Ctx.SubnetID, nodeID, nil, ids.Empty, 1)) 2631 2632 sender := &common.SenderTest{ 2633 T: t, 2634 SendChitsF: func(context.Context, ids.NodeID, uint32, ids.ID, ids.ID, ids.ID) {}, 2635 } 2636 sender.Default(true) 2637 config.Sender = sender 2638 2639 chain := snowmantest.BuildDescendants(snowmantest.Genesis, 3) 2640 vm := &block.TestVM{ 2641 TestVM: common.TestVM{ 2642 T: t, 2643 InitializeF: func( 2644 context.Context, 2645 *snow.Context, 2646 database.Database, 2647 []byte, 2648 []byte, 2649 []byte, 2650 chan<- common.Message, 2651 []*common.Fx, 2652 common.AppSender, 2653 ) error { 2654 return nil 2655 }, 2656 SetStateF: func(context.Context, snow.State) error { 2657 return nil 2658 }, 2659 }, 2660 ParseBlockF: MakeParseBlockF( 2661 []*snowmantest.Block{snowmantest.Genesis}, 2662 chain, 2663 ), 2664 GetBlockF: MakeGetBlockF( 2665 []*snowmantest.Block{snowmantest.Genesis}, 2666 ), 2667 SetPreferenceF: func(context.Context, ids.ID) error { 2668 return nil 2669 }, 2670 LastAcceptedF: MakeLastAcceptedBlockF( 2671 snowmantest.Genesis, 2672 chain, 2673 ), 2674 } 2675 vm.Default(true) 2676 config.VM = vm 2677 2678 engine, err := New(config) 2679 require.NoError(err) 2680 require.NoError(engine.Start(context.Background(), 0)) 2681 2682 var pollRequestIDs []uint32 2683 sender.SendPullQueryF = func(_ context.Context, polledNodeIDs set.Set[ids.NodeID], requestID uint32, _ ids.ID, _ uint64) { 2684 require.Equal(set.Of(nodeID), polledNodeIDs) 2685 pollRequestIDs = append(pollRequestIDs, requestID) 2686 } 2687 2688 getRequestIDs := make(map[ids.ID]uint32) 2689 sender.SendGetF = func(_ context.Context, requestedNodeID ids.NodeID, requestID uint32, blkID ids.ID) { 2690 require.Equal(nodeID, requestedNodeID) 2691 getRequestIDs[blkID] = requestID 2692 } 2693 2694 // Issue block 0 to trigger poll 0. 2695 require.NoError(engine.PushQuery( 2696 context.Background(), 2697 nodeID, 2698 0, 2699 chain[0].Bytes(), 2700 0, 2701 )) 2702 require.Len(pollRequestIDs, 1) 2703 require.Empty(getRequestIDs) 2704 2705 // Update GetBlock to return, the newly issued, block 0. This is needed to 2706 // enable the issuance of block 1. 2707 vm.GetBlockF = MakeGetBlockF( 2708 []*snowmantest.Block{snowmantest.Genesis}, 2709 chain[:1], 2710 ) 2711 2712 // Vote for block 2 or block 1 in poll 0. This should trigger Get requests 2713 // for both block 2 and block 1. 2714 require.NoError(engine.Chits( 2715 context.Background(), 2716 nodeID, 2717 pollRequestIDs[0], 2718 chain[2].ID(), 2719 chain[1].ID(), 2720 snowmantest.GenesisID, 2721 )) 2722 require.Len(pollRequestIDs, 1) 2723 require.Contains(getRequestIDs, chain[1].ID()) 2724 require.Contains(getRequestIDs, chain[2].ID()) 2725 2726 // Mark the request for block 2 as failed. This should not cause the poll to 2727 // be applied as there is still an outstanding request for block 1. 2728 require.NoError(engine.GetFailed( 2729 context.Background(), 2730 nodeID, 2731 getRequestIDs[chain[2].ID()], 2732 )) 2733 require.Len(pollRequestIDs, 1) 2734 2735 // Issue block 1. This should cause the poll to be applied to both block 0 2736 // and block 1. 2737 require.NoError(engine.Put( 2738 context.Background(), 2739 nodeID, 2740 getRequestIDs[chain[1].ID()], 2741 chain[1].Bytes(), 2742 )) 2743 // Because Put added a new preferred block to the chain, a new poll will be 2744 // created. 2745 require.Len(pollRequestIDs, 2) 2746 require.Equal(choices.Accepted, chain[0].Status()) 2747 require.Equal(choices.Accepted, chain[1].Status()) 2748 // Block 2 still hasn't been issued, so it's status should remain 2749 // Processing. 2750 require.Equal(choices.Processing, chain[2].Status()) 2751 } 2752 2753 // Voting for an unissued cached block that fails verification should not 2754 // register any dependencies. 2755 // 2756 // Full blockchain structure: 2757 // 2758 // Genesis 2759 // / \ 2760 // 0 2 2761 // | | 2762 // 1 3 2763 // 2764 // We first issue block 2, and then block 3 fails verification. This causes 2765 // block 3 to be added to the invalid blocks cache. 2766 // 2767 // We then issue block 0, issue block 1, and accept block 0. 2768 // 2769 // If we then vote for block 3, the vote should be dropped and trigger a repoll 2770 // which could then be used to accept block 1. 2771 func TestEngineRegistersInvalidVoterDependencyRegression(t *testing.T) { 2772 require := require.New(t) 2773 2774 config := DefaultConfig(t) 2775 nodeID := ids.GenerateTestNodeID() 2776 require.NoError(config.Validators.AddStaker(config.Ctx.SubnetID, nodeID, nil, ids.Empty, 1)) 2777 2778 sender := &common.SenderTest{ 2779 T: t, 2780 SendChitsF: func(context.Context, ids.NodeID, uint32, ids.ID, ids.ID, ids.ID) {}, 2781 } 2782 sender.Default(true) 2783 config.Sender = sender 2784 2785 var ( 2786 acceptedChain = snowmantest.BuildDescendants(snowmantest.Genesis, 2) 2787 rejectedChain = snowmantest.BuildDescendants(snowmantest.Genesis, 2) 2788 ) 2789 rejectedChain[1].VerifyV = errInvalid 2790 2791 vm := &block.TestVM{ 2792 TestVM: common.TestVM{ 2793 T: t, 2794 InitializeF: func( 2795 context.Context, 2796 *snow.Context, 2797 database.Database, 2798 []byte, 2799 []byte, 2800 []byte, 2801 chan<- common.Message, 2802 []*common.Fx, 2803 common.AppSender, 2804 ) error { 2805 return nil 2806 }, 2807 SetStateF: func(context.Context, snow.State) error { 2808 return nil 2809 }, 2810 }, 2811 ParseBlockF: MakeParseBlockF( 2812 []*snowmantest.Block{snowmantest.Genesis}, 2813 acceptedChain, 2814 rejectedChain, 2815 ), 2816 GetBlockF: MakeGetBlockF( 2817 []*snowmantest.Block{snowmantest.Genesis}, 2818 ), 2819 SetPreferenceF: func(context.Context, ids.ID) error { 2820 return nil 2821 }, 2822 LastAcceptedF: MakeLastAcceptedBlockF( 2823 snowmantest.Genesis, 2824 acceptedChain, 2825 rejectedChain, 2826 ), 2827 } 2828 vm.Default(true) 2829 config.VM = vm 2830 2831 engine, err := New(config) 2832 require.NoError(err) 2833 require.NoError(engine.Start(context.Background(), 0)) 2834 2835 var pollRequestIDs []uint32 2836 sender.SendPullQueryF = func(_ context.Context, polledNodeIDs set.Set[ids.NodeID], requestID uint32, _ ids.ID, _ uint64) { 2837 require.Equal(set.Of(nodeID), polledNodeIDs) 2838 pollRequestIDs = append(pollRequestIDs, requestID) 2839 } 2840 2841 // Issue rejectedChain[0] to consensus. 2842 require.NoError(engine.PushQuery( 2843 context.Background(), 2844 nodeID, 2845 0, 2846 rejectedChain[0].Bytes(), 2847 0, 2848 )) 2849 require.Len(pollRequestIDs, 1) 2850 2851 // In order to attempt to issue rejectedChain[1], the engine expects the VM 2852 // to be willing to provide rejectedChain[0]. 2853 vm.GetBlockF = MakeGetBlockF( 2854 []*snowmantest.Block{snowmantest.Genesis}, 2855 rejectedChain[:1], 2856 ) 2857 2858 // Attempt to issue rejectedChain[1] which should add it to the invalid 2859 // block cache. 2860 require.NoError(engine.PushQuery( 2861 context.Background(), 2862 nodeID, 2863 0, 2864 rejectedChain[1].Bytes(), 2865 0, 2866 )) 2867 require.Len(pollRequestIDs, 1) 2868 2869 _, wasCached := engine.nonVerifiedCache.Get(rejectedChain[1].ID()) 2870 require.True(wasCached) 2871 2872 // Issue acceptedChain[0] to consensus. 2873 require.NoError(engine.PushQuery( 2874 context.Background(), 2875 nodeID, 2876 0, 2877 acceptedChain[0].Bytes(), 2878 0, 2879 )) 2880 // Because acceptedChain[0] isn't initially preferred, a new poll won't be 2881 // created. 2882 require.Len(pollRequestIDs, 1) 2883 2884 // In order to vote for acceptedChain[0], the engine expects the VM to be 2885 // willing to provide it. 2886 vm.GetBlockF = MakeGetBlockF( 2887 []*snowmantest.Block{snowmantest.Genesis}, 2888 acceptedChain[:1], 2889 rejectedChain[:1], 2890 ) 2891 2892 // Accept acceptedChain[0] and reject rejectedChain[0]. 2893 require.NoError(engine.Chits( 2894 context.Background(), 2895 nodeID, 2896 pollRequestIDs[0], 2897 acceptedChain[0].ID(), 2898 acceptedChain[0].ID(), 2899 snowmantest.GenesisID, 2900 )) 2901 // There are no processing blocks, so no new poll should be created. 2902 require.Len(pollRequestIDs, 1) 2903 require.Equal(choices.Accepted, acceptedChain[0].Status()) 2904 require.Equal(choices.Rejected, rejectedChain[0].Status()) 2905 2906 // Issue acceptedChain[1] to consensus. 2907 require.NoError(engine.PushQuery( 2908 context.Background(), 2909 nodeID, 2910 0, 2911 acceptedChain[1].Bytes(), 2912 0, 2913 )) 2914 require.Len(pollRequestIDs, 2) 2915 2916 // Vote for the transitively rejected rejectedChain[1]. This should cause a 2917 // repoll. 2918 require.NoError(engine.Chits( 2919 context.Background(), 2920 nodeID, 2921 pollRequestIDs[1], 2922 rejectedChain[1].ID(), 2923 rejectedChain[1].ID(), 2924 snowmantest.GenesisID, 2925 )) 2926 require.Len(pollRequestIDs, 3) 2927 2928 // In order to vote for acceptedChain[1], the engine expects the VM to be 2929 // willing to provide it. 2930 vm.GetBlockF = MakeGetBlockF( 2931 []*snowmantest.Block{snowmantest.Genesis}, 2932 acceptedChain, 2933 rejectedChain[:1], 2934 ) 2935 2936 // Accept acceptedChain[1]. 2937 require.NoError(engine.Chits( 2938 context.Background(), 2939 nodeID, 2940 pollRequestIDs[2], 2941 acceptedChain[1].ID(), 2942 acceptedChain[1].ID(), 2943 snowmantest.GenesisID, 2944 )) 2945 require.Len(pollRequestIDs, 3) 2946 require.Equal(choices.Accepted, acceptedChain[1].Status()) 2947 } 2948 2949 func TestGetProcessingAncestor(t *testing.T) { 2950 var ( 2951 ctx = snowtest.ConsensusContext( 2952 snowtest.Context(t, snowtest.PChainID), 2953 ) 2954 issuedBlock = snowmantest.BuildChild(snowmantest.Genesis) 2955 unissuedBlock = snowmantest.BuildChild(issuedBlock) 2956 ) 2957 2958 metrics, err := newMetrics(prometheus.NewRegistry()) 2959 require.NoError(t, err) 2960 2961 c := &snowman.Topological{} 2962 require.NoError(t, c.Initialize( 2963 ctx, 2964 snowball.DefaultParameters, 2965 snowmantest.GenesisID, 2966 0, 2967 time.Now(), 2968 )) 2969 2970 require.NoError(t, c.Add(issuedBlock)) 2971 2972 nonVerifiedAncestors := ancestor.NewTree() 2973 nonVerifiedAncestors.Add(unissuedBlock.ID(), unissuedBlock.Parent()) 2974 2975 tests := []struct { 2976 name string 2977 engine *Transitive 2978 initialVote ids.ID 2979 expectedAncestor ids.ID 2980 expectedFound bool 2981 }{ 2982 { 2983 name: "drop accepted blockID", 2984 engine: &Transitive{ 2985 Config: Config{ 2986 Ctx: ctx, 2987 VM: &block.TestVM{ 2988 TestVM: common.TestVM{ 2989 T: t, 2990 }, 2991 GetBlockF: MakeGetBlockF( 2992 []*snowmantest.Block{snowmantest.Genesis}, 2993 ), 2994 }, 2995 Consensus: c, 2996 }, 2997 metrics: metrics, 2998 nonVerifieds: ancestor.NewTree(), 2999 pending: map[ids.ID]snowman.Block{}, 3000 nonVerifiedCache: &cache.Empty[ids.ID, snowman.Block]{}, 3001 }, 3002 initialVote: snowmantest.GenesisID, 3003 expectedAncestor: ids.Empty, 3004 expectedFound: false, 3005 }, 3006 { 3007 name: "return processing blockID", 3008 engine: &Transitive{ 3009 Config: Config{ 3010 Ctx: ctx, 3011 VM: &block.TestVM{ 3012 TestVM: common.TestVM{ 3013 T: t, 3014 }, 3015 GetBlockF: MakeGetBlockF( 3016 []*snowmantest.Block{snowmantest.Genesis}, 3017 ), 3018 }, 3019 Consensus: c, 3020 }, 3021 metrics: metrics, 3022 nonVerifieds: ancestor.NewTree(), 3023 pending: map[ids.ID]snowman.Block{}, 3024 nonVerifiedCache: &cache.Empty[ids.ID, snowman.Block]{}, 3025 }, 3026 initialVote: issuedBlock.ID(), 3027 expectedAncestor: issuedBlock.ID(), 3028 expectedFound: true, 3029 }, 3030 { 3031 name: "drop unknown blockID", 3032 engine: &Transitive{ 3033 Config: Config{ 3034 Ctx: ctx, 3035 VM: &block.TestVM{ 3036 TestVM: common.TestVM{ 3037 T: t, 3038 }, 3039 GetBlockF: MakeGetBlockF( 3040 []*snowmantest.Block{snowmantest.Genesis}, 3041 ), 3042 }, 3043 Consensus: c, 3044 }, 3045 metrics: metrics, 3046 nonVerifieds: ancestor.NewTree(), 3047 pending: map[ids.ID]snowman.Block{}, 3048 nonVerifiedCache: &cache.Empty[ids.ID, snowman.Block]{}, 3049 }, 3050 initialVote: ids.GenerateTestID(), 3051 expectedAncestor: ids.Empty, 3052 expectedFound: false, 3053 }, 3054 { 3055 name: "apply vote through ancestor tree", 3056 engine: &Transitive{ 3057 Config: Config{ 3058 Ctx: ctx, 3059 VM: &block.TestVM{ 3060 TestVM: common.TestVM{ 3061 T: t, 3062 }, 3063 GetBlockF: MakeGetBlockF( 3064 []*snowmantest.Block{snowmantest.Genesis}, 3065 ), 3066 }, 3067 Consensus: c, 3068 }, 3069 metrics: metrics, 3070 nonVerifieds: nonVerifiedAncestors, 3071 pending: map[ids.ID]snowman.Block{}, 3072 nonVerifiedCache: &cache.Empty[ids.ID, snowman.Block]{}, 3073 }, 3074 initialVote: unissuedBlock.ID(), 3075 expectedAncestor: issuedBlock.ID(), 3076 expectedFound: true, 3077 }, 3078 { 3079 name: "apply vote through pending set", 3080 engine: &Transitive{ 3081 Config: Config{ 3082 Ctx: ctx, 3083 VM: &block.TestVM{ 3084 TestVM: common.TestVM{ 3085 T: t, 3086 }, 3087 GetBlockF: MakeGetBlockF( 3088 []*snowmantest.Block{snowmantest.Genesis}, 3089 ), 3090 }, 3091 Consensus: c, 3092 }, 3093 metrics: metrics, 3094 nonVerifieds: ancestor.NewTree(), 3095 pending: map[ids.ID]snowman.Block{ 3096 unissuedBlock.ID(): unissuedBlock, 3097 }, 3098 nonVerifiedCache: &cache.Empty[ids.ID, snowman.Block]{}, 3099 }, 3100 initialVote: unissuedBlock.ID(), 3101 expectedAncestor: issuedBlock.ID(), 3102 expectedFound: true, 3103 }, 3104 } 3105 for _, test := range tests { 3106 t.Run(test.name, func(t *testing.T) { 3107 require := require.New(t) 3108 3109 ancestor, found := test.engine.getProcessingAncestor(context.Background(), test.initialVote) 3110 require.Equal(test.expectedAncestor, ancestor) 3111 require.Equal(test.expectedFound, found) 3112 }) 3113 } 3114 } 3115 3116 // Test the engine's classification for blocks to either be dropped or try 3117 // issuance. 3118 // 3119 // Full blockchain structure: 3120 // 3121 // Genesis 3122 // / \ 3123 // 0 7 3124 // / \ | 3125 // 1 4 8 3126 // | | / \ 3127 // 2 5 9 11 3128 // | | | 3129 // 3 6 10 3130 // 3131 // Genesis and 0 are accepted. 3132 // 1 is issued. 3133 // 5 and 9 are pending. 3134 // 3135 // Structure known to engine: 3136 // 3137 // Genesis 3138 // / 3139 // 0 3140 // / 3141 // 1 3142 // 3143 // 5 9 3144 func TestShouldIssueBlock(t *testing.T) { 3145 var ( 3146 ctx = snowtest.ConsensusContext( 3147 snowtest.Context(t, snowtest.PChainID), 3148 ) 3149 chain0Through3 = snowmantest.BuildDescendants(snowmantest.Genesis, 4) 3150 chain4Through6 = snowmantest.BuildDescendants(chain0Through3[0], 3) 3151 chain7Through10 = snowmantest.BuildDescendants(snowmantest.Genesis, 4) 3152 chain11Through11 = snowmantest.BuildDescendants(chain7Through10[1], 1) 3153 blocks = join(chain0Through3, chain4Through6, chain7Through10, chain11Through11) 3154 ) 3155 3156 require.NoError(t, blocks[0].Accept(context.Background())) 3157 3158 c := &snowman.Topological{} 3159 require.NoError(t, c.Initialize( 3160 ctx, 3161 snowball.DefaultParameters, 3162 blocks[0].ID(), 3163 blocks[0].Height(), 3164 blocks[0].Timestamp(), 3165 )) 3166 require.NoError(t, c.Add(blocks[1])) 3167 3168 engine := &Transitive{ 3169 Config: Config{ 3170 Consensus: c, 3171 }, 3172 pending: map[ids.ID]snowman.Block{ 3173 blocks[5].ID(): blocks[5], 3174 blocks[9].ID(): blocks[9], 3175 }, 3176 } 3177 3178 tests := []struct { 3179 name string 3180 block snowman.Block 3181 expectedShouldIssue bool 3182 }{ 3183 { 3184 name: "genesis", 3185 block: snowmantest.Genesis, 3186 expectedShouldIssue: false, 3187 }, 3188 { 3189 name: "last accepted", 3190 block: blocks[0], 3191 expectedShouldIssue: false, 3192 }, 3193 { 3194 name: "already processing", 3195 block: blocks[1], 3196 expectedShouldIssue: false, 3197 }, 3198 { 3199 name: "next block to enqueue for issuance on top of a processing block", 3200 block: blocks[2], 3201 expectedShouldIssue: true, 3202 }, 3203 { 3204 name: "block to enqueue for issuance which depends on another block", 3205 block: blocks[3], 3206 expectedShouldIssue: true, 3207 }, 3208 { 3209 name: "next block to enqueue for issuance on top of an accepted block", 3210 block: blocks[4], 3211 expectedShouldIssue: true, 3212 }, 3213 { 3214 name: "already pending block", 3215 block: blocks[5], 3216 expectedShouldIssue: false, 3217 }, 3218 { 3219 name: "block to enqueue on top of a pending block", 3220 block: blocks[6], 3221 expectedShouldIssue: true, 3222 }, 3223 { 3224 name: "block was directly rejected", 3225 block: blocks[7], 3226 expectedShouldIssue: false, 3227 }, 3228 { 3229 name: "block was transitively rejected", 3230 block: blocks[8], 3231 expectedShouldIssue: false, 3232 }, 3233 { 3234 name: "block was transitively rejected but that is not known and was marked as pending", 3235 block: blocks[9], 3236 expectedShouldIssue: false, 3237 }, 3238 { 3239 name: "block was transitively rejected but that is not known and is built on top of pending", 3240 block: blocks[10], 3241 expectedShouldIssue: true, 3242 }, 3243 { 3244 name: "block was transitively rejected but that is not known", 3245 block: blocks[11], 3246 expectedShouldIssue: true, 3247 }, 3248 } 3249 for i, test := range tests { 3250 t.Run(fmt.Sprintf("%d %s", i-1, test.name), func(t *testing.T) { 3251 shouldIssue := engine.shouldIssueBlock(test.block) 3252 require.Equal(t, test.expectedShouldIssue, shouldIssue) 3253 }) 3254 } 3255 } 3256 3257 // join the provided slices into a single slice. 3258 // 3259 // TODO: Use slices.Concat once the minimum go version is 1.22. 3260 func join[T any](slices ...[]T) []T { 3261 size := 0 3262 for _, s := range slices { 3263 size += len(s) 3264 } 3265 newSlice := make([]T, 0, size) 3266 for _, s := range slices { 3267 newSlice = append(newSlice, s...) 3268 } 3269 return newSlice 3270 }