github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/vm_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 "testing" 10 "time" 11 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/require" 14 15 "github.com/ava-labs/avalanchego/chains" 16 "github.com/ava-labs/avalanchego/chains/atomic" 17 "github.com/ava-labs/avalanchego/database" 18 "github.com/ava-labs/avalanchego/database/memdb" 19 "github.com/ava-labs/avalanchego/database/prefixdb" 20 "github.com/ava-labs/avalanchego/ids" 21 "github.com/ava-labs/avalanchego/message" 22 "github.com/ava-labs/avalanchego/network/p2p" 23 "github.com/ava-labs/avalanchego/snow" 24 "github.com/ava-labs/avalanchego/snow/consensus/snowball" 25 "github.com/ava-labs/avalanchego/snow/engine/common" 26 "github.com/ava-labs/avalanchego/snow/engine/common/tracker" 27 "github.com/ava-labs/avalanchego/snow/engine/enginetest" 28 "github.com/ava-labs/avalanchego/snow/engine/snowman/bootstrap" 29 "github.com/ava-labs/avalanchego/snow/networking/benchlist" 30 "github.com/ava-labs/avalanchego/snow/networking/handler" 31 "github.com/ava-labs/avalanchego/snow/networking/router" 32 "github.com/ava-labs/avalanchego/snow/networking/sender" 33 "github.com/ava-labs/avalanchego/snow/networking/sender/sendertest" 34 "github.com/ava-labs/avalanchego/snow/networking/timeout" 35 "github.com/ava-labs/avalanchego/snow/snowtest" 36 "github.com/ava-labs/avalanchego/snow/uptime" 37 "github.com/ava-labs/avalanchego/snow/validators" 38 "github.com/ava-labs/avalanchego/subnets" 39 "github.com/ava-labs/avalanchego/upgrade/upgradetest" 40 "github.com/ava-labs/avalanchego/utils/constants" 41 "github.com/ava-labs/avalanchego/utils/crypto/bls" 42 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" 43 "github.com/ava-labs/avalanchego/utils/logging" 44 "github.com/ava-labs/avalanchego/utils/math/meter" 45 "github.com/ava-labs/avalanchego/utils/resource" 46 "github.com/ava-labs/avalanchego/utils/set" 47 "github.com/ava-labs/avalanchego/utils/timer" 48 "github.com/ava-labs/avalanchego/utils/units" 49 "github.com/ava-labs/avalanchego/version" 50 "github.com/ava-labs/avalanchego/vms/components/avax" 51 "github.com/ava-labs/avalanchego/vms/components/gas" 52 "github.com/ava-labs/avalanchego/vms/platformvm/block" 53 "github.com/ava-labs/avalanchego/vms/platformvm/config" 54 "github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest" 55 "github.com/ava-labs/avalanchego/vms/platformvm/reward" 56 "github.com/ava-labs/avalanchego/vms/platformvm/signer" 57 "github.com/ava-labs/avalanchego/vms/platformvm/status" 58 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 59 "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" 60 "github.com/ava-labs/avalanchego/vms/platformvm/txs/txstest" 61 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 62 "github.com/ava-labs/avalanchego/wallet/chain/p/wallet" 63 64 p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" 65 smcon "github.com/ava-labs/avalanchego/snow/consensus/snowman" 66 smeng "github.com/ava-labs/avalanchego/snow/engine/snowman" 67 snowgetter "github.com/ava-labs/avalanchego/snow/engine/snowman/getter" 68 timetracker "github.com/ava-labs/avalanchego/snow/networking/tracker" 69 blockbuilder "github.com/ava-labs/avalanchego/vms/platformvm/block/builder" 70 blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/block/executor" 71 txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" 72 walletbuilder "github.com/ava-labs/avalanchego/wallet/chain/p/builder" 73 walletcommon "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" 74 ) 75 76 const ( 77 defaultMinDelegatorStake = 1 * units.MilliAvax 78 defaultMinValidatorStake = 5 * defaultMinDelegatorStake 79 defaultMaxValidatorStake = 100 * defaultMinValidatorStake 80 81 defaultMinStakingDuration = 24 * time.Hour 82 defaultMaxStakingDuration = 365 * 24 * time.Hour 83 84 defaultTxFee = 100 * units.NanoAvax 85 ) 86 87 var ( 88 defaultRewardConfig = reward.Config{ 89 MaxConsumptionRate: .12 * reward.PercentDenominator, 90 MinConsumptionRate: .10 * reward.PercentDenominator, 91 MintingPeriod: 365 * 24 * time.Hour, 92 SupplyCap: 720 * units.MegaAvax, 93 } 94 95 latestForkTime = genesistest.DefaultValidatorStartTime.Add(time.Second) 96 97 defaultStaticFeeConfig = fee.StaticConfig{ 98 TxFee: defaultTxFee, 99 CreateSubnetTxFee: 100 * defaultTxFee, 100 TransformSubnetTxFee: 100 * defaultTxFee, 101 CreateBlockchainTxFee: 100 * defaultTxFee, 102 } 103 defaultDynamicFeeConfig = gas.Config{ 104 Weights: gas.Dimensions{ 105 gas.Bandwidth: 1, 106 gas.DBRead: 1, 107 gas.DBWrite: 1, 108 gas.Compute: 1, 109 }, 110 MaxCapacity: 10_000, 111 MaxPerSecond: 1_000, 112 TargetPerSecond: 500, 113 MinPrice: 1, 114 ExcessConversionConstant: 5_000, 115 } 116 117 // subnet that exists at genesis in defaultVM 118 testSubnet1 *txs.Tx 119 ) 120 121 type mutableSharedMemory struct { 122 atomic.SharedMemory 123 } 124 125 func defaultVM(t *testing.T, f upgradetest.Fork) (*VM, database.Database, *mutableSharedMemory) { 126 require := require.New(t) 127 128 // always reset latestForkTime (a package level variable) 129 // to ensure test independence 130 latestForkTime = genesistest.DefaultValidatorStartTime.Add(time.Second) 131 vm := &VM{Config: config.Config{ 132 Chains: chains.TestManager, 133 UptimeLockedCalculator: uptime.NewLockedCalculator(), 134 SybilProtectionEnabled: true, 135 Validators: validators.NewManager(), 136 StaticFeeConfig: defaultStaticFeeConfig, 137 DynamicFeeConfig: defaultDynamicFeeConfig, 138 MinValidatorStake: defaultMinValidatorStake, 139 MaxValidatorStake: defaultMaxValidatorStake, 140 MinDelegatorStake: defaultMinDelegatorStake, 141 MinStakeDuration: defaultMinStakingDuration, 142 MaxStakeDuration: defaultMaxStakingDuration, 143 RewardConfig: defaultRewardConfig, 144 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(f, latestForkTime), 145 }} 146 147 db := memdb.New() 148 chainDB := prefixdb.New([]byte{0}, db) 149 atomicDB := prefixdb.New([]byte{1}, db) 150 151 vm.clock.Set(latestForkTime) 152 msgChan := make(chan common.Message, 1) 153 ctx := snowtest.Context(t, snowtest.PChainID) 154 155 m := atomic.NewMemory(atomicDB) 156 msm := &mutableSharedMemory{ 157 SharedMemory: m.NewSharedMemory(ctx.ChainID), 158 } 159 ctx.SharedMemory = msm 160 161 ctx.Lock.Lock() 162 defer ctx.Lock.Unlock() 163 appSender := &enginetest.Sender{} 164 appSender.CantSendAppGossip = true 165 appSender.SendAppGossipF = func(context.Context, common.SendConfig, []byte) error { 166 return nil 167 } 168 appSender.SendAppErrorF = func(context.Context, ids.NodeID, uint32, int32, string) error { 169 return nil 170 } 171 172 dynamicConfigBytes := []byte(`{"network":{"max-validator-set-staleness":0}}`) 173 require.NoError(vm.Initialize( 174 context.Background(), 175 ctx, 176 chainDB, 177 genesistest.NewBytes(t, genesistest.Config{}), 178 nil, 179 dynamicConfigBytes, 180 msgChan, 181 nil, 182 appSender, 183 )) 184 185 // align chain time and local clock 186 vm.state.SetTimestamp(vm.clock.Time()) 187 vm.state.SetFeeState(gas.State{ 188 Capacity: defaultDynamicFeeConfig.MaxCapacity, 189 }) 190 191 require.NoError(vm.SetState(context.Background(), snow.NormalOp)) 192 193 wallet := newWallet(t, vm, walletConfig{ 194 keys: []*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]}, 195 }) 196 197 // Create a subnet and store it in testSubnet1 198 // Note: following Banff activation, block acceptance will move 199 // chain time ahead 200 var err error 201 testSubnet1, err = wallet.IssueCreateSubnetTx( 202 &secp256k1fx.OutputOwners{ 203 Threshold: 2, 204 Addrs: []ids.ShortID{ 205 genesistest.DefaultFundedKeys[0].Address(), 206 genesistest.DefaultFundedKeys[1].Address(), 207 genesistest.DefaultFundedKeys[2].Address(), 208 }, 209 }, 210 ) 211 require.NoError(err) 212 213 vm.ctx.Lock.Unlock() 214 require.NoError(vm.issueTxFromRPC(testSubnet1)) 215 vm.ctx.Lock.Lock() 216 require.NoError(buildAndAcceptStandardBlock(vm)) 217 218 t.Cleanup(func() { 219 vm.ctx.Lock.Lock() 220 defer vm.ctx.Lock.Unlock() 221 222 require.NoError(vm.Shutdown(context.Background())) 223 }) 224 225 return vm, db, msm 226 } 227 228 type walletConfig struct { 229 keys []*secp256k1.PrivateKey 230 subnetIDs []ids.ID 231 } 232 233 func newWallet(t testing.TB, vm *VM, c walletConfig) wallet.Wallet { 234 if len(c.keys) == 0 { 235 c.keys = genesistest.DefaultFundedKeys 236 } 237 return txstest.NewWallet( 238 t, 239 vm.ctx, 240 &vm.Config, 241 vm.state, 242 secp256k1fx.NewKeychain(c.keys...), 243 c.subnetIDs, 244 []ids.ID{vm.ctx.CChainID, vm.ctx.XChainID}, 245 ) 246 } 247 248 // Ensure genesis state is parsed from bytes and stored correctly 249 func TestGenesis(t *testing.T) { 250 require := require.New(t) 251 vm, _, _ := defaultVM(t, upgradetest.Durango) 252 vm.ctx.Lock.Lock() 253 defer vm.ctx.Lock.Unlock() 254 255 // Ensure the genesis block has been accepted and stored 256 genesisBlockID, err := vm.LastAccepted(context.Background()) // lastAccepted should be ID of genesis block 257 require.NoError(err) 258 259 // Ensure the genesis block can be retrieved 260 genesisBlock, err := vm.manager.GetBlock(genesisBlockID) 261 require.NoError(err) 262 require.NotNil(genesisBlock) 263 264 genesisState := genesistest.New(t, genesistest.Config{}) 265 // Ensure all the genesis UTXOs are there 266 for _, utxo := range genesisState.UTXOs { 267 genesisOut := utxo.Out.(*secp256k1fx.TransferOutput) 268 utxos, err := avax.GetAllUTXOs( 269 vm.state, 270 genesisOut.OutputOwners.AddressesSet(), 271 ) 272 require.NoError(err) 273 require.Len(utxos, 1) 274 275 out := utxos[0].Out.(*secp256k1fx.TransferOutput) 276 if out.Amt != genesisOut.Amt { 277 require.Equal( 278 []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()}, 279 out.OutputOwners.Addrs, 280 ) 281 require.Equal(genesisOut.Amt-vm.StaticFeeConfig.CreateSubnetTxFee, out.Amt) 282 } 283 } 284 285 // Ensure current validator set of primary network is correct 286 require.Len(genesisState.Validators, vm.Validators.Count(constants.PrimaryNetworkID)) 287 288 for _, nodeID := range genesistest.DefaultNodeIDs { 289 _, ok := vm.Validators.GetValidator(constants.PrimaryNetworkID, nodeID) 290 require.True(ok) 291 } 292 293 // Ensure the new subnet we created exists 294 _, _, err = vm.state.GetTx(testSubnet1.ID()) 295 require.NoError(err) 296 } 297 298 // accept proposal to add validator to primary network 299 func TestAddValidatorCommit(t *testing.T) { 300 require := require.New(t) 301 vm, _, _ := defaultVM(t, upgradetest.Latest) 302 vm.ctx.Lock.Lock() 303 defer vm.ctx.Lock.Unlock() 304 305 wallet := newWallet(t, vm, walletConfig{}) 306 307 var ( 308 endTime = vm.clock.Time().Add(defaultMinStakingDuration) 309 nodeID = ids.GenerateTestNodeID() 310 rewardsOwner = &secp256k1fx.OutputOwners{ 311 Threshold: 1, 312 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 313 } 314 ) 315 316 sk, err := bls.NewSecretKey() 317 require.NoError(err) 318 319 // create valid tx 320 tx, err := wallet.IssueAddPermissionlessValidatorTx( 321 &txs.SubnetValidator{ 322 Validator: txs.Validator{ 323 NodeID: nodeID, 324 End: uint64(endTime.Unix()), 325 Wght: vm.MinValidatorStake, 326 }, 327 Subnet: constants.PrimaryNetworkID, 328 }, 329 signer.NewProofOfPossession(sk), 330 vm.ctx.AVAXAssetID, 331 rewardsOwner, 332 rewardsOwner, 333 reward.PercentDenominator, 334 ) 335 require.NoError(err) 336 337 // trigger block creation 338 vm.ctx.Lock.Unlock() 339 require.NoError(vm.issueTxFromRPC(tx)) 340 vm.ctx.Lock.Lock() 341 require.NoError(buildAndAcceptStandardBlock(vm)) 342 343 _, txStatus, err := vm.state.GetTx(tx.ID()) 344 require.NoError(err) 345 require.Equal(status.Committed, txStatus) 346 347 // Verify that new validator now in current validator set 348 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 349 require.NoError(err) 350 } 351 352 // verify invalid attempt to add validator to primary network 353 func TestInvalidAddValidatorCommit(t *testing.T) { 354 require := require.New(t) 355 vm, _, _ := defaultVM(t, upgradetest.Cortina) 356 vm.ctx.Lock.Lock() 357 defer vm.ctx.Lock.Unlock() 358 359 wallet := newWallet(t, vm, walletConfig{}) 360 361 nodeID := ids.GenerateTestNodeID() 362 startTime := genesistest.DefaultValidatorStartTime.Add(-txexecutor.SyncBound).Add(-1 * time.Second) 363 endTime := startTime.Add(defaultMinStakingDuration) 364 365 // create invalid tx 366 tx, err := wallet.IssueAddValidatorTx( 367 &txs.Validator{ 368 NodeID: nodeID, 369 Start: uint64(startTime.Unix()), 370 End: uint64(endTime.Unix()), 371 Wght: vm.MinValidatorStake, 372 }, 373 &secp256k1fx.OutputOwners{ 374 Threshold: 1, 375 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 376 }, 377 reward.PercentDenominator, 378 ) 379 require.NoError(err) 380 381 preferredID := vm.manager.Preferred() 382 preferred, err := vm.manager.GetBlock(preferredID) 383 require.NoError(err) 384 preferredHeight := preferred.Height() 385 386 statelessBlk, err := block.NewBanffStandardBlock( 387 preferred.Timestamp(), 388 preferredID, 389 preferredHeight+1, 390 []*txs.Tx{tx}, 391 ) 392 require.NoError(err) 393 394 blkBytes := statelessBlk.Bytes() 395 396 parsedBlock, err := vm.ParseBlock(context.Background(), blkBytes) 397 require.NoError(err) 398 399 err = parsedBlock.Verify(context.Background()) 400 require.ErrorIs(err, txexecutor.ErrTimestampNotBeforeStartTime) 401 402 txID := statelessBlk.Txs()[0].ID() 403 reason := vm.Builder.GetDropReason(txID) 404 require.ErrorIs(reason, txexecutor.ErrTimestampNotBeforeStartTime) 405 } 406 407 // Reject attempt to add validator to primary network 408 func TestAddValidatorReject(t *testing.T) { 409 require := require.New(t) 410 vm, _, _ := defaultVM(t, upgradetest.Cortina) 411 vm.ctx.Lock.Lock() 412 defer vm.ctx.Lock.Unlock() 413 414 wallet := newWallet(t, vm, walletConfig{}) 415 416 var ( 417 startTime = vm.clock.Time().Add(txexecutor.SyncBound).Add(1 * time.Second) 418 endTime = startTime.Add(defaultMinStakingDuration) 419 nodeID = ids.GenerateTestNodeID() 420 rewardAddress = ids.GenerateTestShortID() 421 ) 422 423 // create valid tx 424 tx, err := wallet.IssueAddValidatorTx( 425 &txs.Validator{ 426 NodeID: nodeID, 427 Start: uint64(startTime.Unix()), 428 End: uint64(endTime.Unix()), 429 Wght: vm.MinValidatorStake, 430 }, 431 &secp256k1fx.OutputOwners{ 432 Threshold: 1, 433 Addrs: []ids.ShortID{rewardAddress}, 434 }, 435 reward.PercentDenominator, 436 ) 437 require.NoError(err) 438 439 // trigger block creation 440 vm.ctx.Lock.Unlock() 441 require.NoError(vm.issueTxFromRPC(tx)) 442 vm.ctx.Lock.Lock() 443 444 blk, err := vm.Builder.BuildBlock(context.Background()) 445 require.NoError(err) 446 447 require.NoError(blk.Verify(context.Background())) 448 require.NoError(blk.Reject(context.Background())) 449 450 _, _, err = vm.state.GetTx(tx.ID()) 451 require.ErrorIs(err, database.ErrNotFound) 452 453 _, err = vm.state.GetPendingValidator(constants.PrimaryNetworkID, nodeID) 454 require.ErrorIs(err, database.ErrNotFound) 455 } 456 457 // Reject proposal to add validator to primary network 458 func TestAddValidatorInvalidNotReissued(t *testing.T) { 459 require := require.New(t) 460 vm, _, _ := defaultVM(t, upgradetest.Latest) 461 vm.ctx.Lock.Lock() 462 defer vm.ctx.Lock.Unlock() 463 464 wallet := newWallet(t, vm, walletConfig{}) 465 466 // Use nodeID that is already in the genesis 467 repeatNodeID := genesistest.DefaultNodeIDs[0] 468 469 startTime := latestForkTime.Add(txexecutor.SyncBound).Add(1 * time.Second) 470 endTime := startTime.Add(defaultMinStakingDuration) 471 472 sk, err := bls.NewSecretKey() 473 require.NoError(err) 474 475 rewardsOwner := &secp256k1fx.OutputOwners{ 476 Threshold: 1, 477 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 478 } 479 480 // create valid tx 481 tx, err := wallet.IssueAddPermissionlessValidatorTx( 482 &txs.SubnetValidator{ 483 Validator: txs.Validator{ 484 NodeID: repeatNodeID, 485 Start: uint64(startTime.Unix()), 486 End: uint64(endTime.Unix()), 487 Wght: vm.MinValidatorStake, 488 }, 489 Subnet: constants.PrimaryNetworkID, 490 }, 491 signer.NewProofOfPossession(sk), 492 vm.ctx.AVAXAssetID, 493 rewardsOwner, 494 rewardsOwner, 495 reward.PercentDenominator, 496 ) 497 require.NoError(err) 498 499 // trigger block creation 500 vm.ctx.Lock.Unlock() 501 err = vm.issueTxFromRPC(tx) 502 vm.ctx.Lock.Lock() 503 require.ErrorIs(err, txexecutor.ErrDuplicateValidator) 504 } 505 506 // Accept proposal to add validator to subnet 507 func TestAddSubnetValidatorAccept(t *testing.T) { 508 require := require.New(t) 509 vm, _, _ := defaultVM(t, upgradetest.Latest) 510 vm.ctx.Lock.Lock() 511 defer vm.ctx.Lock.Unlock() 512 513 subnetID := testSubnet1.ID() 514 wallet := newWallet(t, vm, walletConfig{ 515 subnetIDs: []ids.ID{subnetID}, 516 }) 517 518 var ( 519 startTime = vm.clock.Time().Add(txexecutor.SyncBound).Add(1 * time.Second) 520 endTime = startTime.Add(defaultMinStakingDuration) 521 nodeID = genesistest.DefaultNodeIDs[0] 522 ) 523 524 // create valid tx 525 // note that [startTime, endTime] is a subset of time that keys[0] 526 // validates primary network ([genesistest.DefaultValidatorStartTime, genesistest.DefaultValidatorEndTime]) 527 tx, err := wallet.IssueAddSubnetValidatorTx( 528 &txs.SubnetValidator{ 529 Validator: txs.Validator{ 530 NodeID: nodeID, 531 Start: uint64(startTime.Unix()), 532 End: uint64(endTime.Unix()), 533 Wght: genesistest.DefaultValidatorWeight, 534 }, 535 Subnet: subnetID, 536 }, 537 ) 538 require.NoError(err) 539 540 // trigger block creation 541 vm.ctx.Lock.Unlock() 542 require.NoError(vm.issueTxFromRPC(tx)) 543 vm.ctx.Lock.Lock() 544 require.NoError(buildAndAcceptStandardBlock(vm)) 545 546 _, txStatus, err := vm.state.GetTx(tx.ID()) 547 require.NoError(err) 548 require.Equal(status.Committed, txStatus) 549 550 // Verify that new validator is in current validator set 551 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 552 require.NoError(err) 553 } 554 555 // Reject proposal to add validator to subnet 556 func TestAddSubnetValidatorReject(t *testing.T) { 557 require := require.New(t) 558 vm, _, _ := defaultVM(t, upgradetest.Latest) 559 vm.ctx.Lock.Lock() 560 defer vm.ctx.Lock.Unlock() 561 562 subnetID := testSubnet1.ID() 563 wallet := newWallet(t, vm, walletConfig{ 564 subnetIDs: []ids.ID{subnetID}, 565 }) 566 567 var ( 568 startTime = vm.clock.Time().Add(txexecutor.SyncBound).Add(1 * time.Second) 569 endTime = startTime.Add(defaultMinStakingDuration) 570 nodeID = genesistest.DefaultNodeIDs[0] 571 ) 572 573 // create valid tx 574 // note that [startTime, endTime] is a subset of time that keys[0] 575 // validates primary network ([genesistest.DefaultValidatorStartTime, genesistest.DefaultValidatorEndTime]) 576 tx, err := wallet.IssueAddSubnetValidatorTx( 577 &txs.SubnetValidator{ 578 Validator: txs.Validator{ 579 NodeID: nodeID, 580 Start: uint64(startTime.Unix()), 581 End: uint64(endTime.Unix()), 582 Wght: genesistest.DefaultValidatorWeight, 583 }, 584 Subnet: testSubnet1.ID(), 585 }, 586 ) 587 require.NoError(err) 588 589 // trigger block creation 590 vm.ctx.Lock.Unlock() 591 require.NoError(vm.issueTxFromRPC(tx)) 592 vm.ctx.Lock.Lock() 593 594 blk, err := vm.Builder.BuildBlock(context.Background()) 595 require.NoError(err) 596 597 require.NoError(blk.Verify(context.Background())) 598 require.NoError(blk.Reject(context.Background())) 599 600 _, _, err = vm.state.GetTx(tx.ID()) 601 require.ErrorIs(err, database.ErrNotFound) 602 603 // Verify that new validator NOT in validator set 604 _, err = vm.state.GetCurrentValidator(testSubnet1.ID(), nodeID) 605 require.ErrorIs(err, database.ErrNotFound) 606 } 607 608 // Test case where primary network validator rewarded 609 func TestRewardValidatorAccept(t *testing.T) { 610 require := require.New(t) 611 vm, _, _ := defaultVM(t, upgradetest.Latest) 612 vm.ctx.Lock.Lock() 613 defer vm.ctx.Lock.Unlock() 614 615 // Fast forward clock to time for genesis validators to leave 616 vm.clock.Set(genesistest.DefaultValidatorEndTime) 617 618 // Advance time and create proposal to reward a genesis validator 619 blk, err := vm.Builder.BuildBlock(context.Background()) 620 require.NoError(err) 621 require.NoError(blk.Verify(context.Background())) 622 623 // Assert preferences are correct 624 options, err := blk.(smcon.OracleBlock).Options(context.Background()) 625 require.NoError(err) 626 627 commit := options[0].(*blockexecutor.Block) 628 require.IsType(&block.BanffCommitBlock{}, commit.Block) 629 abort := options[1].(*blockexecutor.Block) 630 require.IsType(&block.BanffAbortBlock{}, abort.Block) 631 632 // Assert block tries to reward a genesis validator 633 rewardTx := blk.(block.Block).Txs()[0].Unsigned 634 require.IsType(&txs.RewardValidatorTx{}, rewardTx) 635 636 // Verify options and accept commmit block 637 require.NoError(commit.Verify(context.Background())) 638 require.NoError(abort.Verify(context.Background())) 639 txID := blk.(block.Block).Txs()[0].ID() 640 { 641 onAbort, ok := vm.manager.GetState(abort.ID()) 642 require.True(ok) 643 644 _, txStatus, err := onAbort.GetTx(txID) 645 require.NoError(err) 646 require.Equal(status.Aborted, txStatus) 647 } 648 649 require.NoError(blk.Accept(context.Background())) 650 require.NoError(commit.Accept(context.Background())) 651 652 // Verify that chain's timestamp has advanced 653 timestamp := vm.state.GetTimestamp() 654 require.Equal(genesistest.DefaultValidatorEndTimeUnix, uint64(timestamp.Unix())) 655 656 // Verify that rewarded validator has been removed. 657 // Note that test genesis has multiple validators 658 // terminating at the same time. The rewarded validator 659 // will the first by txID. To make the test more stable 660 // (txID changes every time we change any parameter 661 // of the tx creating the validator), we explicitly 662 // check that rewarded validator is removed from staker set. 663 _, txStatus, err := vm.state.GetTx(txID) 664 require.NoError(err) 665 require.Equal(status.Committed, txStatus) 666 667 tx, _, err := vm.state.GetTx(rewardTx.(*txs.RewardValidatorTx).TxID) 668 require.NoError(err) 669 require.IsType(&txs.AddValidatorTx{}, tx.Unsigned) 670 671 valTx, _ := tx.Unsigned.(*txs.AddValidatorTx) 672 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, valTx.NodeID()) 673 require.ErrorIs(err, database.ErrNotFound) 674 } 675 676 // Test case where primary network validator not rewarded 677 func TestRewardValidatorReject(t *testing.T) { 678 require := require.New(t) 679 vm, _, _ := defaultVM(t, upgradetest.Latest) 680 vm.ctx.Lock.Lock() 681 defer vm.ctx.Lock.Unlock() 682 683 // Fast forward clock to time for genesis validators to leave 684 vm.clock.Set(genesistest.DefaultValidatorEndTime) 685 686 // Advance time and create proposal to reward a genesis validator 687 blk, err := vm.Builder.BuildBlock(context.Background()) 688 require.NoError(err) 689 require.NoError(blk.Verify(context.Background())) 690 691 // Assert preferences are correct 692 oracleBlk := blk.(smcon.OracleBlock) 693 options, err := oracleBlk.Options(context.Background()) 694 require.NoError(err) 695 696 commit := options[0].(*blockexecutor.Block) 697 require.IsType(&block.BanffCommitBlock{}, commit.Block) 698 699 abort := options[1].(*blockexecutor.Block) 700 require.IsType(&block.BanffAbortBlock{}, abort.Block) 701 702 // Assert block tries to reward a genesis validator 703 rewardTx := oracleBlk.(block.Block).Txs()[0].Unsigned 704 require.IsType(&txs.RewardValidatorTx{}, rewardTx) 705 706 // Verify options and accept abort block 707 require.NoError(commit.Verify(context.Background())) 708 require.NoError(abort.Verify(context.Background())) 709 txID := blk.(block.Block).Txs()[0].ID() 710 { 711 onAccept, ok := vm.manager.GetState(commit.ID()) 712 require.True(ok) 713 714 _, txStatus, err := onAccept.GetTx(txID) 715 require.NoError(err) 716 require.Equal(status.Committed, txStatus) 717 } 718 719 require.NoError(blk.Accept(context.Background())) 720 require.NoError(abort.Accept(context.Background())) 721 722 // Verify that chain's timestamp has advanced 723 timestamp := vm.state.GetTimestamp() 724 require.Equal(genesistest.DefaultValidatorEndTimeUnix, uint64(timestamp.Unix())) 725 726 // Verify that rewarded validator has been removed. 727 // Note that test genesis has multiple validators 728 // terminating at the same time. The rewarded validator 729 // will the first by txID. To make the test more stable 730 // (txID changes every time we change any parameter 731 // of the tx creating the validator), we explicitly 732 // check that rewarded validator is removed from staker set. 733 _, txStatus, err := vm.state.GetTx(txID) 734 require.NoError(err) 735 require.Equal(status.Aborted, txStatus) 736 737 tx, _, err := vm.state.GetTx(rewardTx.(*txs.RewardValidatorTx).TxID) 738 require.NoError(err) 739 require.IsType(&txs.AddValidatorTx{}, tx.Unsigned) 740 741 valTx, _ := tx.Unsigned.(*txs.AddValidatorTx) 742 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, valTx.NodeID()) 743 require.ErrorIs(err, database.ErrNotFound) 744 } 745 746 // Ensure BuildBlock errors when there is no block to build 747 func TestUnneededBuildBlock(t *testing.T) { 748 require := require.New(t) 749 vm, _, _ := defaultVM(t, upgradetest.Latest) 750 vm.ctx.Lock.Lock() 751 defer vm.ctx.Lock.Unlock() 752 753 _, err := vm.Builder.BuildBlock(context.Background()) 754 require.ErrorIs(err, blockbuilder.ErrNoPendingBlocks) 755 } 756 757 // test acceptance of proposal to create a new chain 758 func TestCreateChain(t *testing.T) { 759 require := require.New(t) 760 vm, _, _ := defaultVM(t, upgradetest.Latest) 761 vm.ctx.Lock.Lock() 762 defer vm.ctx.Lock.Unlock() 763 764 subnetID := testSubnet1.ID() 765 wallet := newWallet(t, vm, walletConfig{ 766 subnetIDs: []ids.ID{subnetID}, 767 }) 768 769 tx, err := wallet.IssueCreateChainTx( 770 subnetID, 771 nil, 772 ids.ID{'t', 'e', 's', 't', 'v', 'm'}, 773 nil, 774 "name", 775 ) 776 require.NoError(err) 777 778 vm.ctx.Lock.Unlock() 779 require.NoError(vm.issueTxFromRPC(tx)) 780 vm.ctx.Lock.Lock() 781 require.NoError(buildAndAcceptStandardBlock(vm)) 782 783 _, txStatus, err := vm.state.GetTx(tx.ID()) 784 require.NoError(err) 785 require.Equal(status.Committed, txStatus) 786 787 // Verify chain was created 788 chains, err := vm.state.GetChains(subnetID) 789 require.NoError(err) 790 791 foundNewChain := false 792 for _, chain := range chains { 793 if bytes.Equal(chain.Bytes(), tx.Bytes()) { 794 foundNewChain = true 795 } 796 } 797 require.True(foundNewChain) 798 } 799 800 // test where we: 801 // 1) Create a subnet 802 // 2) Add a validator to the subnet's current validator set 803 // 3) Advance timestamp to validator's end time (removing validator from current) 804 func TestCreateSubnet(t *testing.T) { 805 require := require.New(t) 806 vm, _, _ := defaultVM(t, upgradetest.Latest) 807 vm.ctx.Lock.Lock() 808 defer vm.ctx.Lock.Unlock() 809 810 wallet := newWallet(t, vm, walletConfig{}) 811 createSubnetTx, err := wallet.IssueCreateSubnetTx( 812 &secp256k1fx.OutputOwners{ 813 Threshold: 1, 814 Addrs: []ids.ShortID{ 815 genesistest.DefaultFundedKeys[0].Address(), 816 genesistest.DefaultFundedKeys[1].Address(), 817 }, 818 }, 819 ) 820 require.NoError(err) 821 822 vm.ctx.Lock.Unlock() 823 require.NoError(vm.issueTxFromRPC(createSubnetTx)) 824 vm.ctx.Lock.Lock() 825 require.NoError(buildAndAcceptStandardBlock(vm)) 826 827 subnetID := createSubnetTx.ID() 828 _, txStatus, err := vm.state.GetTx(subnetID) 829 require.NoError(err) 830 require.Equal(status.Committed, txStatus) 831 832 subnetIDs, err := vm.state.GetSubnetIDs() 833 require.NoError(err) 834 require.Contains(subnetIDs, subnetID) 835 836 // Now that we've created a new subnet, add a validator to that subnet 837 nodeID := genesistest.DefaultNodeIDs[0] 838 startTime := vm.clock.Time().Add(txexecutor.SyncBound).Add(1 * time.Second) 839 endTime := startTime.Add(defaultMinStakingDuration) 840 // [startTime, endTime] is subset of time keys[0] validates default subnet so tx is valid 841 addValidatorTx, err := wallet.IssueAddSubnetValidatorTx( 842 &txs.SubnetValidator{ 843 Validator: txs.Validator{ 844 NodeID: nodeID, 845 Start: uint64(startTime.Unix()), 846 End: uint64(endTime.Unix()), 847 Wght: genesistest.DefaultValidatorWeight, 848 }, 849 Subnet: subnetID, 850 }, 851 ) 852 require.NoError(err) 853 854 vm.ctx.Lock.Unlock() 855 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 856 vm.ctx.Lock.Lock() 857 require.NoError(buildAndAcceptStandardBlock(vm)) 858 859 txID := addValidatorTx.ID() 860 _, txStatus, err = vm.state.GetTx(txID) 861 require.NoError(err) 862 require.Equal(status.Committed, txStatus) 863 864 _, err = vm.state.GetPendingValidator(subnetID, nodeID) 865 require.ErrorIs(err, database.ErrNotFound) 866 867 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 868 require.NoError(err) 869 870 // remove validator from current validator set 871 vm.clock.Set(endTime) 872 require.NoError(buildAndAcceptStandardBlock(vm)) 873 874 _, err = vm.state.GetPendingValidator(subnetID, nodeID) 875 require.ErrorIs(err, database.ErrNotFound) 876 877 _, err = vm.state.GetCurrentValidator(subnetID, nodeID) 878 require.ErrorIs(err, database.ErrNotFound) 879 } 880 881 // test asset import 882 func TestAtomicImport(t *testing.T) { 883 require := require.New(t) 884 vm, baseDB, mutableSharedMemory := defaultVM(t, upgradetest.Latest) 885 vm.ctx.Lock.Lock() 886 defer vm.ctx.Lock.Unlock() 887 888 recipientKey := genesistest.DefaultFundedKeys[1] 889 importOwners := &secp256k1fx.OutputOwners{ 890 Threshold: 1, 891 Addrs: []ids.ShortID{recipientKey.Address()}, 892 } 893 894 m := atomic.NewMemory(prefixdb.New([]byte{5}, baseDB)) 895 mutableSharedMemory.SharedMemory = m.NewSharedMemory(vm.ctx.ChainID) 896 897 wallet := newWallet(t, vm, walletConfig{}) 898 _, err := wallet.IssueImportTx( 899 vm.ctx.XChainID, 900 importOwners, 901 ) 902 require.ErrorIs(err, walletbuilder.ErrInsufficientFunds) 903 904 // Provide the avm UTXO 905 peerSharedMemory := m.NewSharedMemory(vm.ctx.XChainID) 906 utxoID := avax.UTXOID{ 907 TxID: ids.GenerateTestID(), 908 OutputIndex: 1, 909 } 910 utxo := &avax.UTXO{ 911 UTXOID: utxoID, 912 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 913 Out: &secp256k1fx.TransferOutput{ 914 Amt: 50 * units.MicroAvax, 915 OutputOwners: *importOwners, 916 }, 917 } 918 utxoBytes, err := txs.Codec.Marshal(txs.CodecVersion, utxo) 919 require.NoError(err) 920 921 inputID := utxo.InputID() 922 require.NoError(peerSharedMemory.Apply(map[ids.ID]*atomic.Requests{ 923 vm.ctx.ChainID: { 924 PutRequests: []*atomic.Element{ 925 { 926 Key: inputID[:], 927 Value: utxoBytes, 928 Traits: [][]byte{ 929 recipientKey.Address().Bytes(), 930 }, 931 }, 932 }, 933 }, 934 })) 935 936 // The wallet must be re-loaded because the shared memory has changed 937 wallet = newWallet(t, vm, walletConfig{}) 938 tx, err := wallet.IssueImportTx( 939 vm.ctx.XChainID, 940 importOwners, 941 ) 942 require.NoError(err) 943 944 vm.ctx.Lock.Unlock() 945 require.NoError(vm.issueTxFromRPC(tx)) 946 vm.ctx.Lock.Lock() 947 require.NoError(buildAndAcceptStandardBlock(vm)) 948 949 _, txStatus, err := vm.state.GetTx(tx.ID()) 950 require.NoError(err) 951 require.Equal(status.Committed, txStatus) 952 953 inputID = utxoID.InputID() 954 _, err = vm.ctx.SharedMemory.Get(vm.ctx.XChainID, [][]byte{inputID[:]}) 955 require.ErrorIs(err, database.ErrNotFound) 956 } 957 958 // test optimistic asset import 959 func TestOptimisticAtomicImport(t *testing.T) { 960 require := require.New(t) 961 vm, _, _ := defaultVM(t, upgradetest.ApricotPhase3) 962 vm.ctx.Lock.Lock() 963 defer vm.ctx.Lock.Unlock() 964 965 tx := &txs.Tx{Unsigned: &txs.ImportTx{ 966 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 967 NetworkID: vm.ctx.NetworkID, 968 BlockchainID: vm.ctx.ChainID, 969 }}, 970 SourceChain: vm.ctx.XChainID, 971 ImportedInputs: []*avax.TransferableInput{{ 972 UTXOID: avax.UTXOID{ 973 TxID: ids.Empty.Prefix(1), 974 OutputIndex: 1, 975 }, 976 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 977 In: &secp256k1fx.TransferInput{ 978 Amt: 50000, 979 }, 980 }}, 981 }} 982 require.NoError(tx.Initialize(txs.Codec)) 983 984 preferredID := vm.manager.Preferred() 985 preferred, err := vm.manager.GetBlock(preferredID) 986 require.NoError(err) 987 preferredHeight := preferred.Height() 988 989 statelessBlk, err := block.NewApricotAtomicBlock( 990 preferredID, 991 preferredHeight+1, 992 tx, 993 ) 994 require.NoError(err) 995 996 blk := vm.manager.NewBlock(statelessBlk) 997 998 err = blk.Verify(context.Background()) 999 require.ErrorIs(err, database.ErrNotFound) // erred due to missing shared memory UTXOs 1000 1001 require.NoError(vm.SetState(context.Background(), snow.Bootstrapping)) 1002 1003 require.NoError(blk.Verify(context.Background())) // skips shared memory UTXO verification during bootstrapping 1004 1005 require.NoError(blk.Accept(context.Background())) 1006 1007 require.NoError(vm.SetState(context.Background(), snow.NormalOp)) 1008 1009 _, txStatus, err := vm.state.GetTx(tx.ID()) 1010 require.NoError(err) 1011 1012 require.Equal(status.Committed, txStatus) 1013 } 1014 1015 // test restarting the node 1016 func TestRestartFullyAccepted(t *testing.T) { 1017 require := require.New(t) 1018 db := memdb.New() 1019 1020 firstDB := prefixdb.New([]byte{}, db) 1021 firstVM := &VM{Config: config.Config{ 1022 Chains: chains.TestManager, 1023 Validators: validators.NewManager(), 1024 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1025 MinStakeDuration: defaultMinStakingDuration, 1026 MaxStakeDuration: defaultMaxStakingDuration, 1027 RewardConfig: defaultRewardConfig, 1028 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1029 }} 1030 1031 firstCtx := snowtest.Context(t, snowtest.PChainID) 1032 1033 genesisBytes := genesistest.NewBytes(t, genesistest.Config{}) 1034 1035 baseDB := memdb.New() 1036 atomicDB := prefixdb.New([]byte{1}, baseDB) 1037 m := atomic.NewMemory(atomicDB) 1038 firstCtx.SharedMemory = m.NewSharedMemory(firstCtx.ChainID) 1039 1040 initialClkTime := latestForkTime.Add(time.Second) 1041 firstVM.clock.Set(initialClkTime) 1042 firstCtx.Lock.Lock() 1043 1044 firstMsgChan := make(chan common.Message, 1) 1045 require.NoError(firstVM.Initialize( 1046 context.Background(), 1047 firstCtx, 1048 firstDB, 1049 genesisBytes, 1050 nil, 1051 nil, 1052 firstMsgChan, 1053 nil, 1054 nil, 1055 )) 1056 1057 genesisID, err := firstVM.LastAccepted(context.Background()) 1058 require.NoError(err) 1059 1060 // include a tx to make the block be accepted 1061 tx := &txs.Tx{Unsigned: &txs.ImportTx{ 1062 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 1063 NetworkID: firstVM.ctx.NetworkID, 1064 BlockchainID: firstVM.ctx.ChainID, 1065 }}, 1066 SourceChain: firstVM.ctx.XChainID, 1067 ImportedInputs: []*avax.TransferableInput{{ 1068 UTXOID: avax.UTXOID{ 1069 TxID: ids.Empty.Prefix(1), 1070 OutputIndex: 1, 1071 }, 1072 Asset: avax.Asset{ID: firstVM.ctx.AVAXAssetID}, 1073 In: &secp256k1fx.TransferInput{ 1074 Amt: 50000, 1075 }, 1076 }}, 1077 }} 1078 require.NoError(tx.Initialize(txs.Codec)) 1079 1080 nextChainTime := initialClkTime.Add(time.Second) 1081 firstVM.clock.Set(initialClkTime) 1082 1083 preferredID := firstVM.manager.Preferred() 1084 preferred, err := firstVM.manager.GetBlock(preferredID) 1085 require.NoError(err) 1086 preferredHeight := preferred.Height() 1087 1088 statelessBlk, err := block.NewBanffStandardBlock( 1089 nextChainTime, 1090 preferredID, 1091 preferredHeight+1, 1092 []*txs.Tx{tx}, 1093 ) 1094 require.NoError(err) 1095 1096 firstAdvanceTimeBlk := firstVM.manager.NewBlock(statelessBlk) 1097 1098 nextChainTime = nextChainTime.Add(2 * time.Second) 1099 firstVM.clock.Set(nextChainTime) 1100 require.NoError(firstAdvanceTimeBlk.Verify(context.Background())) 1101 require.NoError(firstAdvanceTimeBlk.Accept(context.Background())) 1102 1103 require.NoError(firstVM.Shutdown(context.Background())) 1104 firstCtx.Lock.Unlock() 1105 1106 secondVM := &VM{Config: config.Config{ 1107 Chains: chains.TestManager, 1108 Validators: validators.NewManager(), 1109 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1110 MinStakeDuration: defaultMinStakingDuration, 1111 MaxStakeDuration: defaultMaxStakingDuration, 1112 RewardConfig: defaultRewardConfig, 1113 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1114 }} 1115 1116 secondCtx := snowtest.Context(t, snowtest.PChainID) 1117 secondCtx.SharedMemory = firstCtx.SharedMemory 1118 secondVM.clock.Set(initialClkTime) 1119 secondCtx.Lock.Lock() 1120 defer func() { 1121 require.NoError(secondVM.Shutdown(context.Background())) 1122 secondCtx.Lock.Unlock() 1123 }() 1124 1125 secondDB := prefixdb.New([]byte{}, db) 1126 secondMsgChan := make(chan common.Message, 1) 1127 require.NoError(secondVM.Initialize( 1128 context.Background(), 1129 secondCtx, 1130 secondDB, 1131 genesisBytes, 1132 nil, 1133 nil, 1134 secondMsgChan, 1135 nil, 1136 nil, 1137 )) 1138 1139 lastAccepted, err := secondVM.LastAccepted(context.Background()) 1140 require.NoError(err) 1141 require.Equal(genesisID, lastAccepted) 1142 } 1143 1144 // test bootstrapping the node 1145 func TestBootstrapPartiallyAccepted(t *testing.T) { 1146 require := require.New(t) 1147 1148 baseDB := memdb.New() 1149 vmDB := prefixdb.New(chains.VMDBPrefix, baseDB) 1150 bootstrappingDB := prefixdb.New(chains.ChainBootstrappingDBPrefix, baseDB) 1151 1152 vm := &VM{Config: config.Config{ 1153 Chains: chains.TestManager, 1154 Validators: validators.NewManager(), 1155 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1156 MinStakeDuration: defaultMinStakingDuration, 1157 MaxStakeDuration: defaultMaxStakingDuration, 1158 RewardConfig: defaultRewardConfig, 1159 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1160 }} 1161 1162 initialClkTime := latestForkTime.Add(time.Second) 1163 vm.clock.Set(initialClkTime) 1164 ctx := snowtest.Context(t, snowtest.PChainID) 1165 1166 atomicDB := prefixdb.New([]byte{1}, baseDB) 1167 m := atomic.NewMemory(atomicDB) 1168 ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) 1169 1170 consensusCtx := snowtest.ConsensusContext(ctx) 1171 ctx.Lock.Lock() 1172 1173 msgChan := make(chan common.Message, 1) 1174 require.NoError(vm.Initialize( 1175 context.Background(), 1176 ctx, 1177 vmDB, 1178 genesistest.NewBytes(t, genesistest.Config{}), 1179 nil, 1180 nil, 1181 msgChan, 1182 nil, 1183 nil, 1184 )) 1185 1186 // include a tx to make the block be accepted 1187 tx := &txs.Tx{Unsigned: &txs.ImportTx{ 1188 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 1189 NetworkID: vm.ctx.NetworkID, 1190 BlockchainID: vm.ctx.ChainID, 1191 }}, 1192 SourceChain: vm.ctx.XChainID, 1193 ImportedInputs: []*avax.TransferableInput{{ 1194 UTXOID: avax.UTXOID{ 1195 TxID: ids.Empty.Prefix(1), 1196 OutputIndex: 1, 1197 }, 1198 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 1199 In: &secp256k1fx.TransferInput{ 1200 Amt: 50000, 1201 }, 1202 }}, 1203 }} 1204 require.NoError(tx.Initialize(txs.Codec)) 1205 1206 nextChainTime := initialClkTime.Add(time.Second) 1207 1208 preferredID := vm.manager.Preferred() 1209 preferred, err := vm.manager.GetBlock(preferredID) 1210 require.NoError(err) 1211 preferredHeight := preferred.Height() 1212 1213 statelessBlk, err := block.NewBanffStandardBlock( 1214 nextChainTime, 1215 preferredID, 1216 preferredHeight+1, 1217 []*txs.Tx{tx}, 1218 ) 1219 require.NoError(err) 1220 1221 advanceTimeBlk := vm.manager.NewBlock(statelessBlk) 1222 require.NoError(err) 1223 1224 advanceTimeBlkID := advanceTimeBlk.ID() 1225 advanceTimeBlkBytes := advanceTimeBlk.Bytes() 1226 1227 peerID := ids.BuildTestNodeID([]byte{1, 2, 3, 4, 5, 4, 3, 2, 1}) 1228 beacons := validators.NewManager() 1229 require.NoError(beacons.AddStaker(ctx.SubnetID, peerID, nil, ids.Empty, 1)) 1230 1231 benchlist := benchlist.NewNoBenchlist() 1232 timeoutManager, err := timeout.NewManager( 1233 &timer.AdaptiveTimeoutConfig{ 1234 InitialTimeout: time.Millisecond, 1235 MinimumTimeout: time.Millisecond, 1236 MaximumTimeout: 10 * time.Second, 1237 TimeoutHalflife: 5 * time.Minute, 1238 TimeoutCoefficient: 1.25, 1239 }, 1240 benchlist, 1241 prometheus.NewRegistry(), 1242 prometheus.NewRegistry(), 1243 ) 1244 require.NoError(err) 1245 1246 go timeoutManager.Dispatch() 1247 defer timeoutManager.Stop() 1248 1249 chainRouter := &router.ChainRouter{} 1250 1251 metrics := prometheus.NewRegistry() 1252 mc, err := message.NewCreator(logging.NoLog{}, metrics, constants.DefaultNetworkCompressionType, 10*time.Second) 1253 require.NoError(err) 1254 1255 require.NoError(chainRouter.Initialize( 1256 ids.EmptyNodeID, 1257 logging.NoLog{}, 1258 timeoutManager, 1259 time.Second, 1260 set.Set[ids.ID]{}, 1261 true, 1262 set.Set[ids.ID]{}, 1263 nil, 1264 router.HealthConfig{}, 1265 prometheus.NewRegistry(), 1266 )) 1267 1268 externalSender := &sendertest.External{TB: t} 1269 externalSender.Default(true) 1270 1271 // Passes messages from the consensus engine to the network 1272 sender, err := sender.New( 1273 consensusCtx, 1274 mc, 1275 externalSender, 1276 chainRouter, 1277 timeoutManager, 1278 p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 1279 subnets.New(consensusCtx.NodeID, subnets.Config{}), 1280 prometheus.NewRegistry(), 1281 ) 1282 require.NoError(err) 1283 1284 isBootstrapped := false 1285 bootstrapTracker := &enginetest.BootstrapTracker{ 1286 T: t, 1287 IsBootstrappedF: func() bool { 1288 return isBootstrapped 1289 }, 1290 BootstrappedF: func(ids.ID) { 1291 isBootstrapped = true 1292 }, 1293 } 1294 1295 peers := tracker.NewPeers() 1296 totalWeight, err := beacons.TotalWeight(ctx.SubnetID) 1297 require.NoError(err) 1298 startup := tracker.NewStartup(peers, (totalWeight+1)/2) 1299 beacons.RegisterSetCallbackListener(ctx.SubnetID, startup) 1300 1301 // The engine handles consensus 1302 snowGetHandler, err := snowgetter.New( 1303 vm, 1304 sender, 1305 consensusCtx.Log, 1306 time.Second, 1307 2000, 1308 consensusCtx.Registerer, 1309 ) 1310 require.NoError(err) 1311 1312 peerTracker, err := p2p.NewPeerTracker( 1313 ctx.Log, 1314 "peer_tracker", 1315 consensusCtx.Registerer, 1316 set.Of(ctx.NodeID), 1317 nil, 1318 ) 1319 require.NoError(err) 1320 1321 bootstrapConfig := bootstrap.Config{ 1322 NonVerifyingParse: vm.ParseBlock, 1323 AllGetsServer: snowGetHandler, 1324 Ctx: consensusCtx, 1325 Beacons: beacons, 1326 SampleK: beacons.Count(ctx.SubnetID), 1327 StartupTracker: startup, 1328 PeerTracker: peerTracker, 1329 Sender: sender, 1330 BootstrapTracker: bootstrapTracker, 1331 AncestorsMaxContainersReceived: 2000, 1332 DB: bootstrappingDB, 1333 VM: vm, 1334 } 1335 1336 // Asynchronously passes messages from the network to the consensus engine 1337 cpuTracker, err := timetracker.NewResourceTracker( 1338 prometheus.NewRegistry(), 1339 resource.NoUsage, 1340 meter.ContinuousFactory{}, 1341 time.Second, 1342 ) 1343 require.NoError(err) 1344 1345 h, err := handler.New( 1346 bootstrapConfig.Ctx, 1347 beacons, 1348 msgChan, 1349 time.Hour, 1350 2, 1351 cpuTracker, 1352 vm, 1353 subnets.New(ctx.NodeID, subnets.Config{}), 1354 tracker.NewPeers(), 1355 peerTracker, 1356 prometheus.NewRegistry(), 1357 ) 1358 require.NoError(err) 1359 1360 engineConfig := smeng.Config{ 1361 Ctx: bootstrapConfig.Ctx, 1362 AllGetsServer: snowGetHandler, 1363 VM: bootstrapConfig.VM, 1364 Sender: bootstrapConfig.Sender, 1365 Validators: beacons, 1366 Params: snowball.Parameters{ 1367 K: 1, 1368 AlphaPreference: 1, 1369 AlphaConfidence: 1, 1370 Beta: 20, 1371 ConcurrentRepolls: 1, 1372 OptimalProcessing: 1, 1373 MaxOutstandingItems: 1, 1374 MaxItemProcessingTime: 1, 1375 }, 1376 Consensus: &smcon.Topological{}, 1377 } 1378 engine, err := smeng.New(engineConfig) 1379 require.NoError(err) 1380 1381 bootstrapper, err := bootstrap.New( 1382 bootstrapConfig, 1383 engine.Start, 1384 ) 1385 require.NoError(err) 1386 1387 h.SetEngineManager(&handler.EngineManager{ 1388 Avalanche: &handler.Engine{ 1389 StateSyncer: nil, 1390 Bootstrapper: bootstrapper, 1391 Consensus: engine, 1392 }, 1393 Snowman: &handler.Engine{ 1394 StateSyncer: nil, 1395 Bootstrapper: bootstrapper, 1396 Consensus: engine, 1397 }, 1398 }) 1399 1400 consensusCtx.State.Set(snow.EngineState{ 1401 Type: p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 1402 State: snow.NormalOp, 1403 }) 1404 1405 // Allow incoming messages to be routed to the new chain 1406 chainRouter.AddChain(context.Background(), h) 1407 ctx.Lock.Unlock() 1408 1409 h.Start(context.Background(), false) 1410 1411 ctx.Lock.Lock() 1412 var reqID uint32 1413 externalSender.SendF = func(msg message.OutboundMessage, config common.SendConfig, _ ids.ID, _ subnets.Allower) set.Set[ids.NodeID] { 1414 inMsg, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) 1415 require.NoError(err) 1416 require.Equal(message.GetAcceptedFrontierOp, inMsg.Op()) 1417 1418 requestID, ok := message.GetRequestID(inMsg.Message()) 1419 require.True(ok) 1420 1421 reqID = requestID 1422 return config.NodeIDs 1423 } 1424 1425 peerTracker.Connected(peerID, version.CurrentApp) 1426 require.NoError(bootstrapper.Connected(context.Background(), peerID, version.CurrentApp)) 1427 1428 externalSender.SendF = func(msg message.OutboundMessage, config common.SendConfig, _ ids.ID, _ subnets.Allower) set.Set[ids.NodeID] { 1429 inMsgIntf, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) 1430 require.NoError(err) 1431 require.Equal(message.GetAcceptedOp, inMsgIntf.Op()) 1432 inMsg := inMsgIntf.Message().(*p2ppb.GetAccepted) 1433 1434 reqID = inMsg.RequestId 1435 return config.NodeIDs 1436 } 1437 1438 require.NoError(bootstrapper.AcceptedFrontier(context.Background(), peerID, reqID, advanceTimeBlkID)) 1439 1440 externalSender.SendF = func(msg message.OutboundMessage, config common.SendConfig, _ ids.ID, _ subnets.Allower) set.Set[ids.NodeID] { 1441 inMsgIntf, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) 1442 require.NoError(err) 1443 require.Equal(message.GetAncestorsOp, inMsgIntf.Op()) 1444 inMsg := inMsgIntf.Message().(*p2ppb.GetAncestors) 1445 1446 reqID = inMsg.RequestId 1447 1448 containerID, err := ids.ToID(inMsg.ContainerId) 1449 require.NoError(err) 1450 require.Equal(advanceTimeBlkID, containerID) 1451 return config.NodeIDs 1452 } 1453 1454 frontier := set.Of(advanceTimeBlkID) 1455 require.NoError(bootstrapper.Accepted(context.Background(), peerID, reqID, frontier)) 1456 1457 externalSender.SendF = func(msg message.OutboundMessage, config common.SendConfig, _ ids.ID, _ subnets.Allower) set.Set[ids.NodeID] { 1458 inMsg, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) 1459 require.NoError(err) 1460 require.Equal(message.GetAcceptedFrontierOp, inMsg.Op()) 1461 1462 requestID, ok := message.GetRequestID(inMsg.Message()) 1463 require.True(ok) 1464 1465 reqID = requestID 1466 return config.NodeIDs 1467 } 1468 1469 require.NoError(bootstrapper.Ancestors(context.Background(), peerID, reqID, [][]byte{advanceTimeBlkBytes})) 1470 1471 externalSender.SendF = func(msg message.OutboundMessage, config common.SendConfig, _ ids.ID, _ subnets.Allower) set.Set[ids.NodeID] { 1472 inMsgIntf, err := mc.Parse(msg.Bytes(), ctx.NodeID, func() {}) 1473 require.NoError(err) 1474 require.Equal(message.GetAcceptedOp, inMsgIntf.Op()) 1475 inMsg := inMsgIntf.Message().(*p2ppb.GetAccepted) 1476 1477 reqID = inMsg.RequestId 1478 return config.NodeIDs 1479 } 1480 1481 require.NoError(bootstrapper.AcceptedFrontier(context.Background(), peerID, reqID, advanceTimeBlkID)) 1482 1483 externalSender.SendF = nil 1484 externalSender.CantSend = false 1485 1486 require.NoError(bootstrapper.Accepted(context.Background(), peerID, reqID, frontier)) 1487 require.Equal(advanceTimeBlk.ID(), vm.manager.Preferred()) 1488 1489 ctx.Lock.Unlock() 1490 chainRouter.Shutdown(context.Background()) 1491 } 1492 1493 func TestUnverifiedParent(t *testing.T) { 1494 require := require.New(t) 1495 1496 vm := &VM{Config: config.Config{ 1497 Chains: chains.TestManager, 1498 Validators: validators.NewManager(), 1499 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1500 MinStakeDuration: defaultMinStakingDuration, 1501 MaxStakeDuration: defaultMaxStakingDuration, 1502 RewardConfig: defaultRewardConfig, 1503 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1504 }} 1505 1506 initialClkTime := latestForkTime.Add(time.Second) 1507 vm.clock.Set(initialClkTime) 1508 ctx := snowtest.Context(t, snowtest.PChainID) 1509 ctx.Lock.Lock() 1510 defer func() { 1511 require.NoError(vm.Shutdown(context.Background())) 1512 ctx.Lock.Unlock() 1513 }() 1514 1515 msgChan := make(chan common.Message, 1) 1516 require.NoError(vm.Initialize( 1517 context.Background(), 1518 ctx, 1519 memdb.New(), 1520 genesistest.NewBytes(t, genesistest.Config{}), 1521 nil, 1522 nil, 1523 msgChan, 1524 nil, 1525 nil, 1526 )) 1527 1528 // include a tx1 to make the block be accepted 1529 tx1 := &txs.Tx{Unsigned: &txs.ImportTx{ 1530 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 1531 NetworkID: vm.ctx.NetworkID, 1532 BlockchainID: vm.ctx.ChainID, 1533 }}, 1534 SourceChain: vm.ctx.XChainID, 1535 ImportedInputs: []*avax.TransferableInput{{ 1536 UTXOID: avax.UTXOID{ 1537 TxID: ids.Empty.Prefix(1), 1538 OutputIndex: 1, 1539 }, 1540 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 1541 In: &secp256k1fx.TransferInput{ 1542 Amt: 50000, 1543 }, 1544 }}, 1545 }} 1546 require.NoError(tx1.Initialize(txs.Codec)) 1547 1548 nextChainTime := initialClkTime.Add(time.Second) 1549 1550 preferredID := vm.manager.Preferred() 1551 preferred, err := vm.manager.GetBlock(preferredID) 1552 require.NoError(err) 1553 preferredHeight := preferred.Height() 1554 1555 statelessBlk, err := block.NewBanffStandardBlock( 1556 nextChainTime, 1557 preferredID, 1558 preferredHeight+1, 1559 []*txs.Tx{tx1}, 1560 ) 1561 require.NoError(err) 1562 firstAdvanceTimeBlk := vm.manager.NewBlock(statelessBlk) 1563 require.NoError(firstAdvanceTimeBlk.Verify(context.Background())) 1564 1565 // include a tx2 to make the block be accepted 1566 tx2 := &txs.Tx{Unsigned: &txs.ImportTx{ 1567 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 1568 NetworkID: vm.ctx.NetworkID, 1569 BlockchainID: vm.ctx.ChainID, 1570 }}, 1571 SourceChain: vm.ctx.XChainID, 1572 ImportedInputs: []*avax.TransferableInput{{ 1573 UTXOID: avax.UTXOID{ 1574 TxID: ids.Empty.Prefix(2), 1575 OutputIndex: 2, 1576 }, 1577 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 1578 In: &secp256k1fx.TransferInput{ 1579 Amt: 50000, 1580 }, 1581 }}, 1582 }} 1583 require.NoError(tx2.Initialize(txs.Codec)) 1584 nextChainTime = nextChainTime.Add(time.Second) 1585 vm.clock.Set(nextChainTime) 1586 statelessSecondAdvanceTimeBlk, err := block.NewBanffStandardBlock( 1587 nextChainTime, 1588 firstAdvanceTimeBlk.ID(), 1589 firstAdvanceTimeBlk.Height()+1, 1590 []*txs.Tx{tx2}, 1591 ) 1592 require.NoError(err) 1593 secondAdvanceTimeBlk := vm.manager.NewBlock(statelessSecondAdvanceTimeBlk) 1594 1595 require.Equal(secondAdvanceTimeBlk.Parent(), firstAdvanceTimeBlk.ID()) 1596 require.NoError(secondAdvanceTimeBlk.Verify(context.Background())) 1597 } 1598 1599 func TestMaxStakeAmount(t *testing.T) { 1600 vm, _, _ := defaultVM(t, upgradetest.Latest) 1601 vm.ctx.Lock.Lock() 1602 defer vm.ctx.Lock.Unlock() 1603 1604 nodeID := genesistest.DefaultNodeIDs[0] 1605 1606 tests := []struct { 1607 description string 1608 startTime time.Time 1609 endTime time.Time 1610 }{ 1611 { 1612 description: "[validator.StartTime] == [startTime] < [endTime] == [validator.EndTime]", 1613 startTime: genesistest.DefaultValidatorStartTime, 1614 endTime: genesistest.DefaultValidatorEndTime, 1615 }, 1616 { 1617 description: "[validator.StartTime] < [startTime] < [endTime] == [validator.EndTime]", 1618 startTime: genesistest.DefaultValidatorStartTime.Add(time.Minute), 1619 endTime: genesistest.DefaultValidatorEndTime, 1620 }, 1621 { 1622 description: "[validator.StartTime] == [startTime] < [endTime] < [validator.EndTime]", 1623 startTime: genesistest.DefaultValidatorStartTime, 1624 endTime: genesistest.DefaultValidatorEndTime.Add(-time.Minute), 1625 }, 1626 { 1627 description: "[validator.StartTime] < [startTime] < [endTime] < [validator.EndTime]", 1628 startTime: genesistest.DefaultValidatorStartTime.Add(time.Minute), 1629 endTime: genesistest.DefaultValidatorEndTime.Add(-time.Minute), 1630 }, 1631 } 1632 1633 for _, test := range tests { 1634 t.Run(test.description, func(t *testing.T) { 1635 require := require.New(t) 1636 staker, err := txexecutor.GetValidator(vm.state, constants.PrimaryNetworkID, nodeID) 1637 require.NoError(err) 1638 1639 amount, err := txexecutor.GetMaxWeight(vm.state, staker, test.startTime, test.endTime) 1640 require.NoError(err) 1641 require.Equal(genesistest.DefaultValidatorWeight, amount) 1642 }) 1643 } 1644 } 1645 1646 func TestUptimeDisallowedWithRestart(t *testing.T) { 1647 require := require.New(t) 1648 latestForkTime = genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration) 1649 db := memdb.New() 1650 1651 firstDB := prefixdb.New([]byte{}, db) 1652 const firstUptimePercentage = 20 // 20% 1653 firstVM := &VM{Config: config.Config{ 1654 Chains: chains.TestManager, 1655 UptimePercentage: firstUptimePercentage / 100., 1656 RewardConfig: defaultRewardConfig, 1657 Validators: validators.NewManager(), 1658 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1659 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1660 }} 1661 1662 firstCtx := snowtest.Context(t, snowtest.PChainID) 1663 firstCtx.Lock.Lock() 1664 1665 genesisBytes := genesistest.NewBytes(t, genesistest.Config{}) 1666 1667 firstMsgChan := make(chan common.Message, 1) 1668 require.NoError(firstVM.Initialize( 1669 context.Background(), 1670 firstCtx, 1671 firstDB, 1672 genesisBytes, 1673 nil, 1674 nil, 1675 firstMsgChan, 1676 nil, 1677 nil, 1678 )) 1679 1680 initialClkTime := latestForkTime.Add(time.Second) 1681 firstVM.clock.Set(initialClkTime) 1682 1683 // Set VM state to NormalOp, to start tracking validators' uptime 1684 require.NoError(firstVM.SetState(context.Background(), snow.Bootstrapping)) 1685 require.NoError(firstVM.SetState(context.Background(), snow.NormalOp)) 1686 1687 // Fast forward clock so that validators meet 20% uptime required for reward 1688 durationForReward := genesistest.DefaultValidatorEndTime.Sub(genesistest.DefaultValidatorStartTime) * firstUptimePercentage / 100 1689 vmStopTime := genesistest.DefaultValidatorStartTime.Add(durationForReward) 1690 firstVM.clock.Set(vmStopTime) 1691 1692 // Shutdown VM to stop all genesis validator uptime. 1693 // At this point they have been validating for the 20% uptime needed to be rewarded 1694 require.NoError(firstVM.Shutdown(context.Background())) 1695 firstCtx.Lock.Unlock() 1696 1697 // Restart the VM with a larger uptime requirement 1698 secondDB := prefixdb.New([]byte{}, db) 1699 const secondUptimePercentage = 21 // 21% > firstUptimePercentage, so uptime for reward is not met now 1700 secondVM := &VM{Config: config.Config{ 1701 Chains: chains.TestManager, 1702 UptimePercentage: secondUptimePercentage / 100., 1703 Validators: validators.NewManager(), 1704 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1705 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1706 }} 1707 1708 secondCtx := snowtest.Context(t, snowtest.PChainID) 1709 secondCtx.Lock.Lock() 1710 defer func() { 1711 require.NoError(secondVM.Shutdown(context.Background())) 1712 secondCtx.Lock.Unlock() 1713 }() 1714 1715 atomicDB := prefixdb.New([]byte{1}, db) 1716 m := atomic.NewMemory(atomicDB) 1717 secondCtx.SharedMemory = m.NewSharedMemory(secondCtx.ChainID) 1718 1719 secondMsgChan := make(chan common.Message, 1) 1720 require.NoError(secondVM.Initialize( 1721 context.Background(), 1722 secondCtx, 1723 secondDB, 1724 genesisBytes, 1725 nil, 1726 nil, 1727 secondMsgChan, 1728 nil, 1729 nil, 1730 )) 1731 1732 secondVM.clock.Set(vmStopTime) 1733 1734 // Set VM state to NormalOp, to start tracking validators' uptime 1735 require.NoError(secondVM.SetState(context.Background(), snow.Bootstrapping)) 1736 require.NoError(secondVM.SetState(context.Background(), snow.NormalOp)) 1737 1738 // after restart and change of uptime required for reward, push validators to their end of life 1739 secondVM.clock.Set(genesistest.DefaultValidatorEndTime) 1740 1741 // evaluate a genesis validator for reward 1742 blk, err := secondVM.Builder.BuildBlock(context.Background()) 1743 require.NoError(err) 1744 require.NoError(blk.Verify(context.Background())) 1745 1746 // Assert preferences are correct. 1747 // secondVM should prefer abort since uptime requirements are not met anymore 1748 oracleBlk := blk.(smcon.OracleBlock) 1749 options, err := oracleBlk.Options(context.Background()) 1750 require.NoError(err) 1751 1752 abort := options[0].(*blockexecutor.Block) 1753 require.IsType(&block.BanffAbortBlock{}, abort.Block) 1754 1755 commit := options[1].(*blockexecutor.Block) 1756 require.IsType(&block.BanffCommitBlock{}, commit.Block) 1757 1758 // Assert block tries to reward a genesis validator 1759 rewardTx := oracleBlk.(block.Block).Txs()[0].Unsigned 1760 require.IsType(&txs.RewardValidatorTx{}, rewardTx) 1761 txID := blk.(block.Block).Txs()[0].ID() 1762 1763 // Verify options and accept abort block 1764 require.NoError(commit.Verify(context.Background())) 1765 require.NoError(abort.Verify(context.Background())) 1766 require.NoError(blk.Accept(context.Background())) 1767 require.NoError(abort.Accept(context.Background())) 1768 require.NoError(secondVM.SetPreference(context.Background(), secondVM.manager.LastAccepted())) 1769 1770 // Verify that rewarded validator has been removed. 1771 // Note that test genesis has multiple validators 1772 // terminating at the same time. The rewarded validator 1773 // will the first by txID. To make the test more stable 1774 // (txID changes every time we change any parameter 1775 // of the tx creating the validator), we explicitly 1776 // check that rewarded validator is removed from staker set. 1777 _, txStatus, err := secondVM.state.GetTx(txID) 1778 require.NoError(err) 1779 require.Equal(status.Aborted, txStatus) 1780 1781 tx, _, err := secondVM.state.GetTx(rewardTx.(*txs.RewardValidatorTx).TxID) 1782 require.NoError(err) 1783 require.IsType(&txs.AddValidatorTx{}, tx.Unsigned) 1784 1785 valTx, _ := tx.Unsigned.(*txs.AddValidatorTx) 1786 _, err = secondVM.state.GetCurrentValidator(constants.PrimaryNetworkID, valTx.NodeID()) 1787 require.ErrorIs(err, database.ErrNotFound) 1788 } 1789 1790 func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { 1791 require := require.New(t) 1792 latestForkTime = genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration) 1793 1794 db := memdb.New() 1795 1796 vm := &VM{Config: config.Config{ 1797 Chains: chains.TestManager, 1798 UptimePercentage: .2, 1799 RewardConfig: defaultRewardConfig, 1800 Validators: validators.NewManager(), 1801 UptimeLockedCalculator: uptime.NewLockedCalculator(), 1802 UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, latestForkTime), 1803 }} 1804 1805 ctx := snowtest.Context(t, snowtest.PChainID) 1806 ctx.Lock.Lock() 1807 1808 atomicDB := prefixdb.New([]byte{1}, db) 1809 m := atomic.NewMemory(atomicDB) 1810 ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) 1811 1812 msgChan := make(chan common.Message, 1) 1813 appSender := &enginetest.Sender{T: t} 1814 require.NoError(vm.Initialize( 1815 context.Background(), 1816 ctx, 1817 db, 1818 genesistest.NewBytes(t, genesistest.Config{}), 1819 nil, 1820 nil, 1821 msgChan, 1822 nil, 1823 appSender, 1824 )) 1825 1826 defer func() { 1827 require.NoError(vm.Shutdown(context.Background())) 1828 ctx.Lock.Unlock() 1829 }() 1830 1831 initialClkTime := latestForkTime.Add(time.Second) 1832 vm.clock.Set(initialClkTime) 1833 1834 // Set VM state to NormalOp, to start tracking validators' uptime 1835 require.NoError(vm.SetState(context.Background(), snow.Bootstrapping)) 1836 require.NoError(vm.SetState(context.Background(), snow.NormalOp)) 1837 1838 // Fast forward clock to time for genesis validators to leave 1839 vm.clock.Set(genesistest.DefaultValidatorEndTime) 1840 1841 // evaluate a genesis validator for reward 1842 blk, err := vm.Builder.BuildBlock(context.Background()) 1843 require.NoError(err) 1844 require.NoError(blk.Verify(context.Background())) 1845 1846 // Assert preferences are correct. 1847 // vm should prefer abort since uptime requirements are not met. 1848 oracleBlk := blk.(smcon.OracleBlock) 1849 options, err := oracleBlk.Options(context.Background()) 1850 require.NoError(err) 1851 1852 abort := options[0].(*blockexecutor.Block) 1853 require.IsType(&block.BanffAbortBlock{}, abort.Block) 1854 1855 commit := options[1].(*blockexecutor.Block) 1856 require.IsType(&block.BanffCommitBlock{}, commit.Block) 1857 1858 // Assert block tries to reward a genesis validator 1859 rewardTx := oracleBlk.(block.Block).Txs()[0].Unsigned 1860 require.IsType(&txs.RewardValidatorTx{}, rewardTx) 1861 txID := blk.(block.Block).Txs()[0].ID() 1862 1863 // Verify options and accept abort block 1864 require.NoError(commit.Verify(context.Background())) 1865 require.NoError(abort.Verify(context.Background())) 1866 require.NoError(blk.Accept(context.Background())) 1867 require.NoError(abort.Accept(context.Background())) 1868 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1869 1870 // Verify that rewarded validator has been removed. 1871 // Note that test genesis has multiple validators 1872 // terminating at the same time. The rewarded validator 1873 // will the first by txID. To make the test more stable 1874 // (txID changes every time we change any parameter 1875 // of the tx creating the validator), we explicitly 1876 // check that rewarded validator is removed from staker set. 1877 _, txStatus, err := vm.state.GetTx(txID) 1878 require.NoError(err) 1879 require.Equal(status.Aborted, txStatus) 1880 1881 tx, _, err := vm.state.GetTx(rewardTx.(*txs.RewardValidatorTx).TxID) 1882 require.NoError(err) 1883 require.IsType(&txs.AddValidatorTx{}, tx.Unsigned) 1884 1885 valTx, _ := tx.Unsigned.(*txs.AddValidatorTx) 1886 _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, valTx.NodeID()) 1887 require.ErrorIs(err, database.ErrNotFound) 1888 } 1889 1890 func TestRemovePermissionedValidatorDuringAddPending(t *testing.T) { 1891 require := require.New(t) 1892 1893 validatorStartTime := latestForkTime.Add(txexecutor.SyncBound).Add(1 * time.Second) 1894 validatorEndTime := validatorStartTime.Add(360 * 24 * time.Hour) 1895 1896 vm, _, _ := defaultVM(t, upgradetest.Latest) 1897 vm.ctx.Lock.Lock() 1898 defer vm.ctx.Lock.Unlock() 1899 1900 wallet := newWallet(t, vm, walletConfig{}) 1901 1902 nodeID := ids.GenerateTestNodeID() 1903 sk, err := bls.NewSecretKey() 1904 require.NoError(err) 1905 rewardsOwner := &secp256k1fx.OutputOwners{ 1906 Threshold: 1, 1907 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 1908 } 1909 1910 addValidatorTx, err := wallet.IssueAddPermissionlessValidatorTx( 1911 &txs.SubnetValidator{ 1912 Validator: txs.Validator{ 1913 NodeID: nodeID, 1914 Start: uint64(validatorStartTime.Unix()), 1915 End: uint64(validatorEndTime.Unix()), 1916 Wght: defaultMaxValidatorStake, 1917 }, 1918 Subnet: constants.PrimaryNetworkID, 1919 }, 1920 signer.NewProofOfPossession(sk), 1921 vm.ctx.AVAXAssetID, 1922 rewardsOwner, 1923 rewardsOwner, 1924 reward.PercentDenominator, 1925 ) 1926 require.NoError(err) 1927 1928 vm.ctx.Lock.Unlock() 1929 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 1930 vm.ctx.Lock.Lock() 1931 require.NoError(buildAndAcceptStandardBlock(vm)) 1932 1933 createSubnetTx, err := wallet.IssueCreateSubnetTx( 1934 &secp256k1fx.OutputOwners{ 1935 Threshold: 1, 1936 Addrs: []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()}, 1937 }, 1938 ) 1939 require.NoError(err) 1940 1941 vm.ctx.Lock.Unlock() 1942 require.NoError(vm.issueTxFromRPC(createSubnetTx)) 1943 vm.ctx.Lock.Lock() 1944 require.NoError(buildAndAcceptStandardBlock(vm)) 1945 1946 subnetID := createSubnetTx.ID() 1947 addSubnetValidatorTx, err := wallet.IssueAddSubnetValidatorTx( 1948 &txs.SubnetValidator{ 1949 Validator: txs.Validator{ 1950 NodeID: nodeID, 1951 Start: uint64(validatorStartTime.Unix()), 1952 End: uint64(validatorEndTime.Unix()), 1953 Wght: defaultMaxValidatorStake, 1954 }, 1955 Subnet: subnetID, 1956 }, 1957 ) 1958 require.NoError(err) 1959 1960 removeSubnetValidatorTx, err := wallet.IssueRemoveSubnetValidatorTx( 1961 nodeID, 1962 subnetID, 1963 ) 1964 require.NoError(err) 1965 1966 lastAcceptedID := vm.state.GetLastAccepted() 1967 lastAcceptedHeight, err := vm.GetCurrentHeight(context.Background()) 1968 require.NoError(err) 1969 statelessBlock, err := block.NewBanffStandardBlock( 1970 vm.state.GetTimestamp(), 1971 lastAcceptedID, 1972 lastAcceptedHeight+1, 1973 []*txs.Tx{ 1974 addSubnetValidatorTx, 1975 removeSubnetValidatorTx, 1976 }, 1977 ) 1978 require.NoError(err) 1979 1980 blockBytes := statelessBlock.Bytes() 1981 block, err := vm.ParseBlock(context.Background(), blockBytes) 1982 require.NoError(err) 1983 require.NoError(block.Verify(context.Background())) 1984 require.NoError(block.Accept(context.Background())) 1985 require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) 1986 1987 _, err = vm.state.GetPendingValidator(subnetID, nodeID) 1988 require.ErrorIs(err, database.ErrNotFound) 1989 } 1990 1991 func TestTransferSubnetOwnershipTx(t *testing.T) { 1992 require := require.New(t) 1993 vm, _, _ := defaultVM(t, upgradetest.Latest) 1994 vm.ctx.Lock.Lock() 1995 defer vm.ctx.Lock.Unlock() 1996 1997 wallet := newWallet(t, vm, walletConfig{}) 1998 1999 expectedSubnetOwner := &secp256k1fx.OutputOwners{ 2000 Threshold: 1, 2001 Addrs: []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()}, 2002 } 2003 createSubnetTx, err := wallet.IssueCreateSubnetTx( 2004 expectedSubnetOwner, 2005 ) 2006 require.NoError(err) 2007 2008 vm.ctx.Lock.Unlock() 2009 require.NoError(vm.issueTxFromRPC(createSubnetTx)) 2010 vm.ctx.Lock.Lock() 2011 require.NoError(buildAndAcceptStandardBlock(vm)) 2012 2013 subnetID := createSubnetTx.ID() 2014 subnetOwner, err := vm.state.GetSubnetOwner(subnetID) 2015 require.NoError(err) 2016 require.Equal(expectedSubnetOwner, subnetOwner) 2017 2018 expectedSubnetOwner = &secp256k1fx.OutputOwners{ 2019 Threshold: 1, 2020 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 2021 } 2022 transferSubnetOwnershipTx, err := wallet.IssueTransferSubnetOwnershipTx( 2023 subnetID, 2024 expectedSubnetOwner, 2025 ) 2026 require.NoError(err) 2027 2028 vm.ctx.Lock.Unlock() 2029 require.NoError(vm.issueTxFromRPC(transferSubnetOwnershipTx)) 2030 vm.ctx.Lock.Lock() 2031 require.NoError(buildAndAcceptStandardBlock(vm)) 2032 2033 subnetOwner, err = vm.state.GetSubnetOwner(subnetID) 2034 require.NoError(err) 2035 require.Equal(expectedSubnetOwner, subnetOwner) 2036 } 2037 2038 func TestBaseTx(t *testing.T) { 2039 require := require.New(t) 2040 vm, _, _ := defaultVM(t, upgradetest.Durango) 2041 vm.ctx.Lock.Lock() 2042 defer vm.ctx.Lock.Unlock() 2043 2044 wallet := newWallet(t, vm, walletConfig{}) 2045 2046 baseTx, err := wallet.IssueBaseTx( 2047 []*avax.TransferableOutput{ 2048 { 2049 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 2050 Out: &secp256k1fx.TransferOutput{ 2051 Amt: 100 * units.MicroAvax, 2052 OutputOwners: secp256k1fx.OutputOwners{ 2053 Threshold: 1, 2054 Addrs: []ids.ShortID{ 2055 ids.GenerateTestShortID(), 2056 }, 2057 }, 2058 }, 2059 }, 2060 }, 2061 ) 2062 require.NoError(err) 2063 2064 vm.ctx.Lock.Unlock() 2065 require.NoError(vm.issueTxFromRPC(baseTx)) 2066 vm.ctx.Lock.Lock() 2067 require.NoError(buildAndAcceptStandardBlock(vm)) 2068 2069 _, txStatus, err := vm.state.GetTx(baseTx.ID()) 2070 require.NoError(err) 2071 require.Equal(status.Committed, txStatus) 2072 } 2073 2074 func TestPruneMempool(t *testing.T) { 2075 require := require.New(t) 2076 vm, _, _ := defaultVM(t, upgradetest.Latest) 2077 vm.ctx.Lock.Lock() 2078 defer vm.ctx.Lock.Unlock() 2079 2080 wallet := newWallet(t, vm, walletConfig{}) 2081 2082 // Create a tx that will be valid regardless of timestamp. 2083 baseTx, err := wallet.IssueBaseTx( 2084 []*avax.TransferableOutput{ 2085 { 2086 Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, 2087 Out: &secp256k1fx.TransferOutput{ 2088 Amt: 100 * units.MicroAvax, 2089 OutputOwners: secp256k1fx.OutputOwners{ 2090 Threshold: 1, 2091 Addrs: []ids.ShortID{ 2092 genesistest.DefaultFundedKeys[0].Address(), 2093 }, 2094 }, 2095 }, 2096 }, 2097 }, 2098 walletcommon.WithCustomAddresses(set.Of( 2099 genesistest.DefaultFundedKeys[0].Address(), 2100 )), 2101 ) 2102 require.NoError(err) 2103 2104 vm.ctx.Lock.Unlock() 2105 require.NoError(vm.issueTxFromRPC(baseTx)) 2106 vm.ctx.Lock.Lock() 2107 2108 // [baseTx] should be in the mempool. 2109 baseTxID := baseTx.ID() 2110 _, ok := vm.Builder.Get(baseTxID) 2111 require.True(ok) 2112 2113 // Create a tx that will be invalid after time advancement. 2114 var ( 2115 startTime = vm.clock.Time() 2116 endTime = startTime.Add(vm.MinStakeDuration) 2117 ) 2118 2119 sk, err := bls.NewSecretKey() 2120 require.NoError(err) 2121 2122 rewardsOwner := &secp256k1fx.OutputOwners{ 2123 Threshold: 1, 2124 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 2125 } 2126 addValidatorTx, err := wallet.IssueAddPermissionlessValidatorTx( 2127 &txs.SubnetValidator{ 2128 Validator: txs.Validator{ 2129 NodeID: ids.GenerateTestNodeID(), 2130 Start: uint64(startTime.Unix()), 2131 End: uint64(endTime.Unix()), 2132 Wght: defaultMinValidatorStake, 2133 }, 2134 Subnet: constants.PrimaryNetworkID, 2135 }, 2136 signer.NewProofOfPossession(sk), 2137 vm.ctx.AVAXAssetID, 2138 rewardsOwner, 2139 rewardsOwner, 2140 20000, 2141 walletcommon.WithCustomAddresses(set.Of( 2142 genesistest.DefaultFundedKeys[1].Address(), 2143 )), 2144 ) 2145 require.NoError(err) 2146 2147 vm.ctx.Lock.Unlock() 2148 require.NoError(vm.issueTxFromRPC(addValidatorTx)) 2149 vm.ctx.Lock.Lock() 2150 2151 // [addValidatorTx] and [baseTx] should be in the mempool. 2152 addValidatorTxID := addValidatorTx.ID() 2153 _, ok = vm.Builder.Get(addValidatorTxID) 2154 require.True(ok) 2155 _, ok = vm.Builder.Get(baseTxID) 2156 require.True(ok) 2157 2158 // Advance clock to [endTime], making [addValidatorTx] invalid. 2159 vm.clock.Set(endTime) 2160 2161 vm.ctx.Lock.Unlock() 2162 require.NoError(vm.pruneMempool()) 2163 vm.ctx.Lock.Lock() 2164 2165 // [addValidatorTx] should be ejected from the mempool. 2166 // [baseTx] should still be in the mempool. 2167 _, ok = vm.Builder.Get(addValidatorTxID) 2168 require.False(ok) 2169 _, ok = vm.Builder.Get(baseTxID) 2170 require.True(ok) 2171 }