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