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