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  }