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