github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/executor/advance_time_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package executor 5 6 import ( 7 "fmt" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/ava-labs/avalanchego/database" 14 "github.com/ava-labs/avalanchego/ids" 15 "github.com/ava-labs/avalanchego/snow/snowtest" 16 "github.com/ava-labs/avalanchego/upgrade/upgradetest" 17 "github.com/ava-labs/avalanchego/utils/constants" 18 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" 19 "github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest" 20 "github.com/ava-labs/avalanchego/vms/platformvm/reward" 21 "github.com/ava-labs/avalanchego/vms/platformvm/state" 22 "github.com/ava-labs/avalanchego/vms/platformvm/status" 23 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 24 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 25 ) 26 27 func newAdvanceTimeTx(t testing.TB, timestamp time.Time) (*txs.Tx, error) { 28 utx := &txs.AdvanceTimeTx{Time: uint64(timestamp.Unix())} 29 tx, err := txs.NewSigned(utx, txs.Codec, nil) 30 if err != nil { 31 return nil, err 32 } 33 return tx, tx.SyntacticVerify(snowtest.Context(t, snowtest.PChainID)) 34 } 35 36 // Ensure semantic verification updates the current and pending staker set 37 // for the primary network 38 func TestAdvanceTimeTxUpdatePrimaryNetworkStakers(t *testing.T) { 39 require := require.New(t) 40 env := newEnvironment(t, upgradetest.ApricotPhase5) 41 env.ctx.Lock.Lock() 42 defer env.ctx.Lock.Unlock() 43 dummyHeight := uint64(1) 44 45 // Case: Timestamp is after next validator start time 46 // Add a pending validator 47 pendingValidatorStartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Second) 48 pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) 49 nodeID := ids.GenerateTestNodeID() 50 addPendingValidatorTx := addPendingValidator( 51 t, 52 env, 53 pendingValidatorStartTime, 54 pendingValidatorEndTime, 55 nodeID, 56 []*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]}, 57 ) 58 59 tx, err := newAdvanceTimeTx(t, pendingValidatorStartTime) 60 require.NoError(err) 61 62 onCommitState, err := state.NewDiff(lastAcceptedID, env) 63 require.NoError(err) 64 65 onAbortState, err := state.NewDiff(lastAcceptedID, env) 66 require.NoError(err) 67 68 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 69 executor := ProposalTxExecutor{ 70 OnCommitState: onCommitState, 71 OnAbortState: onAbortState, 72 Backend: &env.backend, 73 FeeCalculator: feeCalculator, 74 Tx: tx, 75 } 76 require.NoError(tx.Unsigned.Visit(&executor)) 77 78 validatorStaker, err := executor.OnCommitState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 79 require.NoError(err) 80 require.Equal(addPendingValidatorTx.ID(), validatorStaker.TxID) 81 require.Equal(uint64(1370), validatorStaker.PotentialReward) // See rewards tests to explain why 1370 82 83 _, err = executor.OnCommitState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) 84 require.ErrorIs(err, database.ErrNotFound) 85 86 _, err = executor.OnAbortState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) 87 require.ErrorIs(err, database.ErrNotFound) 88 89 validatorStaker, err = executor.OnAbortState.GetPendingValidator(constants.PrimaryNetworkID, nodeID) 90 require.NoError(err) 91 require.Equal(addPendingValidatorTx.ID(), validatorStaker.TxID) 92 93 // Test VM validators 94 require.NoError(executor.OnCommitState.Apply(env.state)) 95 96 env.state.SetHeight(dummyHeight) 97 require.NoError(env.state.Commit()) 98 _, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, nodeID) 99 require.True(ok) 100 } 101 102 // Ensure semantic verification fails when proposed timestamp is at or before current timestamp 103 func TestAdvanceTimeTxTimestampTooEarly(t *testing.T) { 104 require := require.New(t) 105 env := newEnvironment(t, upgradetest.ApricotPhase5) 106 107 tx, err := newAdvanceTimeTx(t, env.state.GetTimestamp()) 108 require.NoError(err) 109 110 onCommitState, err := state.NewDiff(lastAcceptedID, env) 111 require.NoError(err) 112 113 onAbortState, err := state.NewDiff(lastAcceptedID, env) 114 require.NoError(err) 115 116 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 117 executor := ProposalTxExecutor{ 118 OnCommitState: onCommitState, 119 OnAbortState: onAbortState, 120 Backend: &env.backend, 121 FeeCalculator: feeCalculator, 122 Tx: tx, 123 } 124 err = tx.Unsigned.Visit(&executor) 125 require.ErrorIs(err, ErrChildBlockNotAfterParent) 126 } 127 128 // Ensure semantic verification fails when proposed timestamp is after next validator set change time 129 func TestAdvanceTimeTxTimestampTooLate(t *testing.T) { 130 require := require.New(t) 131 env := newEnvironment(t, upgradetest.ApricotPhase5) 132 env.ctx.Lock.Lock() 133 defer env.ctx.Lock.Unlock() 134 135 // Case: Timestamp is after next validator start time 136 // Add a pending validator 137 pendingValidatorStartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Second) 138 pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) 139 nodeID := ids.GenerateTestNodeID() 140 addPendingValidator(t, env, pendingValidatorStartTime, pendingValidatorEndTime, nodeID, []*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]}) 141 142 { 143 tx, err := newAdvanceTimeTx(t, pendingValidatorStartTime.Add(1*time.Second)) 144 require.NoError(err) 145 146 onCommitState, err := state.NewDiff(lastAcceptedID, env) 147 require.NoError(err) 148 149 onAbortState, err := state.NewDiff(lastAcceptedID, env) 150 require.NoError(err) 151 152 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 153 executor := ProposalTxExecutor{ 154 OnCommitState: onCommitState, 155 OnAbortState: onAbortState, 156 Backend: &env.backend, 157 FeeCalculator: feeCalculator, 158 Tx: tx, 159 } 160 err = tx.Unsigned.Visit(&executor) 161 require.ErrorIs(err, ErrChildBlockAfterStakerChangeTime) 162 } 163 164 // Case: Timestamp is after next validator end time 165 env = newEnvironment(t, upgradetest.ApricotPhase5) 166 env.ctx.Lock.Lock() 167 defer env.ctx.Lock.Unlock() 168 169 // fast forward clock to 10 seconds before genesis validators stop validating 170 env.clk.Set(genesistest.DefaultValidatorEndTime.Add(-10 * time.Second)) 171 172 { 173 // Proposes advancing timestamp to 1 second after genesis validators stop validating 174 tx, err := newAdvanceTimeTx(t, genesistest.DefaultValidatorEndTime.Add(1*time.Second)) 175 require.NoError(err) 176 177 onCommitState, err := state.NewDiff(lastAcceptedID, env) 178 require.NoError(err) 179 180 onAbortState, err := state.NewDiff(lastAcceptedID, env) 181 require.NoError(err) 182 183 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 184 executor := ProposalTxExecutor{ 185 OnCommitState: onCommitState, 186 OnAbortState: onAbortState, 187 Backend: &env.backend, 188 FeeCalculator: feeCalculator, 189 Tx: tx, 190 } 191 err = tx.Unsigned.Visit(&executor) 192 require.ErrorIs(err, ErrChildBlockAfterStakerChangeTime) 193 } 194 } 195 196 // Ensure semantic verification updates the current and pending staker sets correctly. 197 // Namely, it should add pending stakers whose start time is at or before the timestamp. 198 // It will not remove primary network stakers; that happens in rewardTxs. 199 func TestAdvanceTimeTxUpdateStakers(t *testing.T) { 200 type stakerStatus uint 201 const ( 202 pending stakerStatus = iota 203 current 204 ) 205 206 type staker struct { 207 nodeID ids.NodeID 208 startTime, endTime time.Time 209 } 210 type test struct { 211 description string 212 stakers []staker 213 subnetStakers []staker 214 advanceTimeTo []time.Time 215 expectedStakers map[ids.NodeID]stakerStatus 216 expectedSubnetStakers map[ids.NodeID]stakerStatus 217 } 218 219 // Chronological order (not in scale): 220 // Staker1: |----------------------------------------------------------| 221 // Staker2: |------------------------| 222 // Staker3: |------------------------| 223 // Staker3sub: |----------------| 224 // Staker4: |------------------------| 225 // Staker5: |--------------------| 226 staker1 := staker{ 227 nodeID: ids.GenerateTestNodeID(), 228 startTime: genesistest.DefaultValidatorStartTime.Add(1 * time.Minute), 229 endTime: genesistest.DefaultValidatorStartTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute), 230 } 231 staker2 := staker{ 232 nodeID: ids.GenerateTestNodeID(), 233 startTime: staker1.startTime.Add(1 * time.Minute), 234 endTime: staker1.startTime.Add(1 * time.Minute).Add(defaultMinStakingDuration), 235 } 236 staker3 := staker{ 237 nodeID: ids.GenerateTestNodeID(), 238 startTime: staker2.startTime.Add(1 * time.Minute), 239 endTime: staker2.endTime.Add(1 * time.Minute), 240 } 241 staker3Sub := staker{ 242 nodeID: staker3.nodeID, 243 startTime: staker3.startTime.Add(1 * time.Minute), 244 endTime: staker3.endTime.Add(-1 * time.Minute), 245 } 246 staker4 := staker{ 247 nodeID: ids.GenerateTestNodeID(), 248 startTime: staker3.startTime, 249 endTime: staker3.endTime, 250 } 251 staker5 := staker{ 252 nodeID: ids.GenerateTestNodeID(), 253 startTime: staker2.endTime, 254 endTime: staker2.endTime.Add(defaultMinStakingDuration), 255 } 256 257 tests := []test{ 258 { 259 description: "advance time to before staker1 start with subnet", 260 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 261 subnetStakers: []staker{staker1, staker2, staker3, staker4, staker5}, 262 advanceTimeTo: []time.Time{staker1.startTime.Add(-1 * time.Second)}, 263 expectedStakers: map[ids.NodeID]stakerStatus{ 264 staker1.nodeID: pending, 265 staker2.nodeID: pending, 266 staker3.nodeID: pending, 267 staker4.nodeID: pending, 268 staker5.nodeID: pending, 269 }, 270 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 271 staker1.nodeID: pending, 272 staker2.nodeID: pending, 273 staker3.nodeID: pending, 274 staker4.nodeID: pending, 275 staker5.nodeID: pending, 276 }, 277 }, 278 { 279 description: "advance time to staker 1 start with subnet", 280 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 281 subnetStakers: []staker{staker1}, 282 advanceTimeTo: []time.Time{staker1.startTime}, 283 expectedStakers: map[ids.NodeID]stakerStatus{ 284 staker1.nodeID: current, 285 staker2.nodeID: pending, 286 staker3.nodeID: pending, 287 staker4.nodeID: pending, 288 staker5.nodeID: pending, 289 }, 290 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 291 staker1.nodeID: current, 292 staker2.nodeID: pending, 293 staker3.nodeID: pending, 294 staker4.nodeID: pending, 295 staker5.nodeID: pending, 296 }, 297 }, 298 { 299 description: "advance time to the staker2 start", 300 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 301 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime}, 302 expectedStakers: map[ids.NodeID]stakerStatus{ 303 staker1.nodeID: current, 304 staker2.nodeID: current, 305 staker3.nodeID: pending, 306 staker4.nodeID: pending, 307 staker5.nodeID: pending, 308 }, 309 }, 310 { 311 description: "staker3 should validate only primary network", 312 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 313 subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, 314 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime}, 315 expectedStakers: map[ids.NodeID]stakerStatus{ 316 staker1.nodeID: current, 317 staker2.nodeID: current, 318 staker3.nodeID: current, 319 staker4.nodeID: current, 320 staker5.nodeID: pending, 321 }, 322 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 323 staker1.nodeID: current, 324 staker2.nodeID: current, 325 staker3Sub.nodeID: pending, 326 staker4.nodeID: current, 327 staker5.nodeID: pending, 328 }, 329 }, 330 { 331 description: "advance time to staker3 start with subnet", 332 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 333 subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5}, 334 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker3Sub.startTime}, 335 expectedStakers: map[ids.NodeID]stakerStatus{ 336 staker1.nodeID: current, 337 staker2.nodeID: current, 338 staker3.nodeID: current, 339 staker4.nodeID: current, 340 staker5.nodeID: pending, 341 }, 342 expectedSubnetStakers: map[ids.NodeID]stakerStatus{ 343 staker1.nodeID: current, 344 staker2.nodeID: current, 345 staker3.nodeID: current, 346 staker4.nodeID: current, 347 staker5.nodeID: pending, 348 }, 349 }, 350 { 351 description: "advance time to staker5 end", 352 stakers: []staker{staker1, staker2, staker3, staker4, staker5}, 353 advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker5.startTime}, 354 expectedStakers: map[ids.NodeID]stakerStatus{ 355 staker1.nodeID: current, 356 staker2.nodeID: current, 357 staker3.nodeID: current, 358 staker4.nodeID: current, 359 staker5.nodeID: current, 360 }, 361 }, 362 } 363 364 for _, test := range tests { 365 t.Run(test.description, func(t *testing.T) { 366 require := require.New(t) 367 env := newEnvironment(t, upgradetest.ApricotPhase5) 368 env.ctx.Lock.Lock() 369 defer env.ctx.Lock.Unlock() 370 371 dummyHeight := uint64(1) 372 373 subnetID := testSubnet1.ID() 374 env.config.TrackedSubnets.Add(subnetID) 375 376 for _, staker := range test.stakers { 377 addPendingValidator( 378 t, 379 env, 380 staker.startTime, 381 staker.endTime, 382 staker.nodeID, 383 []*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]}, 384 ) 385 } 386 387 for _, staker := range test.subnetStakers { 388 wallet := newWallet(t, env, walletConfig{ 389 subnetIDs: []ids.ID{subnetID}, 390 }) 391 392 tx, err := wallet.IssueAddSubnetValidatorTx( 393 &txs.SubnetValidator{ 394 Validator: txs.Validator{ 395 NodeID: staker.nodeID, 396 Start: uint64(staker.startTime.Unix()), 397 End: uint64(staker.endTime.Unix()), 398 Wght: 10, 399 }, 400 Subnet: subnetID, 401 }, 402 ) 403 require.NoError(err) 404 405 staker, err := state.NewPendingStaker( 406 tx.ID(), 407 tx.Unsigned.(*txs.AddSubnetValidatorTx), 408 ) 409 require.NoError(err) 410 411 require.NoError(env.state.PutPendingValidator(staker)) 412 env.state.AddTx(tx, status.Committed) 413 } 414 env.state.SetHeight(dummyHeight) 415 require.NoError(env.state.Commit()) 416 417 for _, newTime := range test.advanceTimeTo { 418 env.clk.Set(newTime) 419 tx, err := newAdvanceTimeTx(t, newTime) 420 require.NoError(err) 421 422 onCommitState, err := state.NewDiff(lastAcceptedID, env) 423 require.NoError(err) 424 425 onAbortState, err := state.NewDiff(lastAcceptedID, env) 426 require.NoError(err) 427 428 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 429 executor := ProposalTxExecutor{ 430 OnCommitState: onCommitState, 431 OnAbortState: onAbortState, 432 Backend: &env.backend, 433 FeeCalculator: feeCalculator, 434 Tx: tx, 435 } 436 require.NoError(tx.Unsigned.Visit(&executor)) 437 438 require.NoError(executor.OnCommitState.Apply(env.state)) 439 } 440 env.state.SetHeight(dummyHeight) 441 require.NoError(env.state.Commit()) 442 443 for stakerNodeID, status := range test.expectedStakers { 444 switch status { 445 case pending: 446 _, err := env.state.GetPendingValidator(constants.PrimaryNetworkID, stakerNodeID) 447 require.NoError(err) 448 _, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, stakerNodeID) 449 require.False(ok) 450 case current: 451 _, err := env.state.GetCurrentValidator(constants.PrimaryNetworkID, stakerNodeID) 452 require.NoError(err) 453 _, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, stakerNodeID) 454 require.True(ok) 455 } 456 } 457 458 for stakerNodeID, status := range test.expectedSubnetStakers { 459 switch status { 460 case pending: 461 _, ok := env.config.Validators.GetValidator(subnetID, stakerNodeID) 462 require.False(ok) 463 case current: 464 _, ok := env.config.Validators.GetValidator(subnetID, stakerNodeID) 465 require.True(ok) 466 } 467 } 468 }) 469 } 470 } 471 472 // Regression test for https://github.com/ava-labs/avalanchego/pull/584 473 // that ensures it fixes a bug where subnet validators are not removed 474 // when timestamp is advanced and there is a pending staker whose start time 475 // is after the new timestamp 476 func TestAdvanceTimeTxRemoveSubnetValidator(t *testing.T) { 477 require := require.New(t) 478 env := newEnvironment(t, upgradetest.ApricotPhase5) 479 env.ctx.Lock.Lock() 480 defer env.ctx.Lock.Unlock() 481 482 subnetID := testSubnet1.ID() 483 env.config.TrackedSubnets.Add(subnetID) 484 485 wallet := newWallet(t, env, walletConfig{ 486 subnetIDs: []ids.ID{subnetID}, 487 }) 488 489 dummyHeight := uint64(1) 490 // Add a subnet validator to the staker set 491 subnetValidatorNodeID := genesistest.DefaultNodeIDs[0] 492 subnetVdr1EndTime := genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration) 493 494 tx, err := wallet.IssueAddSubnetValidatorTx( 495 &txs.SubnetValidator{ 496 Validator: txs.Validator{ 497 NodeID: subnetValidatorNodeID, 498 Start: genesistest.DefaultValidatorStartTimeUnix, 499 End: uint64(subnetVdr1EndTime.Unix()), 500 Wght: 1, 501 }, 502 Subnet: subnetID, 503 }, 504 ) 505 require.NoError(err) 506 507 addSubnetValTx := tx.Unsigned.(*txs.AddSubnetValidatorTx) 508 staker, err := state.NewCurrentStaker( 509 tx.ID(), 510 addSubnetValTx, 511 addSubnetValTx.StartTime(), 512 0, 513 ) 514 require.NoError(err) 515 516 require.NoError(env.state.PutCurrentValidator(staker)) 517 env.state.AddTx(tx, status.Committed) 518 env.state.SetHeight(dummyHeight) 519 require.NoError(env.state.Commit()) 520 521 // The above validator is now part of the staking set 522 523 // Queue a staker that joins the staker set after the above validator leaves 524 subnetVdr2NodeID := genesistest.DefaultNodeIDs[1] 525 tx, err = wallet.IssueAddSubnetValidatorTx( 526 &txs.SubnetValidator{ 527 Validator: txs.Validator{ 528 NodeID: subnetVdr2NodeID, 529 Start: uint64(subnetVdr1EndTime.Add(time.Second).Unix()), 530 End: uint64(subnetVdr1EndTime.Add(time.Second).Add(defaultMinStakingDuration).Unix()), 531 Wght: 1, 532 }, 533 Subnet: subnetID, 534 }, 535 ) 536 require.NoError(err) 537 538 staker, err = state.NewPendingStaker( 539 tx.ID(), 540 tx.Unsigned.(*txs.AddSubnetValidatorTx), 541 ) 542 require.NoError(err) 543 544 require.NoError(env.state.PutPendingValidator(staker)) 545 env.state.AddTx(tx, status.Committed) 546 env.state.SetHeight(dummyHeight) 547 require.NoError(env.state.Commit()) 548 549 // The above validator is now in the pending staker set 550 551 // Advance time to the first staker's end time. 552 env.clk.Set(subnetVdr1EndTime) 553 tx, err = newAdvanceTimeTx(t, subnetVdr1EndTime) 554 require.NoError(err) 555 556 onCommitState, err := state.NewDiff(lastAcceptedID, env) 557 require.NoError(err) 558 559 onAbortState, err := state.NewDiff(lastAcceptedID, env) 560 require.NoError(err) 561 562 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 563 executor := ProposalTxExecutor{ 564 OnCommitState: onCommitState, 565 OnAbortState: onAbortState, 566 Backend: &env.backend, 567 FeeCalculator: feeCalculator, 568 Tx: tx, 569 } 570 require.NoError(tx.Unsigned.Visit(&executor)) 571 572 _, err = executor.OnCommitState.GetCurrentValidator(subnetID, subnetValidatorNodeID) 573 require.ErrorIs(err, database.ErrNotFound) 574 575 // Check VM Validators are removed successfully 576 require.NoError(executor.OnCommitState.Apply(env.state)) 577 578 env.state.SetHeight(dummyHeight) 579 require.NoError(env.state.Commit()) 580 _, ok := env.config.Validators.GetValidator(subnetID, subnetVdr2NodeID) 581 require.False(ok) 582 _, ok = env.config.Validators.GetValidator(subnetID, subnetValidatorNodeID) 583 require.False(ok) 584 } 585 586 func TestTrackedSubnet(t *testing.T) { 587 for _, tracked := range []bool{true, false} { 588 t.Run(fmt.Sprintf("tracked %t", tracked), func(t *testing.T) { 589 require := require.New(t) 590 env := newEnvironment(t, upgradetest.ApricotPhase5) 591 env.ctx.Lock.Lock() 592 defer env.ctx.Lock.Unlock() 593 dummyHeight := uint64(1) 594 595 subnetID := testSubnet1.ID() 596 if tracked { 597 env.config.TrackedSubnets.Add(subnetID) 598 } 599 600 wallet := newWallet(t, env, walletConfig{ 601 subnetIDs: []ids.ID{subnetID}, 602 }) 603 604 // Add a subnet validator to the staker set 605 subnetValidatorNodeID := genesistest.DefaultNodeIDs[0] 606 607 subnetVdr1StartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Minute) 608 subnetVdr1EndTime := genesistest.DefaultValidatorStartTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute) 609 tx, err := wallet.IssueAddSubnetValidatorTx( 610 &txs.SubnetValidator{ 611 Validator: txs.Validator{ 612 NodeID: subnetValidatorNodeID, 613 Start: uint64(subnetVdr1StartTime.Unix()), 614 End: uint64(subnetVdr1EndTime.Unix()), 615 Wght: 1, 616 }, 617 Subnet: subnetID, 618 }, 619 ) 620 require.NoError(err) 621 622 staker, err := state.NewPendingStaker( 623 tx.ID(), 624 tx.Unsigned.(*txs.AddSubnetValidatorTx), 625 ) 626 require.NoError(err) 627 628 require.NoError(env.state.PutPendingValidator(staker)) 629 env.state.AddTx(tx, status.Committed) 630 env.state.SetHeight(dummyHeight) 631 require.NoError(env.state.Commit()) 632 633 // Advance time to the staker's start time. 634 env.clk.Set(subnetVdr1StartTime) 635 tx, err = newAdvanceTimeTx(t, subnetVdr1StartTime) 636 require.NoError(err) 637 638 onCommitState, err := state.NewDiff(lastAcceptedID, env) 639 require.NoError(err) 640 641 onAbortState, err := state.NewDiff(lastAcceptedID, env) 642 require.NoError(err) 643 644 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 645 executor := ProposalTxExecutor{ 646 OnCommitState: onCommitState, 647 OnAbortState: onAbortState, 648 Backend: &env.backend, 649 FeeCalculator: feeCalculator, 650 Tx: tx, 651 } 652 require.NoError(tx.Unsigned.Visit(&executor)) 653 654 require.NoError(executor.OnCommitState.Apply(env.state)) 655 656 env.state.SetHeight(dummyHeight) 657 require.NoError(env.state.Commit()) 658 _, ok := env.config.Validators.GetValidator(subnetID, subnetValidatorNodeID) 659 require.True(ok) 660 }) 661 } 662 } 663 664 func TestAdvanceTimeTxDelegatorStakerWeight(t *testing.T) { 665 require := require.New(t) 666 env := newEnvironment(t, upgradetest.ApricotPhase5) 667 env.ctx.Lock.Lock() 668 defer env.ctx.Lock.Unlock() 669 dummyHeight := uint64(1) 670 671 // Case: Timestamp is after next validator start time 672 // Add a pending validator 673 pendingValidatorStartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Second) 674 pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMaxStakingDuration) 675 nodeID := ids.GenerateTestNodeID() 676 addPendingValidator( 677 t, 678 env, 679 pendingValidatorStartTime, 680 pendingValidatorEndTime, 681 nodeID, 682 []*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]}, 683 ) 684 685 tx, err := newAdvanceTimeTx(t, pendingValidatorStartTime) 686 require.NoError(err) 687 688 onCommitState, err := state.NewDiff(lastAcceptedID, env) 689 require.NoError(err) 690 691 onAbortState, err := state.NewDiff(lastAcceptedID, env) 692 require.NoError(err) 693 694 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 695 executor := ProposalTxExecutor{ 696 OnCommitState: onCommitState, 697 OnAbortState: onAbortState, 698 Backend: &env.backend, 699 FeeCalculator: feeCalculator, 700 Tx: tx, 701 } 702 require.NoError(tx.Unsigned.Visit(&executor)) 703 704 require.NoError(executor.OnCommitState.Apply(env.state)) 705 706 env.state.SetHeight(dummyHeight) 707 require.NoError(env.state.Commit()) 708 709 wallet := newWallet(t, env, walletConfig{}) 710 711 // Test validator weight before delegation 712 vdrWeight := env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID) 713 require.Equal(env.config.MinValidatorStake, vdrWeight) 714 715 // Add delegator 716 pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second) 717 pendingDelegatorEndTime := pendingDelegatorStartTime.Add(1 * time.Second) 718 719 addDelegatorTx, err := wallet.IssueAddDelegatorTx( 720 &txs.Validator{ 721 NodeID: nodeID, 722 Start: uint64(pendingDelegatorStartTime.Unix()), 723 End: uint64(pendingDelegatorEndTime.Unix()), 724 Wght: env.config.MinDelegatorStake, 725 }, 726 &secp256k1fx.OutputOwners{ 727 Threshold: 1, 728 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 729 }, 730 ) 731 require.NoError(err) 732 733 staker, err := state.NewPendingStaker( 734 addDelegatorTx.ID(), 735 addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), 736 ) 737 require.NoError(err) 738 739 env.state.PutPendingDelegator(staker) 740 env.state.AddTx(addDelegatorTx, status.Committed) 741 env.state.SetHeight(dummyHeight) 742 require.NoError(env.state.Commit()) 743 744 // Advance Time 745 tx, err = newAdvanceTimeTx(t, pendingDelegatorStartTime) 746 require.NoError(err) 747 748 onCommitState, err = state.NewDiff(lastAcceptedID, env) 749 require.NoError(err) 750 751 onAbortState, err = state.NewDiff(lastAcceptedID, env) 752 require.NoError(err) 753 754 executor = ProposalTxExecutor{ 755 OnCommitState: onCommitState, 756 OnAbortState: onAbortState, 757 Backend: &env.backend, 758 FeeCalculator: feeCalculator, 759 Tx: tx, 760 } 761 require.NoError(tx.Unsigned.Visit(&executor)) 762 763 require.NoError(executor.OnCommitState.Apply(env.state)) 764 765 env.state.SetHeight(dummyHeight) 766 require.NoError(env.state.Commit()) 767 768 // Test validator weight after delegation 769 vdrWeight = env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID) 770 require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight) 771 } 772 773 func TestAdvanceTimeTxDelegatorStakers(t *testing.T) { 774 require := require.New(t) 775 env := newEnvironment(t, upgradetest.ApricotPhase5) 776 env.ctx.Lock.Lock() 777 defer env.ctx.Lock.Unlock() 778 dummyHeight := uint64(1) 779 780 // Case: Timestamp is after next validator start time 781 // Add a pending validator 782 pendingValidatorStartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Second) 783 pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration) 784 nodeID := ids.GenerateTestNodeID() 785 addPendingValidator(t, env, pendingValidatorStartTime, pendingValidatorEndTime, nodeID, []*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]}) 786 787 tx, err := newAdvanceTimeTx(t, pendingValidatorStartTime) 788 require.NoError(err) 789 790 onCommitState, err := state.NewDiff(lastAcceptedID, env) 791 require.NoError(err) 792 793 onAbortState, err := state.NewDiff(lastAcceptedID, env) 794 require.NoError(err) 795 796 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 797 executor := ProposalTxExecutor{ 798 OnCommitState: onCommitState, 799 OnAbortState: onAbortState, 800 Backend: &env.backend, 801 FeeCalculator: feeCalculator, 802 Tx: tx, 803 } 804 require.NoError(tx.Unsigned.Visit(&executor)) 805 806 require.NoError(executor.OnCommitState.Apply(env.state)) 807 808 env.state.SetHeight(dummyHeight) 809 require.NoError(env.state.Commit()) 810 811 wallet := newWallet(t, env, walletConfig{}) 812 813 // Test validator weight before delegation 814 vdrWeight := env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID) 815 require.Equal(env.config.MinValidatorStake, vdrWeight) 816 817 // Add delegator 818 pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second) 819 pendingDelegatorEndTime := pendingDelegatorStartTime.Add(defaultMinStakingDuration) 820 addDelegatorTx, err := wallet.IssueAddDelegatorTx( 821 &txs.Validator{ 822 NodeID: nodeID, 823 Start: uint64(pendingDelegatorStartTime.Unix()), 824 End: uint64(pendingDelegatorEndTime.Unix()), 825 Wght: env.config.MinDelegatorStake, 826 }, 827 &secp256k1fx.OutputOwners{ 828 Threshold: 1, 829 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 830 }, 831 ) 832 require.NoError(err) 833 834 staker, err := state.NewPendingStaker( 835 addDelegatorTx.ID(), 836 addDelegatorTx.Unsigned.(*txs.AddDelegatorTx), 837 ) 838 require.NoError(err) 839 840 env.state.PutPendingDelegator(staker) 841 env.state.AddTx(addDelegatorTx, status.Committed) 842 env.state.SetHeight(dummyHeight) 843 require.NoError(env.state.Commit()) 844 845 // Advance Time 846 tx, err = newAdvanceTimeTx(t, pendingDelegatorStartTime) 847 require.NoError(err) 848 849 onCommitState, err = state.NewDiff(lastAcceptedID, env) 850 require.NoError(err) 851 852 onAbortState, err = state.NewDiff(lastAcceptedID, env) 853 require.NoError(err) 854 855 executor = ProposalTxExecutor{ 856 OnCommitState: onCommitState, 857 OnAbortState: onAbortState, 858 Backend: &env.backend, 859 FeeCalculator: feeCalculator, 860 Tx: tx, 861 } 862 require.NoError(tx.Unsigned.Visit(&executor)) 863 864 require.NoError(executor.OnCommitState.Apply(env.state)) 865 866 env.state.SetHeight(dummyHeight) 867 require.NoError(env.state.Commit()) 868 869 // Test validator weight after delegation 870 vdrWeight = env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID) 871 require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight) 872 } 873 874 func TestAdvanceTimeTxAfterBanff(t *testing.T) { 875 require := require.New(t) 876 env := newEnvironment(t, upgradetest.Durango) 877 env.ctx.Lock.Lock() 878 defer env.ctx.Lock.Unlock() 879 env.clk.Set(genesistest.DefaultValidatorStartTime) // VM's clock reads the genesis time 880 upgradeTime := env.clk.Time().Add(SyncBound) 881 env.config.UpgradeConfig.BanffTime = upgradeTime 882 env.config.UpgradeConfig.CortinaTime = upgradeTime 883 env.config.UpgradeConfig.DurangoTime = upgradeTime 884 885 // Proposed advancing timestamp to the banff timestamp 886 tx, err := newAdvanceTimeTx(t, upgradeTime) 887 require.NoError(err) 888 889 onCommitState, err := state.NewDiff(lastAcceptedID, env) 890 require.NoError(err) 891 892 onAbortState, err := state.NewDiff(lastAcceptedID, env) 893 require.NoError(err) 894 895 feeCalculator := state.PickFeeCalculator(env.config, onCommitState) 896 executor := ProposalTxExecutor{ 897 OnCommitState: onCommitState, 898 OnAbortState: onAbortState, 899 Backend: &env.backend, 900 FeeCalculator: feeCalculator, 901 Tx: tx, 902 } 903 err = tx.Unsigned.Visit(&executor) 904 require.ErrorIs(err, ErrAdvanceTimeTxIssuedAfterBanff) 905 } 906 907 // Ensure marshaling/unmarshaling works 908 func TestAdvanceTimeTxUnmarshal(t *testing.T) { 909 require := require.New(t) 910 env := newEnvironment(t, upgradetest.ApricotPhase5) 911 env.ctx.Lock.Lock() 912 defer env.ctx.Lock.Unlock() 913 914 chainTime := env.state.GetTimestamp() 915 tx, err := newAdvanceTimeTx(t, chainTime.Add(time.Second)) 916 require.NoError(err) 917 918 bytes, err := txs.Codec.Marshal(txs.CodecVersion, tx) 919 require.NoError(err) 920 921 var unmarshaledTx txs.Tx 922 _, err = txs.Codec.Unmarshal(bytes, &unmarshaledTx) 923 require.NoError(err) 924 925 require.Equal( 926 tx.Unsigned.(*txs.AdvanceTimeTx).Time, 927 unmarshaledTx.Unsigned.(*txs.AdvanceTimeTx).Time, 928 ) 929 } 930 931 func addPendingValidator( 932 t testing.TB, 933 env *environment, 934 startTime time.Time, 935 endTime time.Time, 936 nodeID ids.NodeID, 937 keys []*secp256k1.PrivateKey, 938 ) *txs.Tx { 939 require := require.New(t) 940 941 wallet := newWallet(t, env, walletConfig{ 942 keys: keys, 943 }) 944 addPendingValidatorTx, err := wallet.IssueAddValidatorTx( 945 &txs.Validator{ 946 NodeID: nodeID, 947 Start: uint64(startTime.Unix()), 948 End: uint64(endTime.Unix()), 949 Wght: env.config.MinValidatorStake, 950 }, 951 &secp256k1fx.OutputOwners{ 952 Threshold: 1, 953 Addrs: []ids.ShortID{ids.GenerateTestShortID()}, 954 }, 955 reward.PercentDenominator, 956 ) 957 require.NoError(err) 958 959 staker, err := state.NewPendingStaker( 960 addPendingValidatorTx.ID(), 961 addPendingValidatorTx.Unsigned.(*txs.AddValidatorTx), 962 ) 963 require.NoError(err) 964 965 require.NoError(env.state.PutPendingValidator(staker)) 966 env.state.AddTx(addPendingValidatorTx, status.Committed) 967 dummyHeight := uint64(1) 968 env.state.SetHeight(dummyHeight) 969 require.NoError(env.state.Commit()) 970 return addPendingValidatorTx 971 }