github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/executor/standard_block_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package executor 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/require" 13 "go.uber.org/mock/gomock" 14 15 "github.com/MetalBlockchain/metalgo/database" 16 "github.com/MetalBlockchain/metalgo/ids" 17 "github.com/MetalBlockchain/metalgo/utils/constants" 18 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 19 "github.com/MetalBlockchain/metalgo/vms/components/avax" 20 "github.com/MetalBlockchain/metalgo/vms/platformvm/block" 21 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 22 "github.com/MetalBlockchain/metalgo/vms/platformvm/status" 23 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 24 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/executor" 25 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 26 27 walletsigner "github.com/MetalBlockchain/metalgo/wallet/chain/p/signer" 28 ) 29 30 func TestApricotStandardBlockTimeVerification(t *testing.T) { 31 require := require.New(t) 32 ctrl := gomock.NewController(t) 33 34 env := newEnvironment(t, ctrl, apricotPhase5) 35 36 // setup and store parent block 37 // it's a standard block for simplicity 38 parentHeight := uint64(2022) 39 40 apricotParentBlk, err := block.NewApricotStandardBlock( 41 ids.Empty, // does not matter 42 parentHeight, 43 nil, // txs do not matter in this test 44 ) 45 require.NoError(err) 46 parentID := apricotParentBlk.ID() 47 48 // store parent block, with relevant quantities 49 onParentAccept := state.NewMockDiff(ctrl) 50 env.blkManager.(*manager).blkIDToState[parentID] = &blockState{ 51 statelessBlock: apricotParentBlk, 52 onAcceptState: onParentAccept, 53 } 54 env.blkManager.(*manager).lastAccepted = parentID 55 56 chainTime := env.clk.Time().Truncate(time.Second) 57 env.mockedState.EXPECT().GetLastAccepted().Return(parentID).AnyTimes() 58 env.mockedState.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() 59 onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() 60 61 // wrong height 62 apricotChildBlk, err := block.NewApricotStandardBlock( 63 apricotParentBlk.ID(), 64 apricotParentBlk.Height(), 65 nil, // txs nulled to simplify test 66 ) 67 require.NoError(err) 68 blk := env.blkManager.NewBlock(apricotChildBlk) 69 err = blk.Verify(context.Background()) 70 require.ErrorIs(err, errIncorrectBlockHeight) 71 72 // valid height 73 apricotChildBlk, err = block.NewApricotStandardBlock( 74 apricotParentBlk.ID(), 75 apricotParentBlk.Height()+1, 76 nil, // txs nulled to simplify test 77 ) 78 require.NoError(err) 79 blk = env.blkManager.NewBlock(apricotChildBlk) 80 require.NoError(blk.Verify(context.Background())) 81 } 82 83 func TestBanffStandardBlockTimeVerification(t *testing.T) { 84 require := require.New(t) 85 ctrl := gomock.NewController(t) 86 87 env := newEnvironment(t, ctrl, banff) 88 now := env.clk.Time() 89 env.clk.Set(now) 90 91 // setup and store parent block 92 // it's a standard block for simplicity 93 parentTime := now 94 parentHeight := uint64(2022) 95 96 banffParentBlk, err := block.NewBanffStandardBlock( 97 parentTime, 98 ids.Empty, // does not matter 99 parentHeight, 100 nil, // txs do not matter in this test 101 ) 102 require.NoError(err) 103 parentID := banffParentBlk.ID() 104 105 // store parent block, with relevant quantities 106 onParentAccept := state.NewMockDiff(ctrl) 107 chainTime := env.clk.Time().Truncate(time.Second) 108 env.blkManager.(*manager).blkIDToState[parentID] = &blockState{ 109 statelessBlock: banffParentBlk, 110 onAcceptState: onParentAccept, 111 timestamp: chainTime, 112 } 113 env.blkManager.(*manager).lastAccepted = parentID 114 env.mockedState.EXPECT().GetLastAccepted().Return(parentID).AnyTimes() 115 env.mockedState.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() 116 117 nextStakerTime := chainTime.Add(executor.SyncBound).Add(-1 * time.Second) 118 119 // store just once current staker to mark next staker time. 120 currentStakerIt := state.NewMockStakerIterator(ctrl) 121 currentStakerIt.EXPECT().Next().Return(true).AnyTimes() 122 currentStakerIt.EXPECT().Value().Return( 123 &state.Staker{ 124 NextTime: nextStakerTime, 125 Priority: txs.PrimaryNetworkValidatorCurrentPriority, 126 }, 127 ).AnyTimes() 128 currentStakerIt.EXPECT().Release().Return().AnyTimes() 129 onParentAccept.EXPECT().GetCurrentStakerIterator().Return(currentStakerIt, nil).AnyTimes() 130 131 // no pending stakers 132 pendingIt := state.NewMockStakerIterator(ctrl) 133 pendingIt.EXPECT().Next().Return(false).AnyTimes() 134 pendingIt.EXPECT().Release().Return().AnyTimes() 135 onParentAccept.EXPECT().GetPendingStakerIterator().Return(pendingIt, nil).AnyTimes() 136 137 onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes() 138 139 txID := ids.GenerateTestID() 140 utxo := &avax.UTXO{ 141 UTXOID: avax.UTXOID{ 142 TxID: txID, 143 }, 144 Asset: avax.Asset{ 145 ID: avaxAssetID, 146 }, 147 Out: &secp256k1fx.TransferOutput{ 148 Amt: env.config.StaticFeeConfig.CreateSubnetTxFee, 149 }, 150 } 151 utxoID := utxo.InputID() 152 onParentAccept.EXPECT().GetUTXO(utxoID).Return(utxo, nil).AnyTimes() 153 154 // Create the tx 155 utx := &txs.CreateSubnetTx{ 156 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 157 NetworkID: env.ctx.NetworkID, 158 BlockchainID: env.ctx.ChainID, 159 Ins: []*avax.TransferableInput{{ 160 UTXOID: utxo.UTXOID, 161 Asset: utxo.Asset, 162 In: &secp256k1fx.TransferInput{ 163 Amt: env.config.StaticFeeConfig.CreateSubnetTxFee, 164 }, 165 }}, 166 }}, 167 Owner: &secp256k1fx.OutputOwners{}, 168 } 169 tx := &txs.Tx{Unsigned: utx} 170 require.NoError(tx.Sign(txs.Codec, [][]*secp256k1.PrivateKey{{}})) 171 172 { 173 // wrong version 174 banffChildBlk, err := block.NewApricotStandardBlock( 175 banffParentBlk.ID(), 176 banffParentBlk.Height()+1, 177 []*txs.Tx{tx}, 178 ) 179 require.NoError(err) 180 block := env.blkManager.NewBlock(banffChildBlk) 181 err = block.Verify(context.Background()) 182 require.ErrorIs(err, errApricotBlockIssuedAfterFork) 183 } 184 185 { 186 // wrong height 187 childTimestamp := parentTime.Add(time.Second) 188 banffChildBlk, err := block.NewBanffStandardBlock( 189 childTimestamp, 190 banffParentBlk.ID(), 191 banffParentBlk.Height(), 192 []*txs.Tx{tx}, 193 ) 194 require.NoError(err) 195 block := env.blkManager.NewBlock(banffChildBlk) 196 err = block.Verify(context.Background()) 197 require.ErrorIs(err, errIncorrectBlockHeight) 198 } 199 200 { 201 // wrong timestamp, earlier than parent 202 childTimestamp := parentTime.Add(-1 * time.Second) 203 banffChildBlk, err := block.NewBanffStandardBlock( 204 childTimestamp, 205 banffParentBlk.ID(), 206 banffParentBlk.Height()+1, 207 []*txs.Tx{tx}, 208 ) 209 require.NoError(err) 210 block := env.blkManager.NewBlock(banffChildBlk) 211 err = block.Verify(context.Background()) 212 require.ErrorIs(err, errChildBlockEarlierThanParent) 213 } 214 215 { 216 // wrong timestamp, violated synchrony bound 217 initClkTime := env.clk.Time() 218 env.clk.Set(parentTime.Add(-executor.SyncBound)) 219 banffChildBlk, err := block.NewBanffStandardBlock( 220 parentTime.Add(time.Second), 221 banffParentBlk.ID(), 222 banffParentBlk.Height()+1, 223 []*txs.Tx{tx}, 224 ) 225 require.NoError(err) 226 block := env.blkManager.NewBlock(banffChildBlk) 227 err = block.Verify(context.Background()) 228 require.ErrorIs(err, executor.ErrChildBlockBeyondSyncBound) 229 env.clk.Set(initClkTime) 230 } 231 232 { 233 // wrong timestamp, skipped staker set change event 234 childTimestamp := nextStakerTime.Add(time.Second) 235 banffChildBlk, err := block.NewBanffStandardBlock( 236 childTimestamp, 237 banffParentBlk.ID(), 238 banffParentBlk.Height()+1, 239 []*txs.Tx{tx}, 240 ) 241 require.NoError(err) 242 block := env.blkManager.NewBlock(banffChildBlk) 243 err = block.Verify(context.Background()) 244 require.ErrorIs(err, executor.ErrChildBlockAfterStakerChangeTime) 245 } 246 247 { 248 // no state changes 249 childTimestamp := parentTime 250 banffChildBlk, err := block.NewBanffStandardBlock( 251 childTimestamp, 252 banffParentBlk.ID(), 253 banffParentBlk.Height()+1, 254 nil, 255 ) 256 require.NoError(err) 257 block := env.blkManager.NewBlock(banffChildBlk) 258 err = block.Verify(context.Background()) 259 require.ErrorIs(err, errBanffStandardBlockWithoutChanges) 260 } 261 262 { 263 // valid block, same timestamp as parent block 264 childTimestamp := parentTime 265 banffChildBlk, err := block.NewBanffStandardBlock( 266 childTimestamp, 267 banffParentBlk.ID(), 268 banffParentBlk.Height()+1, 269 []*txs.Tx{tx}, 270 ) 271 require.NoError(err) 272 block := env.blkManager.NewBlock(banffChildBlk) 273 require.NoError(block.Verify(context.Background())) 274 } 275 276 { 277 // valid 278 childTimestamp := nextStakerTime 279 banffChildBlk, err := block.NewBanffStandardBlock( 280 childTimestamp, 281 banffParentBlk.ID(), 282 banffParentBlk.Height()+1, 283 []*txs.Tx{tx}, 284 ) 285 require.NoError(err) 286 block := env.blkManager.NewBlock(banffChildBlk) 287 require.NoError(block.Verify(context.Background())) 288 } 289 } 290 291 func TestBanffStandardBlockUpdatePrimaryNetworkStakers(t *testing.T) { 292 require := require.New(t) 293 294 env := newEnvironment(t, nil, banff) 295 296 // Case: Timestamp is after next validator start time 297 // Add a pending validator 298 pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) 299 pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) 300 nodeID := ids.GenerateTestNodeID() 301 rewardAddress := ids.GenerateTestShortID() 302 addPendingValidatorTx, err := addPendingValidator( 303 env, 304 pendingValidatorStartTime, 305 pendingValidatorEndTime, 306 nodeID, 307 rewardAddress, 308 []*secp256k1.PrivateKey{preFundedKeys[0]}, 309 ) 310 require.NoError(err) 311 312 // build standard block moving ahead chain time 313 preferredID := env.state.GetLastAccepted() 314 parentBlk, err := env.state.GetStatelessBlock(preferredID) 315 require.NoError(err) 316 statelessStandardBlock, err := block.NewBanffStandardBlock( 317 pendingValidatorStartTime, 318 parentBlk.ID(), 319 parentBlk.Height()+1, 320 nil, // txs nulled to simplify test 321 ) 322 require.NoError(err) 323 block := env.blkManager.NewBlock(statelessStandardBlock) 324 325 // update staker set 326 require.NoError(block.Verify(context.Background())) 327 328 // tests 329 blkStateMap := env.blkManager.(*manager).blkIDToState 330 updatedState := blkStateMap[block.ID()].onAcceptState 331 currentValidator, err := updatedState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 332 require.NoError(err) 333 require.Equal(addPendingValidatorTx.ID(), currentValidator.TxID) 334 require.Equal(uint64(1370), currentValidator.PotentialReward) // See rewards tests to explain why 1370 335 336 _, err = updatedState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) 337 require.ErrorIs(err, database.ErrNotFound) 338 339 // Test VM validators 340 require.NoError(block.Accept(context.Background())) 341 _, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, nodeID) 342 require.True(ok) 343 } 344 345 // Ensure semantic verification updates the current and pending staker sets correctly. 346 // Namely, it should add pending stakers whose start time is at or before the timestamp. 347 // It will not remove primary network stakers; that happens in rewardTxs. 348 func TestBanffStandardBlockUpdateStakers(t *testing.T) { 349 // Chronological order (not in scale): 350 // Staker1: |----------------------------------------------------------| 351 // Staker2: |------------------------| 352 // Staker3: |------------------------| 353 // Staker3sub: |----------------| 354 // Staker4: |------------------------| 355 // Staker5: |--------------------| 356 357 // In this test multiple stakers may join and leave the staker set at the same time. 358 // The order in which they do it is asserted; the order may depend on the staker.TxID, 359 // which in turns depend on every feature of the transaction creating the staker. 360 // So in this test we avoid ids.GenerateTestNodeID, in favour of ids.BuildTestNodeID 361 // so that TxID does not depend on the order we run tests. 362 staker1 := staker{ 363 nodeID: ids.BuildTestNodeID([]byte{0xf1}), 364 rewardAddress: ids.ShortID{0xf1}, 365 startTime: defaultGenesisTime.Add(1 * time.Minute), 366 endTime: defaultGenesisTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute), 367 } 368 staker2 := staker{ 369 nodeID: ids.BuildTestNodeID([]byte{0xf2}), 370 rewardAddress: ids.ShortID{0xf2}, 371 startTime: staker1.startTime.Add(1 * time.Minute), 372 endTime: staker1.startTime.Add(1 * time.Minute).Add(defaultMinStakingDuration), 373 } 374 staker3 := staker{ 375 nodeID: ids.BuildTestNodeID([]byte{0xf3}), 376 rewardAddress: ids.ShortID{0xf3}, 377 startTime: staker2.startTime.Add(1 * time.Minute), 378 endTime: staker2.endTime.Add(1 * time.Minute), 379 } 380 staker3Sub := staker{ 381 nodeID: ids.BuildTestNodeID([]byte{0xf3}), 382 rewardAddress: ids.ShortID{0xff}, 383 startTime: staker3.startTime.Add(1 * time.Minute), 384 endTime: staker3.endTime.Add(-1 * time.Minute), 385 } 386 staker4 := staker{ 387 nodeID: ids.BuildTestNodeID([]byte{0xf4}), 388 rewardAddress: ids.ShortID{0xf4}, 389 startTime: staker3.startTime, 390 endTime: staker3.endTime, 391 } 392 staker5 := staker{ 393 nodeID: ids.BuildTestNodeID([]byte{0xf5}), 394 rewardAddress: ids.ShortID{0xf5}, 395 startTime: staker2.endTime, 396 endTime: staker2.endTime.Add(defaultMinStakingDuration), 397 } 398 399 tests := []test{ 400 { 401 description: "advance time to staker 1 start with subnet", 402 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 403 subnetStakers: []staker{staker1}, 404 advanceTimeTo: []time.Time{staker1.startTime}, 405 expectedStakers: map[ids.NodeID]stakerStatus{ 406 staker1.nodeID: current, 407 staker2.nodeID: pending, 408 staker3.nodeID: pending, 409 staker4.nodeID: pending, 410 staker5.nodeID: pending, 411 }, 412 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 413 staker1.nodeID: current, 414 staker2.nodeID: pending, 415 staker3.nodeID: pending, 416 staker4.nodeID: pending, 417 staker5.nodeID: pending, 418 }, 419 }, 420 { 421 description: "advance time to the staker2 start", 422 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 423 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime}, 424 expectedStakers: map[ids.NodeID]stakerStatus{ 425 staker1.nodeID: current, 426 staker2.nodeID: current, 427 staker3.nodeID: pending, 428 staker4.nodeID: pending, 429 staker5.nodeID: pending, 430 }, 431 }, 432 { 433 description: "staker3 should validate only primary network", 434 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 435 subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, 436 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime}, 437 expectedStakers: map[ids.NodeID]stakerStatus{ 438 staker1.nodeID: current, 439 staker2.nodeID: current, 440 staker3.nodeID: current, 441 staker4.nodeID: current, 442 staker5.nodeID: pending, 443 }, 444 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 445 staker1.nodeID: current, 446 staker2.nodeID: current, 447 staker3Sub.nodeID: pending, 448 staker4.nodeID: current, 449 staker5.nodeID: pending, 450 }, 451 }, 452 { 453 description: "advance time to staker3 start with subnet", 454 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 455 subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, 456 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker3Sub.startTime}, 457 expectedStakers: map[ids.NodeID]stakerStatus{ 458 staker1.nodeID: current, 459 staker2.nodeID: current, 460 staker3.nodeID: current, 461 staker4.nodeID: current, 462 staker5.nodeID: pending, 463 }, 464 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 465 staker1.nodeID: current, 466 staker2.nodeID: current, 467 staker3.nodeID: current, 468 staker4.nodeID: current, 469 staker5.nodeID: pending, 470 }, 471 }, 472 { 473 description: "advance time to staker5 start", 474 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 475 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker5.startTime}, 476 expectedStakers: map[ids.NodeID]stakerStatus{ 477 staker1.nodeID: current, 478 479 // Staker2's end time matches staker5's start time, so typically 480 // the block builder would produce a ProposalBlock to remove 481 // staker2 when advancing the time. However, it is valid to only 482 // advance the time with a StandardBlock and not remove staker2, 483 // which is what this test does. 484 staker2.nodeID: current, 485 staker3.nodeID: current, 486 staker4.nodeID: current, 487 staker5.nodeID: current, 488 }, 489 }, 490 } 491 492 for _, test := range tests { 493 t.Run(test.description, func(t *testing.T) { 494 require := require.New(t) 495 env := newEnvironment(t, nil, banff) 496 497 subnetID := testSubnet1.ID() 498 env.config.TrackedSubnets.Add(subnetID) 499 500 for _, staker := range test.stakers { 501 _, err := addPendingValidator( 502 env, 503 staker.startTime, 504 staker.endTime, 505 staker.nodeID, 506 staker.rewardAddress, 507 []*secp256k1.PrivateKey{preFundedKeys[0]}, 508 ) 509 require.NoError(err) 510 } 511 512 for _, staker := range test.subnetStakers { 513 builder, signer := env.factory.NewWallet(preFundedKeys[0], preFundedKeys[1]) 514 utx, err := builder.NewAddSubnetValidatorTx( 515 &txs.SubnetValidator{ 516 Validator: txs.Validator{ 517 NodeID: staker.nodeID, 518 Start: uint64(staker.startTime.Unix()), 519 End: uint64(staker.endTime.Unix()), 520 Wght: 10, 521 }, 522 Subnet: subnetID, 523 }, 524 ) 525 require.NoError(err) 526 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 527 require.NoError(err) 528 529 staker, err := state.NewPendingStaker( 530 tx.ID(), 531 tx.Unsigned.(*txs.AddSubnetValidatorTx), 532 ) 533 require.NoError(err) 534 535 env.state.PutPendingValidator(staker) 536 env.state.AddTx(tx, status.Committed) 537 } 538 env.state.SetHeight( /*dummyHeight*/ 1) 539 require.NoError(env.state.Commit()) 540 541 for _, newTime := range test.advanceTimeTo { 542 env.clk.Set(newTime) 543 544 // build standard block moving ahead chain time 545 preferredID := env.state.GetLastAccepted() 546 parentBlk, err := env.state.GetStatelessBlock(preferredID) 547 require.NoError(err) 548 statelessStandardBlock, err := block.NewBanffStandardBlock( 549 newTime, 550 parentBlk.ID(), 551 parentBlk.Height()+1, 552 nil, // txs nulled to simplify test 553 ) 554 block := env.blkManager.NewBlock(statelessStandardBlock) 555 556 require.NoError(err) 557 558 // update staker set 559 require.NoError(block.Verify(context.Background())) 560 require.NoError(block.Accept(context.Background())) 561 } 562 563 for stakerNodeID, status := range test.expectedStakers { 564 switch status { 565 case pending: 566 _, err := env.state.GetPendingValidator(constants.PrimaryNetworkID, stakerNodeID) 567 require.NoError(err) 568 _, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, stakerNodeID) 569 require.False(ok) 570 case current: 571 _, err := env.state.GetCurrentValidator(constants.PrimaryNetworkID, stakerNodeID) 572 require.NoError(err) 573 _, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, stakerNodeID) 574 require.True(ok) 575 } 576 } 577 578 for stakerNodeID, status := range test.expectedSubnetStakers { 579 switch status { 580 case pending: 581 _, ok := env.config.Validators.GetValidator(subnetID, stakerNodeID) 582 require.False(ok) 583 case current: 584 _, ok := env.config.Validators.GetValidator(subnetID, stakerNodeID) 585 require.True(ok) 586 } 587 } 588 }) 589 } 590 } 591 592 // Regression test for https://github.com/MetalBlockchain/metalgo/pull/584 593 // that ensures it fixes a bug where subnet validators are not removed 594 // when timestamp is advanced and there is a pending staker whose start time 595 // is after the new timestamp 596 func TestBanffStandardBlockRemoveSubnetValidator(t *testing.T) { 597 require := require.New(t) 598 env := newEnvironment(t, nil, banff) 599 600 subnetID := testSubnet1.ID() 601 env.config.TrackedSubnets.Add(subnetID) 602 603 // Add a subnet validator to the staker set 604 subnetValidatorNodeID := genesisNodeIDs[0] 605 subnetVdr1StartTime := defaultValidateStartTime 606 subnetVdr1EndTime := defaultValidateStartTime.Add(defaultMinStakingDuration) 607 builder, signer := env.factory.NewWallet(preFundedKeys[0], preFundedKeys[1]) 608 utx, err := builder.NewAddSubnetValidatorTx( 609 &txs.SubnetValidator{ 610 Validator: txs.Validator{ 611 NodeID: subnetValidatorNodeID, 612 Start: uint64(subnetVdr1StartTime.Unix()), 613 End: uint64(subnetVdr1EndTime.Unix()), 614 Wght: 1, 615 }, 616 Subnet: subnetID, 617 }, 618 ) 619 require.NoError(err) 620 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 621 require.NoError(err) 622 623 addSubnetValTx := tx.Unsigned.(*txs.AddSubnetValidatorTx) 624 staker, err := state.NewCurrentStaker( 625 tx.ID(), 626 addSubnetValTx, 627 addSubnetValTx.StartTime(), 628 0, 629 ) 630 require.NoError(err) 631 632 env.state.PutCurrentValidator(staker) 633 env.state.AddTx(tx, status.Committed) 634 require.NoError(env.state.Commit()) 635 636 // The above validator is now part of the staking set 637 638 // Queue a staker that joins the staker set after the above validator leaves 639 subnetVdr2NodeID := genesisNodeIDs[1] 640 utx, err = builder.NewAddSubnetValidatorTx( 641 &txs.SubnetValidator{ 642 Validator: txs.Validator{ 643 NodeID: subnetVdr2NodeID, 644 Start: uint64(subnetVdr1EndTime.Add(time.Second).Unix()), 645 End: uint64(subnetVdr1EndTime.Add(time.Second).Add(defaultMinStakingDuration).Unix()), 646 Wght: 1, 647 }, 648 Subnet: subnetID, 649 }, 650 ) 651 require.NoError(err) 652 tx, err = walletsigner.SignUnsigned(context.Background(), signer, utx) 653 require.NoError(err) 654 655 staker, err = state.NewPendingStaker( 656 tx.ID(), 657 tx.Unsigned.(*txs.AddSubnetValidatorTx), 658 ) 659 require.NoError(err) 660 661 env.state.PutPendingValidator(staker) 662 env.state.AddTx(tx, status.Committed) 663 require.NoError(env.state.Commit()) 664 665 // The above validator is now in the pending staker set 666 667 // Advance time to the first staker's end time. 668 env.clk.Set(subnetVdr1EndTime) 669 // build standard block moving ahead chain time 670 preferredID := env.state.GetLastAccepted() 671 parentBlk, err := env.state.GetStatelessBlock(preferredID) 672 require.NoError(err) 673 statelessStandardBlock, err := block.NewBanffStandardBlock( 674 subnetVdr1EndTime, 675 parentBlk.ID(), 676 parentBlk.Height()+1, 677 nil, // txs nulled to simplify test 678 ) 679 require.NoError(err) 680 block := env.blkManager.NewBlock(statelessStandardBlock) 681 682 // update staker set 683 require.NoError(block.Verify(context.Background())) 684 685 blkStateMap := env.blkManager.(*manager).blkIDToState 686 updatedState := blkStateMap[block.ID()].onAcceptState 687 _, err = updatedState.GetCurrentValidator(subnetID, subnetValidatorNodeID) 688 require.ErrorIs(err, database.ErrNotFound) 689 690 // Check VM Validators are removed successfully 691 require.NoError(block.Accept(context.Background())) 692 _, ok := env.config.Validators.GetValidator(subnetID, subnetVdr2NodeID) 693 require.False(ok) 694 _, ok = env.config.Validators.GetValidator(subnetID, subnetValidatorNodeID) 695 require.False(ok) 696 } 697 698 func TestBanffStandardBlockTrackedSubnet(t *testing.T) { 699 for _, tracked := range []bool{true, false} { 700 t.Run(fmt.Sprintf("tracked %t", tracked), func(t *testing.T) { 701 require := require.New(t) 702 env := newEnvironment(t, nil, banff) 703 704 subnetID := testSubnet1.ID() 705 if tracked { 706 env.config.TrackedSubnets.Add(subnetID) 707 } 708 709 // Add a subnet validator to the staker set 710 subnetValidatorNodeID := genesisNodeIDs[0] 711 subnetVdr1StartTime := defaultGenesisTime.Add(1 * time.Minute) 712 subnetVdr1EndTime := defaultGenesisTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute) 713 builder, signer := env.factory.NewWallet(preFundedKeys[0], preFundedKeys[1]) 714 utx, err := builder.NewAddSubnetValidatorTx( 715 &txs.SubnetValidator{ 716 Validator: txs.Validator{ 717 NodeID: subnetValidatorNodeID, 718 Start: uint64(subnetVdr1StartTime.Unix()), 719 End: uint64(subnetVdr1EndTime.Unix()), 720 Wght: 1, 721 }, 722 Subnet: subnetID, 723 }, 724 ) 725 require.NoError(err) 726 tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 727 require.NoError(err) 728 729 staker, err := state.NewPendingStaker( 730 tx.ID(), 731 tx.Unsigned.(*txs.AddSubnetValidatorTx), 732 ) 733 require.NoError(err) 734 735 env.state.PutPendingValidator(staker) 736 env.state.AddTx(tx, status.Committed) 737 require.NoError(env.state.Commit()) 738 739 // Advance time to the staker's start time. 740 env.clk.Set(subnetVdr1StartTime) 741 742 // build standard block moving ahead chain time 743 preferredID := env.state.GetLastAccepted() 744 parentBlk, err := env.state.GetStatelessBlock(preferredID) 745 require.NoError(err) 746 statelessStandardBlock, err := block.NewBanffStandardBlock( 747 subnetVdr1StartTime, 748 parentBlk.ID(), 749 parentBlk.Height()+1, 750 nil, // txs nulled to simplify test 751 ) 752 require.NoError(err) 753 block := env.blkManager.NewBlock(statelessStandardBlock) 754 755 // update staker set 756 require.NoError(block.Verify(context.Background())) 757 require.NoError(block.Accept(context.Background())) 758 _, ok := env.config.Validators.GetValidator(subnetID, subnetValidatorNodeID) 759 require.True(ok) 760 }) 761 } 762 } 763 764 func TestBanffStandardBlockDelegatorStakerWeight(t *testing.T) { 765 require := require.New(t) 766 env := newEnvironment(t, nil, banff) 767 768 // Case: Timestamp is after next validator start time 769 // Add a pending validator 770 pendingValidatorStartTime := defaultGenesisTime.Add(1 * time.Second) 771 pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMaxStakingDuration) 772 nodeID := ids.GenerateTestNodeID() 773 rewardAddress := ids.GenerateTestShortID() 774 _, err := addPendingValidator( 775 env, 776 pendingValidatorStartTime, 777 pendingValidatorEndTime, 778 nodeID, 779 rewardAddress, 780 []*secp256k1.PrivateKey{preFundedKeys[0]}, 781 ) 782 require.NoError(err) 783 784 // build standard block moving ahead chain time 785 preferredID := env.state.GetLastAccepted() 786 parentBlk, err := env.state.GetStatelessBlock(preferredID) 787 require.NoError(err) 788 statelessStandardBlock, err := block.NewBanffStandardBlock( 789 pendingValidatorStartTime, 790 parentBlk.ID(), 791 parentBlk.Height()+1, 792 nil, // txs nulled to simplify test 793 ) 794 require.NoError(err) 795 blk := env.blkManager.NewBlock(statelessStandardBlock) 796 require.NoError(blk.Verify(context.Background())) 797 require.NoError(blk.Accept(context.Background())) 798 require.NoError(env.state.Commit()) 799 800 // Test validator weight before delegation 801 vdrWeight := env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID) 802 require.Equal(env.config.MinValidatorStake, vdrWeight) 803 804 // Add delegator 805 pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second) 806 pendingDelegatorEndTime := pendingDelegatorStartTime.Add(1 * time.Second) 807 808 builder, signer := env.factory.NewWallet(preFundedKeys[0], preFundedKeys[1], preFundedKeys[4]) 809 utx, err := builder.NewAddDelegatorTx( 810 &txs.Validator{ 811 NodeID: nodeID, 812 Start: uint64(pendingDelegatorStartTime.Unix()), 813 End: uint64(pendingDelegatorEndTime.Unix()), 814 Wght: env.config.MinDelegatorStake, 815 }, 816 &secp256k1fx.OutputOwners{ 817 Threshold: 1, 818 Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, 819 }, 820 ) 821 require.NoError(err) 822 addDelegatorTx, err := walletsigner.SignUnsigned(context.Background(), signer, utx) 823 require.NoError(err) 824 825 staker, err := state.NewPendingStaker( 826 addDelegatorTx.ID(), 827 addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), 828 ) 829 require.NoError(err) 830 831 env.state.PutPendingDelegator(staker) 832 env.state.AddTx(addDelegatorTx, status.Committed) 833 env.state.SetHeight( /*dummyHeight*/ uint64(1)) 834 require.NoError(env.state.Commit()) 835 836 // Advance Time 837 preferredID = env.state.GetLastAccepted() 838 parentBlk, err = env.state.GetStatelessBlock(preferredID) 839 require.NoError(err) 840 statelessStandardBlock, err = block.NewBanffStandardBlock( 841 pendingDelegatorStartTime, 842 parentBlk.ID(), 843 parentBlk.Height()+1, 844 nil, // txs nulled to simplify test 845 ) 846 require.NoError(err) 847 blk = env.blkManager.NewBlock(statelessStandardBlock) 848 require.NoError(blk.Verify(context.Background())) 849 require.NoError(blk.Accept(context.Background())) 850 require.NoError(env.state.Commit()) 851 852 // Test validator weight after delegation 853 vdrWeight = env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID) 854 require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight) 855 }