github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/vm_regression_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 platformvm 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "testing" 11 "time" 12 13 "github.com/stretchr/testify/require" 14 "golang.org/x/sync/errgroup" 15 16 "github.com/ava-labs/avalanchego/chains" 17 "github.com/ava-labs/avalanchego/chains/atomic" 18 "github.com/ava-labs/avalanchego/database" 19 "github.com/ava-labs/avalanchego/database/memdb" 20 "github.com/ava-labs/avalanchego/database/prefixdb" 21 "github.com/ava-labs/avalanchego/ids" 22 "github.com/ava-labs/avalanchego/network/p2p" 23 "github.com/ava-labs/avalanchego/network/p2p/gossip" 24 "github.com/ava-labs/avalanchego/snow/consensus/snowman" 25 "github.com/ava-labs/avalanchego/snow/engine/common" 26 "github.com/ava-labs/avalanchego/snow/snowtest" 27 "github.com/ava-labs/avalanchego/snow/uptime" 28 "github.com/ava-labs/avalanchego/snow/validators" 29 "github.com/ava-labs/avalanchego/upgrade/upgradetest" 30 "github.com/ava-labs/avalanchego/utils/bloom" 31 "github.com/ava-labs/avalanchego/utils/constants" 32 "github.com/ava-labs/avalanchego/utils/crypto/bls" 33 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" 34 "github.com/ava-labs/avalanchego/utils/set" 35 "github.com/ava-labs/avalanchego/version" 36 "github.com/ava-labs/avalanchego/vms/components/avax" 37 "github.com/ava-labs/avalanchego/vms/platformvm/block" 38 "github.com/ava-labs/avalanchego/vms/platformvm/config" 39 "github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest" 40 "github.com/ava-labs/avalanchego/vms/platformvm/reward" 41 "github.com/ava-labs/avalanchego/vms/platformvm/signer" 42 "github.com/ava-labs/avalanchego/vms/platformvm/state" 43 "github.com/ava-labs/avalanchego/vms/platformvm/state/statetest" 44 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 45 "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" 46 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 47 48 blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/block/executor" 49 walletcommon "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" 50 ) 51 52 func TestAddDelegatorTxOverDelegatedRegression(t *testing.T) { 53 require := require.New(t) 54 vm, _, _ := defaultVM(t, upgradetest.Cortina) 55 vm.ctx.Lock.Lock() 56 defer vm.ctx.Lock.Unlock() 57 58 wallet := newWallet(t, vm, walletConfig{}) 59 60 validatorStartTime := vm.clock.Time().Add(executor.SyncBound).Add(1 * time.Second) 61 validatorEndTime := validatorStartTime.Add(360 * 24 * time.Hour) 62 63 nodeID := ids.GenerateTestNodeID() 64 rewardsOwner := &secp256k1fx.OutputOwners{ 65 Threshold: 1, 66 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 67 } 68 69 // create valid tx 70 addValidatorTx, err := wallet.IssueAddValidatorTx( 71 &txs.Validator{ 72 NodeID: nodeID, 73 Start: uint64(validatorStartTime.Unix()), 74 End: uint64(validatorEndTime.Unix()), 75 Wght: vm.MinValidatorStake, 76 }, 77 rewardsOwner, 78 reward.PercentDenominator, 79 ) 80 require.NoError(err) 81 82 // trigger block creation 83 vm.ctx.Lock.Unlock() 84 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 85 vm.ctx.Lock.Lock() 86 87 // Accept addValidatorTx 88 require.NoError(buildAndAcceptStandardBlock(vm)) 89 90 // Advance the time 91 vm.clock.Set(validatorStartTime) 92 require.NoError(buildAndAcceptStandardBlock(vm)) 93 94 firstDelegatorStartTime := validatorStartTime.Add(executor.SyncBound).Add(1 * time.Second) 95 firstDelegatorEndTime := firstDelegatorStartTime.Add(vm.MinStakeDuration) 96 97 // create valid tx 98 addFirstDelegatorTx, err := wallet.IssueAddDelegatorTx( 99 &txs.Validator{ 100 NodeID: nodeID, 101 Start: uint64(firstDelegatorStartTime.Unix()), 102 End: uint64(firstDelegatorEndTime.Unix()), 103 Wght: 4 * vm.MinValidatorStake, // maximum amount of stake this delegator can provide 104 }, 105 rewardsOwner, 106 ) 107 require.NoError(err) 108 109 // trigger block creation 110 vm.ctx.Lock.Unlock() 111 require.NoError(vm.issueTxFromRPC(addFirstDelegatorTx)) 112 vm.ctx.Lock.Lock() 113 114 // Accept addFirstDelegatorTx 115 require.NoError(buildAndAcceptStandardBlock(vm)) 116 117 // Advance the time 118 vm.clock.Set(firstDelegatorStartTime) 119 require.NoError(buildAndAcceptStandardBlock(vm)) 120 121 secondDelegatorStartTime := firstDelegatorEndTime.Add(2 * time.Second) 122 secondDelegatorEndTime := secondDelegatorStartTime.Add(vm.MinStakeDuration) 123 124 vm.clock.Set(secondDelegatorStartTime.Add(-10 * executor.SyncBound)) 125 126 // create valid tx 127 addSecondDelegatorTx, err := wallet.IssueAddDelegatorTx( 128 &txs.Validator{ 129 NodeID: nodeID, 130 Start: uint64(secondDelegatorStartTime.Unix()), 131 End: uint64(secondDelegatorEndTime.Unix()), 132 Wght: vm.MinDelegatorStake, 133 }, 134 rewardsOwner, 135 ) 136 require.NoError(err) 137 138 // trigger block creation 139 vm.ctx.Lock.Unlock() 140 require.NoError(vm.issueTxFromRPC(addSecondDelegatorTx)) 141 vm.ctx.Lock.Lock() 142 143 // Accept addSecondDelegatorTx 144 require.NoError(buildAndAcceptStandardBlock(vm)) 145 146 thirdDelegatorStartTime := firstDelegatorEndTime.Add(-time.Second) 147 thirdDelegatorEndTime := thirdDelegatorStartTime.Add(vm.MinStakeDuration) 148 149 // create invalid tx 150 addThirdDelegatorTx, err := wallet.IssueAddDelegatorTx( 151 &txs.Validator{ 152 NodeID: nodeID, 153 Start: uint64(thirdDelegatorStartTime.Unix()), 154 End: uint64(thirdDelegatorEndTime.Unix()), 155 Wght: vm.MinDelegatorStake, 156 }, 157 rewardsOwner, 158 ) 159 require.NoError(err) 160 161 // trigger block creation 162 vm.ctx.Lock.Unlock() 163 err = vm.issueTxFromRPC(addThirdDelegatorTx) 164 require.ErrorIs(err, executor.ErrOverDelegated) 165 vm.ctx.Lock.Lock() 166 } 167 168 func TestAddDelegatorTxHeapCorruption(t *testing.T) { 169 validatorStartTime := latestForkTime.Add(executor.SyncBound).Add(1 * time.Second) 170 validatorEndTime := validatorStartTime.Add(360 * 24 * time.Hour) 171 validatorStake := defaultMaxValidatorStake / 5 172 173 delegator1StartTime := validatorStartTime 174 delegator1EndTime := delegator1StartTime.Add(3 * defaultMinStakingDuration) 175 delegator1Stake := defaultMinValidatorStake 176 177 delegator2StartTime := validatorStartTime.Add(1 * defaultMinStakingDuration) 178 delegator2EndTime := delegator1StartTime.Add(6 * defaultMinStakingDuration) 179 delegator2Stake := defaultMinValidatorStake 180 181 delegator3StartTime := validatorStartTime.Add(2 * defaultMinStakingDuration) 182 delegator3EndTime := delegator1StartTime.Add(4 * defaultMinStakingDuration) 183 delegator3Stake := defaultMaxValidatorStake - validatorStake - 2*defaultMinValidatorStake 184 185 delegator4StartTime := validatorStartTime.Add(5 * defaultMinStakingDuration) 186 delegator4EndTime := delegator1StartTime.Add(7 * defaultMinStakingDuration) 187 delegator4Stake := defaultMaxValidatorStake - validatorStake - defaultMinValidatorStake 188 189 tests := []struct { 190 name string 191 ap3Time time.Time 192 }{ 193 { 194 name: "pre-upgrade is no longer restrictive", 195 ap3Time: validatorEndTime, 196 }, 197 { 198 name: "post-upgrade calculate max stake correctly", 199 ap3Time: genesistest.DefaultValidatorStartTime, 200 }, 201 } 202 203 for _, test := range tests { 204 t.Run(test.name, func(t *testing.T) { 205 require := require.New(t) 206 207 vm, _, _ := defaultVM(t, upgradetest.ApricotPhase3) 208 vm.UpgradeConfig.ApricotPhase3Time = test.ap3Time 209 210 vm.ctx.Lock.Lock() 211 defer vm.ctx.Lock.Unlock() 212 213 wallet := newWallet(t, vm, walletConfig{}) 214 215 nodeID := ids.GenerateTestNodeID() 216 rewardsOwner := &secp256k1fx.OutputOwners{ 217 Threshold: 1, 218 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 219 } 220 221 // create valid tx 222 addValidatorTx, err := wallet.IssueAddValidatorTx( 223 &txs.Validator{ 224 NodeID: nodeID, 225 Start: uint64(validatorStartTime.Unix()), 226 End: uint64(validatorEndTime.Unix()), 227 Wght: validatorStake, 228 }, 229 rewardsOwner, 230 reward.PercentDenominator, 231 ) 232 require.NoError(err) 233 234 // issue the add validator tx 235 vm.ctx.Lock.Unlock() 236 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 237 vm.ctx.Lock.Lock() 238 239 // Accept addValidatorTx 240 require.NoError(buildAndAcceptStandardBlock(vm)) 241 242 // create valid tx 243 addFirstDelegatorTx, err := wallet.IssueAddDelegatorTx( 244 &txs.Validator{ 245 NodeID: nodeID, 246 Start: uint64(delegator1StartTime.Unix()), 247 End: uint64(delegator1EndTime.Unix()), 248 Wght: delegator1Stake, 249 }, 250 rewardsOwner, 251 ) 252 require.NoError(err) 253 254 // issue the first add delegator tx 255 vm.ctx.Lock.Unlock() 256 require.NoError(vm.issueTxFromRPC(addFirstDelegatorTx)) 257 vm.ctx.Lock.Lock() 258 259 // Accept addFirstDelegatorTx 260 require.NoError(buildAndAcceptStandardBlock(vm)) 261 262 // create valid tx 263 addSecondDelegatorTx, err := wallet.IssueAddDelegatorTx( 264 &txs.Validator{ 265 NodeID: nodeID, 266 Start: uint64(delegator2StartTime.Unix()), 267 End: uint64(delegator2EndTime.Unix()), 268 Wght: delegator2Stake, 269 }, 270 rewardsOwner, 271 ) 272 require.NoError(err) 273 274 // issue the second add delegator tx 275 vm.ctx.Lock.Unlock() 276 require.NoError(vm.issueTxFromRPC(addSecondDelegatorTx)) 277 vm.ctx.Lock.Lock() 278 279 // Accept addSecondDelegatorTx 280 require.NoError(buildAndAcceptStandardBlock(vm)) 281 282 // create valid tx 283 addThirdDelegatorTx, err := wallet.IssueAddDelegatorTx( 284 &txs.Validator{ 285 NodeID: nodeID, 286 Start: uint64(delegator3StartTime.Unix()), 287 End: uint64(delegator3EndTime.Unix()), 288 Wght: delegator3Stake, 289 }, 290 rewardsOwner, 291 ) 292 require.NoError(err) 293 294 // issue the third add delegator tx 295 vm.ctx.Lock.Unlock() 296 require.NoError(vm.issueTxFromRPC(addThirdDelegatorTx)) 297 vm.ctx.Lock.Lock() 298 299 // Accept addThirdDelegatorTx 300 require.NoError(buildAndAcceptStandardBlock(vm)) 301 302 // create valid tx 303 addFourthDelegatorTx, err := wallet.IssueAddDelegatorTx( 304 &txs.Validator{ 305 NodeID: nodeID, 306 Start: uint64(delegator4StartTime.Unix()), 307 End: uint64(delegator4EndTime.Unix()), 308 Wght: delegator4Stake, 309 }, 310 rewardsOwner, 311 ) 312 require.NoError(err) 313 314 // issue the fourth add delegator tx 315 vm.ctx.Lock.Unlock() 316 require.NoError(vm.issueTxFromRPC(addFourthDelegatorTx)) 317 vm.ctx.Lock.Lock() 318 319 // Accept addFourthDelegatorTx 320 require.NoError(buildAndAcceptStandardBlock(vm)) 321 }) 322 } 323 } 324 325 // Test that calling Verify on a block with an unverified parent doesn't cause a 326 // panic. 327 func TestUnverifiedParentPanicRegression(t *testing.T) { 328 require := require.New(t) 329 330 baseDB := memdb.New() 331 atomicDB := prefixdb.New([]byte{1}, baseDB) 332 333 vm := &VM{Config: config.Config{ 334 Chains: chains.TestManager, 335 Validators: validators.NewManager(), 336 UptimeLockedCalculator: uptime.NewLockedCalculator(), 337 MinStakeDuration: defaultMinStakingDuration, 338 MaxStakeDuration: defaultMaxStakingDuration, 339 RewardConfig: defaultRewardConfig, 340 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Banff, latestForkTime), 341 }} 342 343 ctx := snowtest.Context(t, snowtest.PChainID) 344 ctx.Lock.Lock() 345 defer func() { 346 require.NoError(vm.Shutdown(context.Background())) 347 ctx.Lock.Unlock() 348 }() 349 350 msgChan := make(chan common.Message, 1) 351 require.NoError(vm.Initialize( 352 context.Background(), 353 ctx, 354 baseDB, 355 genesistest.NewBytes(t, genesistest.Config{}), 356 nil, 357 nil, 358 msgChan, 359 nil, 360 nil, 361 )) 362 363 m := atomic.NewMemory(atomicDB) 364 vm.ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) 365 366 // set time to post Banff fork 367 vm.clock.Set(latestForkTime.Add(time.Second)) 368 vm.state.SetTimestamp(latestForkTime.Add(time.Second)) 369 370 var ( 371 key0 = genesistest.DefaultFundedKeys[0] 372 addr0 = key0.Address() 373 owners0 = &secp256k1fx.OutputOwners{ 374 Threshold: 1, 375 Addrs: []ids.ShortID{addr0}, 376 } 377 378 key1 = genesistest.DefaultFundedKeys[1] 379 addr1 = key1.Address() 380 owners1 = &secp256k1fx.OutputOwners{ 381 Threshold: 1, 382 Addrs: []ids.ShortID{addr1}, 383 } 384 ) 385 386 wallet := newWallet(t, vm, walletConfig{}) 387 addSubnetTx0, err := wallet.IssueCreateSubnetTx( 388 owners0, 389 walletcommon.WithCustomAddresses(set.Of( 390 addr0, 391 )), 392 ) 393 require.NoError(err) 394 395 addSubnetTx1, err := wallet.IssueCreateSubnetTx( 396 owners1, 397 walletcommon.WithCustomAddresses(set.Of( 398 addr1, 399 )), 400 ) 401 require.NoError(err) 402 403 // Wallet needs to be re-created to generate a conflicting transaction 404 wallet = newWallet(t, vm, walletConfig{}) 405 addSubnetTx2, err := wallet.IssueCreateSubnetTx( 406 owners1, 407 walletcommon.WithCustomAddresses(set.Of( 408 addr0, 409 )), 410 ) 411 require.NoError(err) 412 413 preferredID := vm.manager.Preferred() 414 preferred, err := vm.manager.GetBlock(preferredID) 415 require.NoError(err) 416 preferredChainTime := preferred.Timestamp() 417 preferredHeight := preferred.Height() 418 419 statelessStandardBlk, err := block.NewBanffStandardBlock( 420 preferredChainTime, 421 preferredID, 422 preferredHeight+1, 423 []*txs.Tx{addSubnetTx0}, 424 ) 425 require.NoError(err) 426 addSubnetBlk0 := vm.manager.NewBlock(statelessStandardBlk) 427 428 statelessStandardBlk, err = block.NewBanffStandardBlock( 429 preferredChainTime, 430 preferredID, 431 preferredHeight+1, 432 []*txs.Tx{addSubnetTx1}, 433 ) 434 require.NoError(err) 435 addSubnetBlk1 := vm.manager.NewBlock(statelessStandardBlk) 436 437 statelessStandardBlk, err = block.NewBanffStandardBlock( 438 preferredChainTime, 439 addSubnetBlk1.ID(), 440 preferredHeight+2, 441 []*txs.Tx{addSubnetTx2}, 442 ) 443 require.NoError(err) 444 addSubnetBlk2 := vm.manager.NewBlock(statelessStandardBlk) 445 446 _, err = vm.ParseBlock(context.Background(), addSubnetBlk0.Bytes()) 447 require.NoError(err) 448 449 _, err = vm.ParseBlock(context.Background(), addSubnetBlk1.Bytes()) 450 require.NoError(err) 451 452 _, err = vm.ParseBlock(context.Background(), addSubnetBlk2.Bytes()) 453 require.NoError(err) 454 455 require.NoError(addSubnetBlk0.Verify(context.Background())) 456 require.NoError(addSubnetBlk0.Accept(context.Background())) 457 458 // Doesn't matter what verify returns as long as it's not panicking. 459 _ = addSubnetBlk2.Verify(context.Background()) 460 } 461 462 func TestRejectedStateRegressionInvalidValidatorTimestamp(t *testing.T) { 463 require := require.New(t) 464 465 vm, baseDB, mutableSharedMemory := defaultVM(t, upgradetest.Cortina) 466 vm.ctx.Lock.Lock() 467 defer vm.ctx.Lock.Unlock() 468 469 wallet := newWallet(t, vm, walletConfig{}) 470 471 nodeID := ids.GenerateTestNodeID() 472 newValidatorStartTime := vm.clock.Time().Add(executor.SyncBound).Add(1 * time.Second) 473 newValidatorEndTime := newValidatorStartTime.Add(defaultMinStakingDuration) 474 475 // Create the tx to add a new validator 476 addValidatorTx, err := wallet.IssueAddValidatorTx( 477 &txs.Validator{ 478 NodeID: nodeID, 479 Start: uint64(newValidatorStartTime.Unix()), 480 End: uint64(newValidatorEndTime.Unix()), 481 Wght: vm.MinValidatorStake, 482 }, 483 &secp256k1fx.OutputOwners{ 484 Threshold: 1, 485 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 486 }, 487 reward.PercentDenominator, 488 ) 489 require.NoError(err) 490 491 // Create the standard block to add the new validator 492 preferredID := vm.manager.Preferred() 493 preferred, err := vm.manager.GetBlock(preferredID) 494 require.NoError(err) 495 preferredChainTime := preferred.Timestamp() 496 preferredHeight := preferred.Height() 497 498 statelessBlk, err := block.NewBanffStandardBlock( 499 preferredChainTime, 500 preferredID, 501 preferredHeight+1, 502 []*txs.Tx{addValidatorTx}, 503 ) 504 require.NoError(err) 505 506 addValidatorStandardBlk := vm.manager.NewBlock(statelessBlk) 507 require.NoError(addValidatorStandardBlk.Verify(context.Background())) 508 509 // Verify that the new validator now in pending validator set 510 { 511 onAccept, found := vm.manager.GetState(addValidatorStandardBlk.ID()) 512 require.True(found) 513 514 _, err := onAccept.GetPendingValidator(constants.PrimaryNetworkID, nodeID) 515 require.NoError(err) 516 } 517 518 // Create the UTXO that will be added to shared memory 519 utxo := &avax.UTXO{ 520 UTXOID: avax.UTXOID{ 521 TxID: ids.GenerateTestID(), 522 }, 523 Asset: avax.Asset{ 524 ID: vm.ctx.AVAXAssetID, 525 }, 526 Out: &secp256k1fx.TransferOutput{ 527 Amt: vm.StaticFeeConfig.TxFee, 528 OutputOwners: secp256k1fx.OutputOwners{}, 529 }, 530 } 531 532 // Create the import tx that will fail verification 533 unsignedImportTx := &txs.ImportTx{ 534 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 535 NetworkID: vm.ctx.NetworkID, 536 BlockchainID: vm.ctx.ChainID, 537 }}, 538 SourceChain: vm.ctx.XChainID, 539 ImportedInputs: []*avax.TransferableInput{ 540 { 541 UTXOID: utxo.UTXOID, 542 Asset: utxo.Asset, 543 In: &secp256k1fx.TransferInput{ 544 Amt: vm.StaticFeeConfig.TxFee, 545 }, 546 }, 547 }, 548 } 549 signedImportTx := &txs.Tx{Unsigned: unsignedImportTx} 550 require.NoError(signedImportTx.Sign(txs.Codec, [][]*secp256k1.PrivateKey{ 551 {}, // There is one input, with no required signers 552 })) 553 554 // Create the standard block that will fail verification, and then be 555 // re-verified. 556 preferredChainTime = addValidatorStandardBlk.Timestamp() 557 preferredID = addValidatorStandardBlk.ID() 558 preferredHeight = addValidatorStandardBlk.Height() 559 560 statelessImportBlk, err := block.NewBanffStandardBlock( 561 preferredChainTime, 562 preferredID, 563 preferredHeight+1, 564 []*txs.Tx{signedImportTx}, 565 ) 566 require.NoError(err) 567 568 importBlk := vm.manager.NewBlock(statelessImportBlk) 569 570 // Because the shared memory UTXO hasn't been populated, this block is 571 // currently invalid. 572 err = importBlk.Verify(context.Background()) 573 require.ErrorIs(err, database.ErrNotFound) 574 575 // Populate the shared memory UTXO. 576 m := atomic.NewMemory(prefixdb.New([]byte{5}, baseDB)) 577 578 mutableSharedMemory.SharedMemory = m.NewSharedMemory(vm.ctx.ChainID) 579 peerSharedMemory := m.NewSharedMemory(vm.ctx.XChainID) 580 581 utxoBytes, err := txs.Codec.Marshal(txs.CodecVersion, utxo) 582 require.NoError(err) 583 584 inputID := utxo.InputID() 585 require.NoError(peerSharedMemory.Apply( 586 map[ids.ID]*atomic.Requests{ 587 vm.ctx.ChainID: { 588 PutRequests: []*atomic.Element{ 589 { 590 Key: inputID[:], 591 Value: utxoBytes, 592 }, 593 }, 594 }, 595 }, 596 )) 597 598 // Because the shared memory UTXO has now been populated, the block should 599 // pass verification. 600 require.NoError(importBlk.Verify(context.Background())) 601 602 // Move chain time ahead to bring the new validator from the pending 603 // validator set into the current validator set. 604 vm.clock.Set(newValidatorStartTime) 605 606 // Create the proposal block that should have moved the new validator from 607 // the pending validator set into the current validator set. 608 preferredID = importBlk.ID() 609 preferredHeight = importBlk.Height() 610 611 statelessAdvanceTimeStandardBlk, err := block.NewBanffStandardBlock( 612 newValidatorStartTime, 613 preferredID, 614 preferredHeight+1, 615 nil, 616 ) 617 require.NoError(err) 618 619 advanceTimeStandardBlk := vm.manager.NewBlock(statelessAdvanceTimeStandardBlk) 620 require.NoError(advanceTimeStandardBlk.Verify(context.Background())) 621 622 // Accept all the blocks 623 allBlocks := []snowman.Block{ 624 addValidatorStandardBlk, 625 importBlk, 626 advanceTimeStandardBlk, 627 } 628 for _, blk := range allBlocks { 629 require.NoError(blk.Accept(context.Background())) 630 } 631 632 // Force a reload of the state from the database. 633 vm.Config.Validators = validators.NewManager() 634 newState := statetest.New(t, statetest.Config{ 635 DB: vm.db, 636 Validators: vm.Config.Validators, 637 Upgrades: vm.Config.UpgradeConfig, 638 Context: vm.ctx, 639 Rewards: reward.NewCalculator(vm.Config.RewardConfig), 640 }) 641 642 // Verify that new validator is now in the current validator set. 643 { 644 _, err := newState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 645 require.NoError(err) 646 647 _, err = newState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) 648 require.ErrorIs(err, database.ErrNotFound) 649 650 currentTimestamp := newState.GetTimestamp() 651 require.Equal(newValidatorStartTime.Unix(), currentTimestamp.Unix()) 652 } 653 } 654 655 func TestRejectedStateRegressionInvalidValidatorReward(t *testing.T) { 656 require := require.New(t) 657 658 vm, baseDB, mutableSharedMemory := defaultVM(t, upgradetest.Cortina) 659 vm.ctx.Lock.Lock() 660 defer vm.ctx.Lock.Unlock() 661 662 vm.state.SetCurrentSupply(constants.PrimaryNetworkID, defaultRewardConfig.SupplyCap/2) 663 664 wallet := newWallet(t, vm, walletConfig{}) 665 666 nodeID0 := ids.GenerateTestNodeID() 667 newValidatorStartTime0 := vm.clock.Time().Add(executor.SyncBound).Add(1 * time.Second) 668 newValidatorEndTime0 := newValidatorStartTime0.Add(defaultMaxStakingDuration) 669 670 rewardsOwner := &secp256k1fx.OutputOwners{ 671 Threshold: 1, 672 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 673 } 674 675 // Create the tx to add the first new validator 676 addValidatorTx0, err := wallet.IssueAddValidatorTx( 677 &txs.Validator{ 678 NodeID: nodeID0, 679 Start: uint64(newValidatorStartTime0.Unix()), 680 End: uint64(newValidatorEndTime0.Unix()), 681 Wght: vm.MaxValidatorStake, 682 }, 683 rewardsOwner, 684 reward.PercentDenominator, 685 ) 686 require.NoError(err) 687 688 // Create the standard block to add the first new validator 689 preferredID := vm.manager.Preferred() 690 preferred, err := vm.manager.GetBlock(preferredID) 691 require.NoError(err) 692 preferredChainTime := preferred.Timestamp() 693 preferredHeight := preferred.Height() 694 695 statelessAddValidatorStandardBlk0, err := block.NewBanffStandardBlock( 696 preferredChainTime, 697 preferredID, 698 preferredHeight+1, 699 []*txs.Tx{addValidatorTx0}, 700 ) 701 require.NoError(err) 702 703 addValidatorStandardBlk0 := vm.manager.NewBlock(statelessAddValidatorStandardBlk0) 704 require.NoError(addValidatorStandardBlk0.Verify(context.Background())) 705 706 // Verify that first new validator now in pending validator set 707 { 708 onAccept, ok := vm.manager.GetState(addValidatorStandardBlk0.ID()) 709 require.True(ok) 710 711 _, err := onAccept.GetPendingValidator(constants.PrimaryNetworkID, nodeID0) 712 require.NoError(err) 713 } 714 715 // Move chain time to bring the first new validator from the pending 716 // validator set into the current validator set. 717 vm.clock.Set(newValidatorStartTime0) 718 719 // Create the proposal block that moves the first new validator from the 720 // pending validator set into the current validator set. 721 preferredID = addValidatorStandardBlk0.ID() 722 preferredHeight = addValidatorStandardBlk0.Height() 723 724 statelessAdvanceTimeStandardBlk0, err := block.NewBanffStandardBlock( 725 newValidatorStartTime0, 726 preferredID, 727 preferredHeight+1, 728 nil, 729 ) 730 require.NoError(err) 731 732 advanceTimeStandardBlk0 := vm.manager.NewBlock(statelessAdvanceTimeStandardBlk0) 733 require.NoError(advanceTimeStandardBlk0.Verify(context.Background())) 734 735 // Verify that the first new validator is now in the current validator set. 736 { 737 onAccept, ok := vm.manager.GetState(advanceTimeStandardBlk0.ID()) 738 require.True(ok) 739 740 _, err := onAccept.GetCurrentValidator(constants.PrimaryNetworkID, nodeID0) 741 require.NoError(err) 742 743 _, err = onAccept.GetPendingValidator(constants.PrimaryNetworkID, nodeID0) 744 require.ErrorIs(err, database.ErrNotFound) 745 746 currentTimestamp := onAccept.GetTimestamp() 747 require.Equal(newValidatorStartTime0.Unix(), currentTimestamp.Unix()) 748 } 749 750 // Create the UTXO that will be added to shared memory 751 utxo := &avax.UTXO{ 752 UTXOID: avax.UTXOID{ 753 TxID: ids.GenerateTestID(), 754 }, 755 Asset: avax.Asset{ 756 ID: vm.ctx.AVAXAssetID, 757 }, 758 Out: &secp256k1fx.TransferOutput{ 759 Amt: vm.StaticFeeConfig.TxFee, 760 OutputOwners: secp256k1fx.OutputOwners{}, 761 }, 762 } 763 764 // Create the import tx that will fail verification 765 unsignedImportTx := &txs.ImportTx{ 766 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 767 NetworkID: vm.ctx.NetworkID, 768 BlockchainID: vm.ctx.ChainID, 769 }}, 770 SourceChain: vm.ctx.XChainID, 771 ImportedInputs: []*avax.TransferableInput{ 772 { 773 UTXOID: utxo.UTXOID, 774 Asset: utxo.Asset, 775 In: &secp256k1fx.TransferInput{ 776 Amt: vm.StaticFeeConfig.TxFee, 777 }, 778 }, 779 }, 780 } 781 signedImportTx := &txs.Tx{Unsigned: unsignedImportTx} 782 require.NoError(signedImportTx.Sign(txs.Codec, [][]*secp256k1.PrivateKey{ 783 {}, // There is one input, with no required signers 784 })) 785 786 // Create the standard block that will fail verification, and then be 787 // re-verified. 788 preferredChainTime = advanceTimeStandardBlk0.Timestamp() 789 preferredID = advanceTimeStandardBlk0.ID() 790 preferredHeight = advanceTimeStandardBlk0.Height() 791 792 statelessImportBlk, err := block.NewBanffStandardBlock( 793 preferredChainTime, 794 preferredID, 795 preferredHeight+1, 796 []*txs.Tx{signedImportTx}, 797 ) 798 require.NoError(err) 799 800 importBlk := vm.manager.NewBlock(statelessImportBlk) 801 // Because the shared memory UTXO hasn't been populated, this block is 802 // currently invalid. 803 err = importBlk.Verify(context.Background()) 804 require.ErrorIs(err, database.ErrNotFound) 805 806 // Populate the shared memory UTXO. 807 m := atomic.NewMemory(prefixdb.New([]byte{5}, baseDB)) 808 809 mutableSharedMemory.SharedMemory = m.NewSharedMemory(vm.ctx.ChainID) 810 peerSharedMemory := m.NewSharedMemory(vm.ctx.XChainID) 811 812 utxoBytes, err := txs.Codec.Marshal(txs.CodecVersion, utxo) 813 require.NoError(err) 814 815 inputID := utxo.InputID() 816 require.NoError(peerSharedMemory.Apply( 817 map[ids.ID]*atomic.Requests{ 818 vm.ctx.ChainID: { 819 PutRequests: []*atomic.Element{ 820 { 821 Key: inputID[:], 822 Value: utxoBytes, 823 }, 824 }, 825 }, 826 }, 827 )) 828 829 // Because the shared memory UTXO has now been populated, the block should 830 // pass verification. 831 require.NoError(importBlk.Verify(context.Background())) 832 833 newValidatorStartTime1 := newValidatorStartTime0.Add(executor.SyncBound).Add(1 * time.Second) 834 newValidatorEndTime1 := newValidatorStartTime1.Add(defaultMaxStakingDuration) 835 836 nodeID1 := ids.GenerateTestNodeID() 837 838 // Create the tx to add the second new validator 839 addValidatorTx1, err := wallet.IssueAddValidatorTx( 840 &txs.Validator{ 841 NodeID: nodeID1, 842 Start: uint64(newValidatorStartTime1.Unix()), 843 End: uint64(newValidatorEndTime1.Unix()), 844 Wght: vm.MaxValidatorStake, 845 }, 846 rewardsOwner, 847 reward.PercentDenominator, 848 ) 849 require.NoError(err) 850 851 // Create the standard block to add the second new validator 852 preferredChainTime = importBlk.Timestamp() 853 preferredID = importBlk.ID() 854 preferredHeight = importBlk.Height() 855 856 statelessAddValidatorStandardBlk1, err := block.NewBanffStandardBlock( 857 preferredChainTime, 858 preferredID, 859 preferredHeight+1, 860 []*txs.Tx{addValidatorTx1}, 861 ) 862 require.NoError(err) 863 864 addValidatorStandardBlk1 := vm.manager.NewBlock(statelessAddValidatorStandardBlk1) 865 866 require.NoError(addValidatorStandardBlk1.Verify(context.Background())) 867 868 // Verify that the second new validator now in pending validator set 869 { 870 onAccept, ok := vm.manager.GetState(addValidatorStandardBlk1.ID()) 871 require.True(ok) 872 873 _, err := onAccept.GetPendingValidator(constants.PrimaryNetworkID, nodeID1) 874 require.NoError(err) 875 } 876 877 // Move chain time to bring the second new validator from the pending 878 // validator set into the current validator set. 879 vm.clock.Set(newValidatorStartTime1) 880 881 // Create the proposal block that moves the second new validator from the 882 // pending validator set into the current validator set. 883 preferredID = addValidatorStandardBlk1.ID() 884 preferredHeight = addValidatorStandardBlk1.Height() 885 886 statelessAdvanceTimeStandardBlk1, err := block.NewBanffStandardBlock( 887 newValidatorStartTime1, 888 preferredID, 889 preferredHeight+1, 890 nil, 891 ) 892 require.NoError(err) 893 894 advanceTimeStandardBlk1 := vm.manager.NewBlock(statelessAdvanceTimeStandardBlk1) 895 require.NoError(advanceTimeStandardBlk1.Verify(context.Background())) 896 897 // Verify that the second new validator is now in the current validator set. 898 { 899 onAccept, ok := vm.manager.GetState(advanceTimeStandardBlk1.ID()) 900 require.True(ok) 901 902 _, err := onAccept.GetCurrentValidator(constants.PrimaryNetworkID, nodeID1) 903 require.NoError(err) 904 905 _, err = onAccept.GetPendingValidator(constants.PrimaryNetworkID, nodeID1) 906 require.ErrorIs(err, database.ErrNotFound) 907 908 currentTimestamp := onAccept.GetTimestamp() 909 require.Equal(newValidatorStartTime1.Unix(), currentTimestamp.Unix()) 910 } 911 912 // Accept all the blocks 913 allBlocks := []snowman.Block{ 914 addValidatorStandardBlk0, 915 advanceTimeStandardBlk0, 916 importBlk, 917 addValidatorStandardBlk1, 918 advanceTimeStandardBlk1, 919 } 920 for _, blk := range allBlocks { 921 require.NoError(blk.Accept(context.Background())) 922 } 923 924 // Force a reload of the state from the database. 925 vm.Config.Validators = validators.NewManager() 926 newState := statetest.New(t, statetest.Config{ 927 DB: vm.db, 928 Validators: vm.Config.Validators, 929 Upgrades: vm.Config.UpgradeConfig, 930 Context: vm.ctx, 931 Rewards: reward.NewCalculator(vm.Config.RewardConfig), 932 }) 933 934 // Verify that validators are in the current validator set with the correct 935 // reward calculated. 936 { 937 staker0, err := newState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID0) 938 require.NoError(err) 939 require.Equal(uint64(60000000), staker0.PotentialReward) 940 941 staker1, err := newState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID1) 942 require.NoError(err) 943 require.Equal(uint64(59999999), staker1.PotentialReward) 944 945 _, err = newState.GetPendingValidator(constants.PrimaryNetworkID, nodeID0) 946 require.ErrorIs(err, database.ErrNotFound) 947 948 _, err = newState.GetPendingValidator(constants.PrimaryNetworkID, nodeID1) 949 require.ErrorIs(err, database.ErrNotFound) 950 951 currentTimestamp := newState.GetTimestamp() 952 require.Equal(newValidatorStartTime1.Unix(), currentTimestamp.Unix()) 953 } 954 } 955 956 func TestValidatorSetAtCacheOverwriteRegression(t *testing.T) { 957 require := require.New(t) 958 959 vm, _, _ := defaultVM(t, upgradetest.Cortina) 960 vm.ctx.Lock.Lock() 961 defer vm.ctx.Lock.Unlock() 962 963 currentHeight, err := vm.GetCurrentHeight(context.Background()) 964 require.NoError(err) 965 require.Equal(uint64(1), currentHeight) 966 967 expectedValidators1 := map[ids.NodeID]uint64{ 968 genesistest.DefaultNodeIDs[0]: genesistest.DefaultValidatorWeight, 969 genesistest.DefaultNodeIDs[1]: genesistest.DefaultValidatorWeight, 970 genesistest.DefaultNodeIDs[2]: genesistest.DefaultValidatorWeight, 971 genesistest.DefaultNodeIDs[3]: genesistest.DefaultValidatorWeight, 972 genesistest.DefaultNodeIDs[4]: genesistest.DefaultValidatorWeight, 973 } 974 validators, err := vm.GetValidatorSet(context.Background(), 1, constants.PrimaryNetworkID) 975 require.NoError(err) 976 for nodeID, weight := range expectedValidators1 { 977 require.Equal(weight, validators[nodeID].Weight) 978 } 979 980 wallet := newWallet(t, vm, walletConfig{}) 981 982 newValidatorStartTime0 := vm.clock.Time().Add(executor.SyncBound).Add(1 * time.Second) 983 newValidatorEndTime0 := newValidatorStartTime0.Add(defaultMaxStakingDuration) 984 985 extraNodeID := ids.GenerateTestNodeID() 986 987 // Create the tx to add the first new validator 988 addValidatorTx0, err := wallet.IssueAddValidatorTx( 989 &txs.Validator{ 990 NodeID: extraNodeID, 991 Start: uint64(newValidatorStartTime0.Unix()), 992 End: uint64(newValidatorEndTime0.Unix()), 993 Wght: vm.MaxValidatorStake, 994 }, 995 &secp256k1fx.OutputOwners{ 996 Threshold: 1, 997 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 998 }, 999 reward.PercentDenominator, 1000 ) 1001 require.NoError(err) 1002 1003 // Create the standard block to add the first new validator 1004 preferredID := vm.manager.Preferred() 1005 preferred, err := vm.manager.GetBlock(preferredID) 1006 require.NoError(err) 1007 preferredChainTime := preferred.Timestamp() 1008 preferredHeight := preferred.Height() 1009 1010 statelessStandardBlk, err := block.NewBanffStandardBlock( 1011 preferredChainTime, 1012 preferredID, 1013 preferredHeight+1, 1014 []*txs.Tx{addValidatorTx0}, 1015 ) 1016 require.NoError(err) 1017 addValidatorProposalBlk0 := vm.manager.NewBlock(statelessStandardBlk) 1018 require.NoError(addValidatorProposalBlk0.Verify(context.Background())) 1019 require.NoError(addValidatorProposalBlk0.Accept(context.Background())) 1020 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1021 1022 currentHeight, err = vm.GetCurrentHeight(context.Background()) 1023 require.NoError(err) 1024 require.Equal(uint64(2), currentHeight) 1025 1026 for i := uint64(1); i <= 2; i++ { 1027 validators, err = vm.GetValidatorSet(context.Background(), i, constants.PrimaryNetworkID) 1028 require.NoError(err) 1029 for nodeID, weight := range expectedValidators1 { 1030 require.Equal(weight, validators[nodeID].Weight) 1031 } 1032 } 1033 1034 // Advance chain time to move the first new validator from the pending 1035 // validator set into the current validator set. 1036 vm.clock.Set(newValidatorStartTime0) 1037 1038 // Create the standard block that moves the first new validator from the 1039 // pending validator set into the current validator set. 1040 preferredID = vm.manager.Preferred() 1041 preferred, err = vm.manager.GetBlock(preferredID) 1042 require.NoError(err) 1043 preferredID = preferred.ID() 1044 preferredHeight = preferred.Height() 1045 1046 statelessStandardBlk, err = block.NewBanffStandardBlock( 1047 newValidatorStartTime0, 1048 preferredID, 1049 preferredHeight+1, 1050 nil, 1051 ) 1052 require.NoError(err) 1053 advanceTimeProposalBlk0 := vm.manager.NewBlock(statelessStandardBlk) 1054 require.NoError(advanceTimeProposalBlk0.Verify(context.Background())) 1055 require.NoError(advanceTimeProposalBlk0.Accept(context.Background())) 1056 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1057 1058 currentHeight, err = vm.GetCurrentHeight(context.Background()) 1059 require.NoError(err) 1060 require.Equal(uint64(3), currentHeight) 1061 1062 for i := uint64(1); i <= 2; i++ { 1063 validators, err = vm.GetValidatorSet(context.Background(), i, constants.PrimaryNetworkID) 1064 require.NoError(err) 1065 for nodeID, weight := range expectedValidators1 { 1066 require.Equal(weight, validators[nodeID].Weight) 1067 } 1068 } 1069 1070 expectedValidators2 := map[ids.NodeID]uint64{ 1071 genesistest.DefaultNodeIDs[0]: genesistest.DefaultValidatorWeight, 1072 genesistest.DefaultNodeIDs[1]: genesistest.DefaultValidatorWeight, 1073 genesistest.DefaultNodeIDs[2]: genesistest.DefaultValidatorWeight, 1074 genesistest.DefaultNodeIDs[3]: genesistest.DefaultValidatorWeight, 1075 genesistest.DefaultNodeIDs[4]: genesistest.DefaultValidatorWeight, 1076 extraNodeID: vm.MaxValidatorStake, 1077 } 1078 validators, err = vm.GetValidatorSet(context.Background(), 3, constants.PrimaryNetworkID) 1079 require.NoError(err) 1080 for nodeID, weight := range expectedValidators2 { 1081 require.Equal(weight, validators[nodeID].Weight) 1082 } 1083 } 1084 1085 func TestAddDelegatorTxAddBeforeRemove(t *testing.T) { 1086 require := require.New(t) 1087 1088 validatorStartTime := latestForkTime.Add(executor.SyncBound).Add(1 * time.Second) 1089 validatorEndTime := validatorStartTime.Add(360 * 24 * time.Hour) 1090 validatorStake := defaultMaxValidatorStake / 5 1091 1092 delegator1StartTime := validatorStartTime 1093 delegator1EndTime := delegator1StartTime.Add(3 * defaultMinStakingDuration) 1094 delegator1Stake := defaultMaxValidatorStake - validatorStake 1095 1096 delegator2StartTime := delegator1EndTime 1097 delegator2EndTime := delegator2StartTime.Add(3 * defaultMinStakingDuration) 1098 delegator2Stake := defaultMaxValidatorStake - validatorStake 1099 1100 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1101 vm.ctx.Lock.Lock() 1102 defer vm.ctx.Lock.Unlock() 1103 1104 wallet := newWallet(t, vm, walletConfig{}) 1105 1106 nodeID := ids.GenerateTestNodeID() 1107 rewardsOwner := &secp256k1fx.OutputOwners{ 1108 Threshold: 1, 1109 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1110 } 1111 1112 // create valid tx 1113 addValidatorTx, err := wallet.IssueAddValidatorTx( 1114 &txs.Validator{ 1115 NodeID: nodeID, 1116 Start: uint64(validatorStartTime.Unix()), 1117 End: uint64(validatorEndTime.Unix()), 1118 Wght: validatorStake, 1119 }, 1120 rewardsOwner, 1121 reward.PercentDenominator, 1122 ) 1123 require.NoError(err) 1124 1125 // issue the add validator tx 1126 vm.ctx.Lock.Unlock() 1127 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 1128 vm.ctx.Lock.Lock() 1129 1130 // Accept addValidatorTx 1131 require.NoError(buildAndAcceptStandardBlock(vm)) 1132 1133 // create valid tx 1134 addFirstDelegatorTx, err := wallet.IssueAddDelegatorTx( 1135 &txs.Validator{ 1136 NodeID: nodeID, 1137 Start: uint64(delegator1StartTime.Unix()), 1138 End: uint64(delegator1EndTime.Unix()), 1139 Wght: delegator1Stake, 1140 }, 1141 rewardsOwner, 1142 ) 1143 require.NoError(err) 1144 1145 // issue the first add delegator tx 1146 vm.ctx.Lock.Unlock() 1147 require.NoError(vm.issueTxFromRPC(addFirstDelegatorTx)) 1148 vm.ctx.Lock.Lock() 1149 1150 // Accept addFirstDelegatorTx 1151 require.NoError(buildAndAcceptStandardBlock(vm)) 1152 1153 // create invalid tx 1154 addSecondDelegatorTx, err := wallet.IssueAddDelegatorTx( 1155 &txs.Validator{ 1156 NodeID: nodeID, 1157 Start: uint64(delegator2StartTime.Unix()), 1158 End: uint64(delegator2EndTime.Unix()), 1159 Wght: delegator2Stake, 1160 }, 1161 rewardsOwner, 1162 ) 1163 require.NoError(err) 1164 1165 // attempting to issue the second add delegator tx should fail because the 1166 // total stake weight would go over the limit. 1167 vm.ctx.Lock.Unlock() 1168 err = vm.issueTxFromRPC(addSecondDelegatorTx) 1169 require.ErrorIs(err, executor.ErrOverDelegated) 1170 vm.ctx.Lock.Lock() 1171 } 1172 1173 func TestRemovePermissionedValidatorDuringPendingToCurrentTransitionNotTracked(t *testing.T) { 1174 require := require.New(t) 1175 1176 validatorStartTime := latestForkTime.Add(executor.SyncBound).Add(1 * time.Second) 1177 validatorEndTime := validatorStartTime.Add(360 * 24 * time.Hour) 1178 1179 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1180 vm.ctx.Lock.Lock() 1181 defer vm.ctx.Lock.Unlock() 1182 1183 wallet := newWallet(t, vm, walletConfig{}) 1184 1185 nodeID := ids.GenerateTestNodeID() 1186 addValidatorTx, err := wallet.IssueAddValidatorTx( 1187 &txs.Validator{ 1188 NodeID: nodeID, 1189 Start: uint64(validatorStartTime.Unix()), 1190 End: uint64(validatorEndTime.Unix()), 1191 Wght: defaultMaxValidatorStake, 1192 }, 1193 &secp256k1fx.OutputOwners{ 1194 Threshold: 1, 1195 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1196 }, 1197 reward.PercentDenominator, 1198 ) 1199 require.NoError(err) 1200 1201 vm.ctx.Lock.Unlock() 1202 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 1203 vm.ctx.Lock.Lock() 1204 1205 // Accept addValidatorTx 1206 require.NoError(buildAndAcceptStandardBlock(vm)) 1207 1208 createSubnetTx, err := wallet.IssueCreateSubnetTx( 1209 &secp256k1fx.OutputOwners{ 1210 Threshold: 1, 1211 Addrs: []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()}, 1212 }, 1213 ) 1214 require.NoError(err) 1215 1216 vm.ctx.Lock.Unlock() 1217 require.NoError(vm.issueTxFromRPC(createSubnetTx)) 1218 vm.ctx.Lock.Lock() 1219 1220 // Accept createSubnetTx 1221 require.NoError(buildAndAcceptStandardBlock(vm)) 1222 1223 subnetID := createSubnetTx.ID() 1224 addSubnetValidatorTx, err := wallet.IssueAddSubnetValidatorTx( 1225 &txs.SubnetValidator{ 1226 Validator: txs.Validator{ 1227 NodeID: nodeID, 1228 Start: uint64(validatorStartTime.Unix()), 1229 End: uint64(validatorEndTime.Unix()), 1230 Wght: defaultMaxValidatorStake, 1231 }, 1232 Subnet: subnetID, 1233 }, 1234 ) 1235 require.NoError(err) 1236 1237 vm.ctx.Lock.Unlock() 1238 require.NoError(vm.issueTxFromRPC(addSubnetValidatorTx)) 1239 vm.ctx.Lock.Lock() 1240 1241 // Accept addSubnetValidatorTx 1242 require.NoError(buildAndAcceptStandardBlock(vm)) 1243 1244 addSubnetValidatorHeight, err := vm.GetCurrentHeight(context.Background()) 1245 require.NoError(err) 1246 1247 emptyValidatorSet, err := vm.GetValidatorSet( 1248 context.Background(), 1249 addSubnetValidatorHeight, 1250 subnetID, 1251 ) 1252 require.NoError(err) 1253 require.Empty(emptyValidatorSet) 1254 1255 removeSubnetValidatorTx, err := wallet.IssueRemoveSubnetValidatorTx( 1256 nodeID, 1257 subnetID, 1258 ) 1259 require.NoError(err) 1260 1261 // Set the clock so that the validator will be moved from the pending 1262 // validator set into the current validator set. 1263 vm.clock.Set(validatorStartTime) 1264 1265 vm.ctx.Lock.Unlock() 1266 require.NoError(vm.issueTxFromRPC(removeSubnetValidatorTx)) 1267 vm.ctx.Lock.Lock() 1268 1269 // Accept removeSubnetValidatorTx 1270 require.NoError(buildAndAcceptStandardBlock(vm)) 1271 1272 emptyValidatorSet, err = vm.GetValidatorSet( 1273 context.Background(), 1274 addSubnetValidatorHeight, 1275 subnetID, 1276 ) 1277 require.NoError(err) 1278 require.Empty(emptyValidatorSet) 1279 } 1280 1281 func TestRemovePermissionedValidatorDuringPendingToCurrentTransitionTracked(t *testing.T) { 1282 require := require.New(t) 1283 1284 validatorStartTime := latestForkTime.Add(executor.SyncBound).Add(1 * time.Second) 1285 validatorEndTime := validatorStartTime.Add(360 * 24 * time.Hour) 1286 1287 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1288 vm.ctx.Lock.Lock() 1289 defer vm.ctx.Lock.Unlock() 1290 1291 wallet := newWallet(t, vm, walletConfig{}) 1292 1293 nodeID := ids.GenerateTestNodeID() 1294 addValidatorTx, err := wallet.IssueAddValidatorTx( 1295 &txs.Validator{ 1296 NodeID: nodeID, 1297 Start: uint64(validatorStartTime.Unix()), 1298 End: uint64(validatorEndTime.Unix()), 1299 Wght: defaultMaxValidatorStake, 1300 }, 1301 &secp256k1fx.OutputOwners{ 1302 Threshold: 1, 1303 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1304 }, 1305 reward.PercentDenominator, 1306 ) 1307 require.NoError(err) 1308 1309 vm.ctx.Lock.Unlock() 1310 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 1311 vm.ctx.Lock.Lock() 1312 1313 // Accept addValidatorTx 1314 require.NoError(buildAndAcceptStandardBlock(vm)) 1315 1316 createSubnetTx, err := wallet.IssueCreateSubnetTx( 1317 &secp256k1fx.OutputOwners{ 1318 Threshold: 1, 1319 Addrs: []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()}, 1320 }, 1321 ) 1322 require.NoError(err) 1323 1324 vm.ctx.Lock.Unlock() 1325 require.NoError(vm.issueTxFromRPC(createSubnetTx)) 1326 vm.ctx.Lock.Lock() 1327 1328 // Accept createSubnetTx 1329 require.NoError(buildAndAcceptStandardBlock(vm)) 1330 1331 subnetID := createSubnetTx.ID() 1332 addSubnetValidatorTx, err := wallet.IssueAddSubnetValidatorTx( 1333 &txs.SubnetValidator{ 1334 Validator: txs.Validator{ 1335 NodeID: nodeID, 1336 Start: uint64(validatorStartTime.Unix()), 1337 End: uint64(validatorEndTime.Unix()), 1338 Wght: defaultMaxValidatorStake, 1339 }, 1340 Subnet: subnetID, 1341 }, 1342 ) 1343 require.NoError(err) 1344 1345 vm.ctx.Lock.Unlock() 1346 require.NoError(vm.issueTxFromRPC(addSubnetValidatorTx)) 1347 vm.ctx.Lock.Lock() 1348 1349 // Accept addSubnetValidatorTx 1350 require.NoError(buildAndAcceptStandardBlock(vm)) 1351 1352 removeSubnetValidatorTx, err := wallet.IssueRemoveSubnetValidatorTx( 1353 nodeID, 1354 subnetID, 1355 ) 1356 require.NoError(err) 1357 1358 // Set the clock so that the validator will be moved from the pending 1359 // validator set into the current validator set. 1360 vm.clock.Set(validatorStartTime) 1361 1362 vm.ctx.Lock.Unlock() 1363 require.NoError(vm.issueTxFromRPC(removeSubnetValidatorTx)) 1364 vm.ctx.Lock.Lock() 1365 1366 // Accept removeSubnetValidatorTx 1367 require.NoError(buildAndAcceptStandardBlock(vm)) 1368 } 1369 1370 func TestAddValidatorDuringRemoval(t *testing.T) { 1371 require := require.New(t) 1372 1373 vm, _, _ := defaultVM(t, upgradetest.Durango) 1374 vm.ctx.Lock.Lock() 1375 defer vm.ctx.Lock.Unlock() 1376 1377 var ( 1378 nodeID = genesistest.DefaultNodeIDs[0] 1379 subnetID = testSubnet1.ID() 1380 wallet = newWallet(t, vm, walletConfig{ 1381 subnetIDs: []ids.ID{subnetID}, 1382 }) 1383 1384 duration = defaultMinStakingDuration 1385 firstEndTime = latestForkTime.Add(duration) 1386 ) 1387 1388 firstAddSubnetValidatorTx, err := wallet.IssueAddSubnetValidatorTx(&txs.SubnetValidator{ 1389 Validator: txs.Validator{ 1390 NodeID: nodeID, 1391 End: uint64(firstEndTime.Unix()), 1392 Wght: 1, 1393 }, 1394 Subnet: subnetID, 1395 }) 1396 require.NoError(err) 1397 1398 vm.ctx.Lock.Unlock() 1399 require.NoError(vm.issueTxFromRPC(firstAddSubnetValidatorTx)) 1400 vm.ctx.Lock.Lock() 1401 1402 // Accept firstAddSubnetValidatorTx 1403 require.NoError(buildAndAcceptStandardBlock(vm)) 1404 1405 // Verify that the validator was added 1406 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 1407 require.NoError(err) 1408 1409 secondEndTime := firstEndTime.Add(duration) 1410 secondSubnetValidatorTx, err := wallet.IssueAddSubnetValidatorTx(&txs.SubnetValidator{ 1411 Validator: txs.Validator{ 1412 NodeID: nodeID, 1413 End: uint64(secondEndTime.Unix()), 1414 Wght: 1, 1415 }, 1416 Subnet: subnetID, 1417 }) 1418 require.NoError(err) 1419 1420 vm.clock.Set(firstEndTime) 1421 vm.ctx.Lock.Unlock() 1422 err = vm.issueTxFromRPC(secondSubnetValidatorTx) 1423 vm.ctx.Lock.Lock() 1424 require.ErrorIs(err, state.ErrAddingStakerAfterDeletion) 1425 1426 // Remove the first subnet validator 1427 require.NoError(buildAndAcceptStandardBlock(vm)) 1428 1429 // Verify that the validator does not exist 1430 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 1431 require.ErrorIs(err, database.ErrNotFound) 1432 1433 // Verify that the invalid transaction was not executed 1434 _, _, err = vm.state.GetTx(secondSubnetValidatorTx.ID()) 1435 require.ErrorIs(err, database.ErrNotFound) 1436 } 1437 1438 // GetValidatorSet must return the BLS keys for a given validator correctly when 1439 // queried at a previous height, even in case it has currently expired 1440 func TestSubnetValidatorBLSKeyDiffAfterExpiry(t *testing.T) { 1441 // setup 1442 require := require.New(t) 1443 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1444 vm.ctx.Lock.Lock() 1445 defer vm.ctx.Lock.Unlock() 1446 1447 subnetID := testSubnet1.TxID 1448 wallet := newWallet(t, vm, walletConfig{ 1449 subnetIDs: []ids.ID{subnetID}, 1450 }) 1451 1452 // A subnet validator stakes and then stops; also its primary network counterpart stops staking 1453 var ( 1454 primaryStartTime = genesistest.DefaultValidatorStartTime.Add(executor.SyncBound) 1455 subnetStartTime = primaryStartTime.Add(executor.SyncBound) 1456 subnetEndTime = subnetStartTime.Add(defaultMinStakingDuration) 1457 primaryEndTime = subnetEndTime.Add(time.Second) 1458 primaryReStartTime = primaryEndTime.Add(executor.SyncBound) 1459 primaryReEndTime = primaryReStartTime.Add(defaultMinStakingDuration) 1460 ) 1461 1462 // insert primary network validator 1463 var ( 1464 nodeID = ids.GenerateTestNodeID() 1465 rewardsOwner = &secp256k1fx.OutputOwners{ 1466 Threshold: 1, 1467 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1468 } 1469 ) 1470 sk1, err := bls.NewSecretKey() 1471 require.NoError(err) 1472 pk1 := bls.PublicFromSecretKey(sk1) 1473 1474 // build primary network validator with BLS key 1475 primaryTx, err := wallet.IssueAddPermissionlessValidatorTx( 1476 &txs.SubnetValidator{ 1477 Validator: txs.Validator{ 1478 NodeID: nodeID, 1479 Start: uint64(primaryStartTime.Unix()), 1480 End: uint64(primaryEndTime.Unix()), 1481 Wght: vm.MinValidatorStake, 1482 }, 1483 Subnet: constants.PrimaryNetworkID, 1484 }, 1485 signer.NewProofOfPossession(sk1), 1486 vm.ctx.AVAXAssetID, 1487 rewardsOwner, 1488 rewardsOwner, 1489 reward.PercentDenominator, 1490 ) 1491 require.NoError(err) 1492 1493 vm.ctx.Lock.Unlock() 1494 require.NoError(vm.issueTxFromRPC(primaryTx)) 1495 vm.ctx.Lock.Lock() 1496 require.NoError(buildAndAcceptStandardBlock(vm)) 1497 1498 // move time ahead, promoting primary validator to current 1499 vm.clock.Set(primaryStartTime) 1500 require.NoError(buildAndAcceptStandardBlock(vm)) 1501 1502 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1503 require.NoError(err) 1504 1505 primaryStartHeight, err := vm.GetCurrentHeight(context.Background()) 1506 require.NoError(err) 1507 1508 // insert the subnet validator 1509 subnetTx, err := wallet.IssueAddSubnetValidatorTx( 1510 &txs.SubnetValidator{ 1511 Validator: txs.Validator{ 1512 NodeID: nodeID, 1513 Start: uint64(subnetStartTime.Unix()), 1514 End: uint64(subnetEndTime.Unix()), 1515 Wght: 1, 1516 }, 1517 Subnet: subnetID, 1518 }, 1519 ) 1520 require.NoError(err) 1521 1522 vm.ctx.Lock.Unlock() 1523 require.NoError(vm.issueTxFromRPC(subnetTx)) 1524 vm.ctx.Lock.Lock() 1525 require.NoError(buildAndAcceptStandardBlock(vm)) 1526 1527 // move time ahead, promoting the subnet validator to current 1528 vm.clock.Set(subnetStartTime) 1529 require.NoError(buildAndAcceptStandardBlock(vm)) 1530 1531 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 1532 require.NoError(err) 1533 1534 subnetStartHeight, err := vm.GetCurrentHeight(context.Background()) 1535 require.NoError(err) 1536 1537 // move time ahead, terminating the subnet validator 1538 vm.clock.Set(subnetEndTime) 1539 require.NoError(buildAndAcceptStandardBlock(vm)) 1540 1541 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 1542 require.ErrorIs(err, database.ErrNotFound) 1543 1544 subnetEndHeight, err := vm.GetCurrentHeight(context.Background()) 1545 require.NoError(err) 1546 1547 // move time ahead, terminating primary network validator 1548 vm.clock.Set(primaryEndTime) 1549 blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator 1550 require.NoError(err) 1551 require.NoError(blk.Verify(context.Background())) 1552 1553 proposalBlk := blk.(snowman.OracleBlock) 1554 options, err := proposalBlk.Options(context.Background()) 1555 require.NoError(err) 1556 1557 commit := options[0].(*blockexecutor.Block) 1558 require.IsType(&block.BanffCommitBlock{}, commit.Block) 1559 1560 require.NoError(blk.Accept(context.Background())) 1561 require.NoError(commit.Verify(context.Background())) 1562 require.NoError(commit.Accept(context.Background())) 1563 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1564 1565 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1566 require.ErrorIs(err, database.ErrNotFound) 1567 1568 primaryEndHeight, err := vm.GetCurrentHeight(context.Background()) 1569 require.NoError(err) 1570 1571 // reinsert primary validator with a different BLS key 1572 sk2, err := bls.NewSecretKey() 1573 require.NoError(err) 1574 pk2 := bls.PublicFromSecretKey(sk2) 1575 1576 primaryRestartTx, err := wallet.IssueAddPermissionlessValidatorTx( 1577 &txs.SubnetValidator{ 1578 Validator: txs.Validator{ 1579 NodeID: nodeID, 1580 Start: uint64(primaryReStartTime.Unix()), 1581 End: uint64(primaryReEndTime.Unix()), 1582 Wght: vm.MinValidatorStake, 1583 }, 1584 Subnet: constants.PrimaryNetworkID, 1585 }, 1586 signer.NewProofOfPossession(sk2), 1587 vm.ctx.AVAXAssetID, 1588 rewardsOwner, 1589 rewardsOwner, 1590 reward.PercentDenominator, 1591 ) 1592 require.NoError(err) 1593 1594 vm.ctx.Lock.Unlock() 1595 require.NoError(vm.issueTxFromRPC(primaryRestartTx)) 1596 vm.ctx.Lock.Lock() 1597 require.NoError(buildAndAcceptStandardBlock(vm)) 1598 1599 // move time ahead, promoting restarted primary validator to current 1600 vm.clock.Set(primaryReStartTime) 1601 require.NoError(buildAndAcceptStandardBlock(vm)) 1602 1603 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1604 require.NoError(err) 1605 1606 primaryRestartHeight, err := vm.GetCurrentHeight(context.Background()) 1607 require.NoError(err) 1608 1609 // Show that validators are rebuilt with the right BLS key 1610 for height := primaryStartHeight; height < primaryEndHeight; height++ { 1611 require.NoError(checkValidatorBlsKeyIsSet( 1612 vm.State, 1613 nodeID, 1614 constants.PrimaryNetworkID, 1615 height, 1616 pk1, 1617 )) 1618 } 1619 for height := primaryEndHeight; height < primaryRestartHeight; height++ { 1620 err := checkValidatorBlsKeyIsSet( 1621 vm.State, 1622 nodeID, 1623 constants.PrimaryNetworkID, 1624 primaryEndHeight, 1625 pk1, 1626 ) 1627 require.ErrorIs(err, database.ErrNotFound) 1628 } 1629 require.NoError(checkValidatorBlsKeyIsSet( 1630 vm.State, 1631 nodeID, 1632 constants.PrimaryNetworkID, 1633 primaryRestartHeight, 1634 pk2, 1635 )) 1636 1637 for height := subnetStartHeight; height < subnetEndHeight; height++ { 1638 require.NoError(checkValidatorBlsKeyIsSet( 1639 vm.State, 1640 nodeID, 1641 subnetID, 1642 height, 1643 pk1, 1644 )) 1645 } 1646 1647 for height := subnetEndHeight; height <= primaryRestartHeight; height++ { 1648 err := checkValidatorBlsKeyIsSet( 1649 vm.State, 1650 nodeID, 1651 subnetID, 1652 primaryEndHeight, 1653 pk1, 1654 ) 1655 require.ErrorIs(err, database.ErrNotFound) 1656 } 1657 } 1658 1659 func TestPrimaryNetworkValidatorPopulatedToEmptyBLSKeyDiff(t *testing.T) { 1660 // A primary network validator has an empty BLS key. Then it restakes adding 1661 // the BLS key. Querying the validator set back when BLS key was empty must 1662 // return an empty BLS key. 1663 1664 // setup 1665 require := require.New(t) 1666 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1667 vm.ctx.Lock.Lock() 1668 defer vm.ctx.Lock.Unlock() 1669 1670 wallet := newWallet(t, vm, walletConfig{}) 1671 1672 // A primary network validator stake twice 1673 var ( 1674 primaryStartTime1 = genesistest.DefaultValidatorStartTime.Add(executor.SyncBound) 1675 primaryEndTime1 = primaryStartTime1.Add(defaultMinStakingDuration) 1676 primaryStartTime2 = primaryEndTime1.Add(executor.SyncBound) 1677 primaryEndTime2 = primaryStartTime2.Add(defaultMinStakingDuration) 1678 ) 1679 1680 // Add a primary network validator with no BLS key 1681 nodeID := ids.GenerateTestNodeID() 1682 rewardsOwner := &secp256k1fx.OutputOwners{ 1683 Threshold: 1, 1684 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1685 } 1686 1687 primaryTx1, err := wallet.IssueAddValidatorTx( 1688 &txs.Validator{ 1689 NodeID: nodeID, 1690 Start: uint64(primaryStartTime1.Unix()), 1691 End: uint64(primaryEndTime1.Unix()), 1692 Wght: vm.MinValidatorStake, 1693 }, 1694 rewardsOwner, 1695 reward.PercentDenominator, 1696 ) 1697 require.NoError(err) 1698 1699 vm.ctx.Lock.Unlock() 1700 require.NoError(vm.issueTxFromRPC(primaryTx1)) 1701 vm.ctx.Lock.Lock() 1702 require.NoError(buildAndAcceptStandardBlock(vm)) 1703 1704 // move time ahead, promoting primary validator to current 1705 vm.clock.Set(primaryStartTime1) 1706 require.NoError(buildAndAcceptStandardBlock(vm)) 1707 1708 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1709 require.NoError(err) 1710 1711 primaryStartHeight, err := vm.GetCurrentHeight(context.Background()) 1712 require.NoError(err) 1713 1714 // move time ahead, terminating primary network validator 1715 vm.clock.Set(primaryEndTime1) 1716 blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator 1717 require.NoError(err) 1718 require.NoError(blk.Verify(context.Background())) 1719 1720 proposalBlk := blk.(snowman.OracleBlock) 1721 options, err := proposalBlk.Options(context.Background()) 1722 require.NoError(err) 1723 1724 commit := options[0].(*blockexecutor.Block) 1725 require.IsType(&block.BanffCommitBlock{}, commit.Block) 1726 1727 require.NoError(blk.Accept(context.Background())) 1728 require.NoError(commit.Verify(context.Background())) 1729 require.NoError(commit.Accept(context.Background())) 1730 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1731 1732 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1733 require.ErrorIs(err, database.ErrNotFound) 1734 1735 primaryEndHeight, err := vm.GetCurrentHeight(context.Background()) 1736 require.NoError(err) 1737 1738 // reinsert primary validator with a different BLS key 1739 sk, err := bls.NewSecretKey() 1740 require.NoError(err) 1741 1742 primaryRestartTx, err := wallet.IssueAddPermissionlessValidatorTx( 1743 &txs.SubnetValidator{ 1744 Validator: txs.Validator{ 1745 NodeID: nodeID, 1746 Start: uint64(primaryStartTime2.Unix()), 1747 End: uint64(primaryEndTime2.Unix()), 1748 Wght: vm.MinValidatorStake, 1749 }, 1750 Subnet: constants.PrimaryNetworkID, 1751 }, 1752 signer.NewProofOfPossession(sk), 1753 vm.ctx.AVAXAssetID, 1754 rewardsOwner, 1755 rewardsOwner, 1756 reward.PercentDenominator, 1757 ) 1758 require.NoError(err) 1759 1760 vm.ctx.Lock.Unlock() 1761 require.NoError(vm.issueTxFromRPC(primaryRestartTx)) 1762 vm.ctx.Lock.Lock() 1763 require.NoError(buildAndAcceptStandardBlock(vm)) 1764 1765 // move time ahead, promoting restarted primary validator to current 1766 vm.clock.Set(primaryStartTime2) 1767 require.NoError(buildAndAcceptStandardBlock(vm)) 1768 1769 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1770 require.NoError(err) 1771 1772 for height := primaryStartHeight; height < primaryEndHeight; height++ { 1773 require.NoError(checkValidatorBlsKeyIsSet( 1774 vm.State, 1775 nodeID, 1776 constants.PrimaryNetworkID, 1777 height, 1778 nil, 1779 )) 1780 } 1781 } 1782 1783 func TestSubnetValidatorPopulatedToEmptyBLSKeyDiff(t *testing.T) { 1784 // A primary network validator has an empty BLS key and a subnet validator. 1785 // Primary network validator terminates its first staking cycle and it 1786 // restakes adding the BLS key. Querying the validator set back when BLS key 1787 // was empty must return an empty BLS key for the subnet validator 1788 1789 // setup 1790 require := require.New(t) 1791 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1792 vm.ctx.Lock.Lock() 1793 defer vm.ctx.Lock.Unlock() 1794 1795 subnetID := testSubnet1.TxID 1796 wallet := newWallet(t, vm, walletConfig{ 1797 subnetIDs: []ids.ID{subnetID}, 1798 }) 1799 1800 // A primary network validator stake twice 1801 var ( 1802 primaryStartTime1 = genesistest.DefaultValidatorStartTime.Add(executor.SyncBound) 1803 subnetStartTime = primaryStartTime1.Add(executor.SyncBound) 1804 subnetEndTime = subnetStartTime.Add(defaultMinStakingDuration) 1805 primaryEndTime1 = subnetEndTime.Add(time.Second) 1806 primaryStartTime2 = primaryEndTime1.Add(executor.SyncBound) 1807 primaryEndTime2 = primaryStartTime2.Add(defaultMinStakingDuration) 1808 ) 1809 1810 // Add a primary network validator with no BLS key 1811 nodeID := ids.GenerateTestNodeID() 1812 rewardsOwner := &secp256k1fx.OutputOwners{ 1813 Threshold: 1, 1814 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1815 } 1816 1817 primaryTx1, err := wallet.IssueAddValidatorTx( 1818 &txs.Validator{ 1819 NodeID: nodeID, 1820 Start: uint64(primaryStartTime1.Unix()), 1821 End: uint64(primaryEndTime1.Unix()), 1822 Wght: vm.MinValidatorStake, 1823 }, 1824 rewardsOwner, 1825 reward.PercentDenominator, 1826 ) 1827 require.NoError(err) 1828 1829 vm.ctx.Lock.Unlock() 1830 require.NoError(vm.issueTxFromRPC(primaryTx1)) 1831 vm.ctx.Lock.Lock() 1832 require.NoError(buildAndAcceptStandardBlock(vm)) 1833 1834 // move time ahead, promoting primary validator to current 1835 vm.clock.Set(primaryStartTime1) 1836 require.NoError(buildAndAcceptStandardBlock(vm)) 1837 1838 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1839 require.NoError(err) 1840 1841 primaryStartHeight, err := vm.GetCurrentHeight(context.Background()) 1842 require.NoError(err) 1843 1844 // insert the subnet validator 1845 subnetTx, err := wallet.IssueAddSubnetValidatorTx( 1846 &txs.SubnetValidator{ 1847 Validator: txs.Validator{ 1848 NodeID: nodeID, 1849 Start: uint64(subnetStartTime.Unix()), 1850 End: uint64(subnetEndTime.Unix()), 1851 Wght: 1, 1852 }, 1853 Subnet: subnetID, 1854 }, 1855 ) 1856 require.NoError(err) 1857 1858 vm.ctx.Lock.Unlock() 1859 require.NoError(vm.issueTxFromRPC(subnetTx)) 1860 vm.ctx.Lock.Lock() 1861 require.NoError(buildAndAcceptStandardBlock(vm)) 1862 1863 // move time ahead, promoting the subnet validator to current 1864 vm.clock.Set(subnetStartTime) 1865 require.NoError(buildAndAcceptStandardBlock(vm)) 1866 1867 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 1868 require.NoError(err) 1869 1870 subnetStartHeight, err := vm.GetCurrentHeight(context.Background()) 1871 require.NoError(err) 1872 1873 // move time ahead, terminating the subnet validator 1874 vm.clock.Set(subnetEndTime) 1875 require.NoError(buildAndAcceptStandardBlock(vm)) 1876 1877 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 1878 require.ErrorIs(err, database.ErrNotFound) 1879 1880 subnetEndHeight, err := vm.GetCurrentHeight(context.Background()) 1881 require.NoError(err) 1882 1883 // move time ahead, terminating primary network validator 1884 vm.clock.Set(primaryEndTime1) 1885 blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator 1886 require.NoError(err) 1887 require.NoError(blk.Verify(context.Background())) 1888 1889 proposalBlk := blk.(snowman.OracleBlock) 1890 options, err := proposalBlk.Options(context.Background()) 1891 require.NoError(err) 1892 1893 commit := options[0].(*blockexecutor.Block) 1894 require.IsType(&block.BanffCommitBlock{}, commit.Block) 1895 1896 require.NoError(blk.Accept(context.Background())) 1897 require.NoError(commit.Verify(context.Background())) 1898 require.NoError(commit.Accept(context.Background())) 1899 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1900 1901 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1902 require.ErrorIs(err, database.ErrNotFound) 1903 1904 primaryEndHeight, err := vm.GetCurrentHeight(context.Background()) 1905 require.NoError(err) 1906 1907 // reinsert primary validator with a different BLS key 1908 sk2, err := bls.NewSecretKey() 1909 require.NoError(err) 1910 1911 primaryRestartTx, err := wallet.IssueAddPermissionlessValidatorTx( 1912 &txs.SubnetValidator{ 1913 Validator: txs.Validator{ 1914 NodeID: nodeID, 1915 Start: uint64(primaryStartTime2.Unix()), 1916 End: uint64(primaryEndTime2.Unix()), 1917 Wght: vm.MinValidatorStake, 1918 }, 1919 Subnet: constants.PrimaryNetworkID, 1920 }, 1921 signer.NewProofOfPossession(sk2), 1922 vm.ctx.AVAXAssetID, 1923 rewardsOwner, 1924 rewardsOwner, 1925 reward.PercentDenominator, 1926 ) 1927 require.NoError(err) 1928 1929 vm.ctx.Lock.Unlock() 1930 require.NoError(vm.issueTxFromRPC(primaryRestartTx)) 1931 vm.ctx.Lock.Lock() 1932 require.NoError(buildAndAcceptStandardBlock(vm)) 1933 1934 // move time ahead, promoting restarted primary validator to current 1935 vm.clock.Set(primaryStartTime2) 1936 require.NoError(buildAndAcceptStandardBlock(vm)) 1937 1938 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 1939 require.NoError(err) 1940 1941 for height := primaryStartHeight; height < primaryEndHeight; height++ { 1942 require.NoError(checkValidatorBlsKeyIsSet( 1943 vm.State, 1944 nodeID, 1945 constants.PrimaryNetworkID, 1946 height, 1947 nil, 1948 )) 1949 } 1950 for height := subnetStartHeight; height < subnetEndHeight; height++ { 1951 require.NoError(checkValidatorBlsKeyIsSet( 1952 vm.State, 1953 nodeID, 1954 subnetID, 1955 height, 1956 nil, 1957 )) 1958 } 1959 } 1960 1961 func TestSubnetValidatorSetAfterPrimaryNetworkValidatorRemoval(t *testing.T) { 1962 // A primary network validator and a subnet validator are running. 1963 // Primary network validator terminates its staking cycle. 1964 // Querying the validator set when the subnet validator existed should 1965 // succeed. 1966 1967 // setup 1968 require := require.New(t) 1969 vm, _, _ := defaultVM(t, upgradetest.Cortina) 1970 vm.ctx.Lock.Lock() 1971 defer vm.ctx.Lock.Unlock() 1972 1973 subnetID := testSubnet1.TxID 1974 wallet := newWallet(t, vm, walletConfig{ 1975 subnetIDs: []ids.ID{subnetID}, 1976 }) 1977 1978 // A primary network validator stake twice 1979 var ( 1980 primaryStartTime1 = genesistest.DefaultValidatorStartTime.Add(executor.SyncBound) 1981 subnetStartTime = primaryStartTime1.Add(executor.SyncBound) 1982 subnetEndTime = subnetStartTime.Add(defaultMinStakingDuration) 1983 primaryEndTime1 = subnetEndTime.Add(time.Second) 1984 ) 1985 1986 // Add a primary network validator with no BLS key 1987 nodeID := ids.GenerateTestNodeID() 1988 1989 primaryTx1, err := wallet.IssueAddValidatorTx( 1990 &txs.Validator{ 1991 NodeID: nodeID, 1992 Start: uint64(primaryStartTime1.Unix()), 1993 End: uint64(primaryEndTime1.Unix()), 1994 Wght: vm.MinValidatorStake, 1995 }, 1996 &secp256k1fx.OutputOwners{ 1997 Threshold: 1, 1998 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1999 }, 2000 reward.PercentDenominator, 2001 ) 2002 require.NoError(err) 2003 2004 vm.ctx.Lock.Unlock() 2005 require.NoError(vm.issueTxFromRPC(primaryTx1)) 2006 vm.ctx.Lock.Lock() 2007 require.NoError(buildAndAcceptStandardBlock(vm)) 2008 2009 // move time ahead, promoting primary validator to current 2010 vm.clock.Set(primaryStartTime1) 2011 require.NoError(buildAndAcceptStandardBlock(vm)) 2012 2013 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 2014 require.NoError(err) 2015 2016 // insert the subnet validator 2017 subnetTx, err := wallet.IssueAddSubnetValidatorTx( 2018 &txs.SubnetValidator{ 2019 Validator: txs.Validator{ 2020 NodeID: nodeID, 2021 Start: uint64(subnetStartTime.Unix()), 2022 End: uint64(subnetEndTime.Unix()), 2023 Wght: 1, 2024 }, 2025 Subnet: subnetID, 2026 }, 2027 ) 2028 require.NoError(err) 2029 2030 vm.ctx.Lock.Unlock() 2031 require.NoError(vm.issueTxFromRPC(subnetTx)) 2032 vm.ctx.Lock.Lock() 2033 require.NoError(buildAndAcceptStandardBlock(vm)) 2034 2035 // move time ahead, promoting the subnet validator to current 2036 vm.clock.Set(subnetStartTime) 2037 require.NoError(buildAndAcceptStandardBlock(vm)) 2038 2039 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 2040 require.NoError(err) 2041 2042 subnetStartHeight, err := vm.GetCurrentHeight(context.Background()) 2043 require.NoError(err) 2044 2045 // move time ahead, terminating the subnet validator 2046 vm.clock.Set(subnetEndTime) 2047 require.NoError(buildAndAcceptStandardBlock(vm)) 2048 2049 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 2050 require.ErrorIs(err, database.ErrNotFound) 2051 2052 // move time ahead, terminating primary network validator 2053 vm.clock.Set(primaryEndTime1) 2054 blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator 2055 require.NoError(err) 2056 require.NoError(blk.Verify(context.Background())) 2057 2058 proposalBlk := blk.(snowman.OracleBlock) 2059 options, err := proposalBlk.Options(context.Background()) 2060 require.NoError(err) 2061 2062 commit := options[0].(*blockexecutor.Block) 2063 require.IsType(&block.BanffCommitBlock{}, commit.Block) 2064 2065 require.NoError(blk.Accept(context.Background())) 2066 require.NoError(commit.Verify(context.Background())) 2067 require.NoError(commit.Accept(context.Background())) 2068 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 2069 2070 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 2071 require.ErrorIs(err, database.ErrNotFound) 2072 2073 // Generating the validator set should not error when re-introducing a 2074 // subnet validator whose primary network validator was also removed. 2075 _, err = vm.State.GetValidatorSet(context.Background(), subnetStartHeight, subnetID) 2076 require.NoError(err) 2077 } 2078 2079 func TestValidatorSetRaceCondition(t *testing.T) { 2080 require := require.New(t) 2081 vm, _, _ := defaultVM(t, upgradetest.Cortina) 2082 vm.ctx.Lock.Lock() 2083 defer vm.ctx.Lock.Unlock() 2084 2085 nodeID := ids.GenerateTestNodeID() 2086 require.NoError(vm.Connected(context.Background(), nodeID, version.CurrentApp)) 2087 2088 protocolAppRequestBytest, err := gossip.MarshalAppRequest( 2089 bloom.EmptyFilter.Marshal(), 2090 ids.Empty[:], 2091 ) 2092 require.NoError(err) 2093 2094 appRequestBytes := p2p.PrefixMessage( 2095 p2p.ProtocolPrefix(p2p.TxGossipHandlerID), 2096 protocolAppRequestBytest, 2097 ) 2098 2099 var ( 2100 eg errgroup.Group 2101 ctx, cancel = context.WithCancel(context.Background()) 2102 ) 2103 // keep 10 workers running 2104 for i := 0; i < 10; i++ { 2105 eg.Go(func() error { 2106 for ctx.Err() == nil { 2107 err := vm.AppRequest( 2108 context.Background(), 2109 nodeID, 2110 0, 2111 time.Now().Add(time.Hour), 2112 appRequestBytes, 2113 ) 2114 if err != nil { 2115 return err 2116 } 2117 } 2118 return nil 2119 }) 2120 } 2121 2122 // If the validator set lock isn't held, the race detector should fail here. 2123 for i := uint64(0); i < 1000; i++ { 2124 blk, err := block.NewBanffStandardBlock( 2125 time.Now(), 2126 vm.state.GetLastAccepted(), 2127 i, 2128 nil, 2129 ) 2130 require.NoError(err) 2131 2132 vm.state.SetLastAccepted(blk.ID()) 2133 vm.state.SetHeight(blk.Height()) 2134 vm.state.AddStatelessBlock(blk) 2135 } 2136 2137 // If the validator set lock is grabbed, we need to make sure to release the 2138 // lock to avoid a deadlock. 2139 vm.ctx.Lock.Unlock() 2140 cancel() // stop and wait for workers 2141 require.NoError(eg.Wait()) 2142 vm.ctx.Lock.Lock() 2143 } 2144 2145 func buildAndAcceptStandardBlock(vm *VM) error { 2146 blk, err := vm.Builder.BuildBlock(context.Background()) 2147 if err != nil { 2148 return err 2149 } 2150 2151 if err := blk.Verify(context.Background()); err != nil { 2152 return err 2153 } 2154 2155 if err := blk.Accept(context.Background()); err != nil { 2156 return err 2157 } 2158 2159 return vm.SetPreference(context.Background(), vm.manager.LastAccepted()) 2160 } 2161 2162 func checkValidatorBlsKeyIsSet( 2163 valState validators.State, 2164 nodeID ids.NodeID, 2165 subnetID ids.ID, 2166 height uint64, 2167 expectedBlsKey *bls.PublicKey, 2168 ) error { 2169 vals, err := valState.GetValidatorSet(context.Background(), height, subnetID) 2170 if err != nil { 2171 return err 2172 } 2173 2174 val, found := vals[nodeID] 2175 switch { 2176 case !found: 2177 return database.ErrNotFound 2178 case expectedBlsKey == val.PublicKey: 2179 return nil 2180 case expectedBlsKey == nil && val.PublicKey != nil: 2181 return errors.New("unexpected BLS key") 2182 case expectedBlsKey != nil && val.PublicKey == nil: 2183 return errors.New("missing BLS key") 2184 case !bytes.Equal(bls.PublicKeyToUncompressedBytes(expectedBlsKey), bls.PublicKeyToUncompressedBytes(val.PublicKey)): 2185 return errors.New("incorrect BLS key") 2186 default: 2187 return nil 2188 } 2189 }