github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/block/executor/standard_block_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  	"go.uber.org/mock/gomock"
    14  
    15  	"github.com/ava-labs/avalanchego/database"
    16  	"github.com/ava-labs/avalanchego/ids"
    17  	"github.com/ava-labs/avalanchego/snow/snowtest"
    18  	"github.com/ava-labs/avalanchego/upgrade/upgradetest"
    19  	"github.com/ava-labs/avalanchego/utils/constants"
    20  	"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
    21  	"github.com/ava-labs/avalanchego/utils/iterator/iteratormock"
    22  	"github.com/ava-labs/avalanchego/vms/components/avax"
    23  	"github.com/ava-labs/avalanchego/vms/components/gas"
    24  	"github.com/ava-labs/avalanchego/vms/platformvm/block"
    25  	"github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest"
    26  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    27  	"github.com/ava-labs/avalanchego/vms/platformvm/status"
    28  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    29  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
    30  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    31  )
    32  
    33  func TestApricotStandardBlockTimeVerification(t *testing.T) {
    34  	require := require.New(t)
    35  	ctrl := gomock.NewController(t)
    36  
    37  	env := newEnvironment(t, ctrl, upgradetest.ApricotPhase5)
    38  
    39  	// setup and store parent block
    40  	// it's a standard block for simplicity
    41  	parentHeight := uint64(2022)
    42  
    43  	apricotParentBlk, err := block.NewApricotStandardBlock(
    44  		ids.Empty, // does not matter
    45  		parentHeight,
    46  		nil, // txs do not matter in this test
    47  	)
    48  	require.NoError(err)
    49  	parentID := apricotParentBlk.ID()
    50  
    51  	// store parent block, with relevant quantities
    52  	onParentAccept := state.NewMockDiff(ctrl)
    53  	env.blkManager.(*manager).blkIDToState[parentID] = &blockState{
    54  		statelessBlock: apricotParentBlk,
    55  		onAcceptState:  onParentAccept,
    56  	}
    57  	env.blkManager.(*manager).lastAccepted = parentID
    58  
    59  	chainTime := env.clk.Time().Truncate(time.Second)
    60  	onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes()
    61  	onParentAccept.EXPECT().GetFeeState().Return(gas.State{}).AnyTimes()
    62  
    63  	// wrong height
    64  	apricotChildBlk, err := block.NewApricotStandardBlock(
    65  		apricotParentBlk.ID(),
    66  		apricotParentBlk.Height(),
    67  		nil, // txs nulled to simplify test
    68  	)
    69  	require.NoError(err)
    70  	blk := env.blkManager.NewBlock(apricotChildBlk)
    71  	err = blk.Verify(context.Background())
    72  	require.ErrorIs(err, errIncorrectBlockHeight)
    73  
    74  	// valid height
    75  	apricotChildBlk, err = block.NewApricotStandardBlock(
    76  		apricotParentBlk.ID(),
    77  		apricotParentBlk.Height()+1,
    78  		nil, // txs nulled to simplify test
    79  	)
    80  	require.NoError(err)
    81  	blk = env.blkManager.NewBlock(apricotChildBlk)
    82  	require.NoError(blk.Verify(context.Background()))
    83  }
    84  
    85  func TestBanffStandardBlockTimeVerification(t *testing.T) {
    86  	require := require.New(t)
    87  	ctrl := gomock.NewController(t)
    88  
    89  	env := newEnvironment(t, ctrl, upgradetest.Banff)
    90  	now := env.clk.Time()
    91  	env.clk.Set(now)
    92  
    93  	// setup and store parent block
    94  	// it's a standard block for simplicity
    95  	parentTime := now
    96  	parentHeight := uint64(2022)
    97  
    98  	banffParentBlk, err := block.NewBanffStandardBlock(
    99  		parentTime,
   100  		ids.Empty, // does not matter
   101  		parentHeight,
   102  		nil, // txs do not matter in this test
   103  	)
   104  	require.NoError(err)
   105  	parentID := banffParentBlk.ID()
   106  
   107  	// store parent block, with relevant quantities
   108  	onParentAccept := state.NewMockDiff(ctrl)
   109  	chainTime := env.clk.Time().Truncate(time.Second)
   110  	env.blkManager.(*manager).blkIDToState[parentID] = &blockState{
   111  		statelessBlock: banffParentBlk,
   112  		onAcceptState:  onParentAccept,
   113  		timestamp:      chainTime,
   114  	}
   115  	env.blkManager.(*manager).lastAccepted = parentID
   116  
   117  	nextStakerTime := chainTime.Add(executor.SyncBound).Add(-1 * time.Second)
   118  
   119  	// store just once current staker to mark next staker time.
   120  	currentStakerIt := iteratormock.NewIterator[*state.Staker](ctrl)
   121  	currentStakerIt.EXPECT().Next().Return(true).AnyTimes()
   122  	currentStakerIt.EXPECT().Value().Return(
   123  		&state.Staker{
   124  			NextTime: nextStakerTime,
   125  			Priority: txs.PrimaryNetworkValidatorCurrentPriority,
   126  		},
   127  	).AnyTimes()
   128  	currentStakerIt.EXPECT().Release().Return().AnyTimes()
   129  	onParentAccept.EXPECT().GetCurrentStakerIterator().Return(currentStakerIt, nil).AnyTimes()
   130  
   131  	// no pending stakers
   132  	pendingIt := iteratormock.NewIterator[*state.Staker](ctrl)
   133  	pendingIt.EXPECT().Next().Return(false).AnyTimes()
   134  	pendingIt.EXPECT().Release().Return().AnyTimes()
   135  	onParentAccept.EXPECT().GetPendingStakerIterator().Return(pendingIt, nil).AnyTimes()
   136  
   137  	onParentAccept.EXPECT().GetTimestamp().Return(chainTime).AnyTimes()
   138  	onParentAccept.EXPECT().GetFeeState().Return(gas.State{}).AnyTimes()
   139  
   140  	txID := ids.GenerateTestID()
   141  	utxo := &avax.UTXO{
   142  		UTXOID: avax.UTXOID{
   143  			TxID: txID,
   144  		},
   145  		Asset: avax.Asset{
   146  			ID: snowtest.AVAXAssetID,
   147  		},
   148  		Out: &secp256k1fx.TransferOutput{
   149  			Amt: env.config.StaticFeeConfig.CreateSubnetTxFee,
   150  		},
   151  	}
   152  	utxoID := utxo.InputID()
   153  	onParentAccept.EXPECT().GetUTXO(utxoID).Return(utxo, nil).AnyTimes()
   154  
   155  	// Create the tx
   156  	utx := &txs.CreateSubnetTx{
   157  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   158  			NetworkID:    env.ctx.NetworkID,
   159  			BlockchainID: env.ctx.ChainID,
   160  			Ins: []*avax.TransferableInput{{
   161  				UTXOID: utxo.UTXOID,
   162  				Asset:  utxo.Asset,
   163  				In: &secp256k1fx.TransferInput{
   164  					Amt: env.config.StaticFeeConfig.CreateSubnetTxFee,
   165  				},
   166  			}},
   167  		}},
   168  		Owner: &secp256k1fx.OutputOwners{},
   169  	}
   170  	tx := &txs.Tx{Unsigned: utx}
   171  	require.NoError(tx.Sign(txs.Codec, [][]*secp256k1.PrivateKey{{}}))
   172  
   173  	{
   174  		// wrong version
   175  		banffChildBlk, err := block.NewApricotStandardBlock(
   176  			banffParentBlk.ID(),
   177  			banffParentBlk.Height()+1,
   178  			[]*txs.Tx{tx},
   179  		)
   180  		require.NoError(err)
   181  		block := env.blkManager.NewBlock(banffChildBlk)
   182  		err = block.Verify(context.Background())
   183  		require.ErrorIs(err, errApricotBlockIssuedAfterFork)
   184  	}
   185  
   186  	{
   187  		// wrong height
   188  		childTimestamp := parentTime.Add(time.Second)
   189  		banffChildBlk, err := block.NewBanffStandardBlock(
   190  			childTimestamp,
   191  			banffParentBlk.ID(),
   192  			banffParentBlk.Height(),
   193  			[]*txs.Tx{tx},
   194  		)
   195  		require.NoError(err)
   196  		block := env.blkManager.NewBlock(banffChildBlk)
   197  		err = block.Verify(context.Background())
   198  		require.ErrorIs(err, errIncorrectBlockHeight)
   199  	}
   200  
   201  	{
   202  		// wrong timestamp, earlier than parent
   203  		childTimestamp := parentTime.Add(-1 * time.Second)
   204  		banffChildBlk, err := block.NewBanffStandardBlock(
   205  			childTimestamp,
   206  			banffParentBlk.ID(),
   207  			banffParentBlk.Height()+1,
   208  			[]*txs.Tx{tx},
   209  		)
   210  		require.NoError(err)
   211  		block := env.blkManager.NewBlock(banffChildBlk)
   212  		err = block.Verify(context.Background())
   213  		require.ErrorIs(err, errChildBlockEarlierThanParent)
   214  	}
   215  
   216  	{
   217  		// wrong timestamp, violated synchrony bound
   218  		initClkTime := env.clk.Time()
   219  		env.clk.Set(parentTime.Add(-executor.SyncBound))
   220  		banffChildBlk, err := block.NewBanffStandardBlock(
   221  			parentTime.Add(time.Second),
   222  			banffParentBlk.ID(),
   223  			banffParentBlk.Height()+1,
   224  			[]*txs.Tx{tx},
   225  		)
   226  		require.NoError(err)
   227  		block := env.blkManager.NewBlock(banffChildBlk)
   228  		err = block.Verify(context.Background())
   229  		require.ErrorIs(err, executor.ErrChildBlockBeyondSyncBound)
   230  		env.clk.Set(initClkTime)
   231  	}
   232  
   233  	{
   234  		// wrong timestamp, skipped staker set change event
   235  		childTimestamp := nextStakerTime.Add(time.Second)
   236  		banffChildBlk, err := block.NewBanffStandardBlock(
   237  			childTimestamp,
   238  			banffParentBlk.ID(),
   239  			banffParentBlk.Height()+1,
   240  			[]*txs.Tx{tx},
   241  		)
   242  		require.NoError(err)
   243  		block := env.blkManager.NewBlock(banffChildBlk)
   244  		err = block.Verify(context.Background())
   245  		require.ErrorIs(err, executor.ErrChildBlockAfterStakerChangeTime)
   246  	}
   247  
   248  	{
   249  		// no state changes
   250  		childTimestamp := parentTime
   251  		banffChildBlk, err := block.NewBanffStandardBlock(
   252  			childTimestamp,
   253  			banffParentBlk.ID(),
   254  			banffParentBlk.Height()+1,
   255  			nil,
   256  		)
   257  		require.NoError(err)
   258  		block := env.blkManager.NewBlock(banffChildBlk)
   259  		err = block.Verify(context.Background())
   260  		require.ErrorIs(err, errBanffStandardBlockWithoutChanges)
   261  	}
   262  
   263  	{
   264  		// valid block, same timestamp as parent block
   265  		childTimestamp := parentTime
   266  		banffChildBlk, err := block.NewBanffStandardBlock(
   267  			childTimestamp,
   268  			banffParentBlk.ID(),
   269  			banffParentBlk.Height()+1,
   270  			[]*txs.Tx{tx},
   271  		)
   272  		require.NoError(err)
   273  		block := env.blkManager.NewBlock(banffChildBlk)
   274  		require.NoError(block.Verify(context.Background()))
   275  	}
   276  
   277  	{
   278  		// valid
   279  		childTimestamp := nextStakerTime
   280  		banffChildBlk, err := block.NewBanffStandardBlock(
   281  			childTimestamp,
   282  			banffParentBlk.ID(),
   283  			banffParentBlk.Height()+1,
   284  			[]*txs.Tx{tx},
   285  		)
   286  		require.NoError(err)
   287  		block := env.blkManager.NewBlock(banffChildBlk)
   288  		require.NoError(block.Verify(context.Background()))
   289  	}
   290  }
   291  
   292  func TestBanffStandardBlockUpdatePrimaryNetworkStakers(t *testing.T) {
   293  	require := require.New(t)
   294  
   295  	env := newEnvironment(t, nil, upgradetest.Banff)
   296  
   297  	// Case: Timestamp is after next validator start time
   298  	// Add a pending validator
   299  	pendingValidatorStartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Second)
   300  	pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMinStakingDuration)
   301  	nodeID := ids.GenerateTestNodeID()
   302  	rewardAddress := ids.GenerateTestShortID()
   303  	addPendingValidatorTx := addPendingValidator(
   304  		t,
   305  		env,
   306  		pendingValidatorStartTime,
   307  		pendingValidatorEndTime,
   308  		nodeID,
   309  		rewardAddress,
   310  		[]*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]},
   311  	)
   312  
   313  	// build standard block moving ahead chain time
   314  	preferredID := env.state.GetLastAccepted()
   315  	parentBlk, err := env.state.GetStatelessBlock(preferredID)
   316  	require.NoError(err)
   317  	statelessStandardBlock, err := block.NewBanffStandardBlock(
   318  		pendingValidatorStartTime,
   319  		parentBlk.ID(),
   320  		parentBlk.Height()+1,
   321  		nil, // txs nulled to simplify test
   322  	)
   323  	require.NoError(err)
   324  	block := env.blkManager.NewBlock(statelessStandardBlock)
   325  
   326  	// update staker set
   327  	require.NoError(block.Verify(context.Background()))
   328  
   329  	// tests
   330  	blkStateMap := env.blkManager.(*manager).blkIDToState
   331  	updatedState := blkStateMap[block.ID()].onAcceptState
   332  	currentValidator, err := updatedState.GetCurrentValidator(constants.PrimaryNetworkID, nodeID)
   333  	require.NoError(err)
   334  	require.Equal(addPendingValidatorTx.ID(), currentValidator.TxID)
   335  	require.Equal(uint64(1370), currentValidator.PotentialReward) // See rewards tests to explain why 1370
   336  
   337  	_, err = updatedState.GetPendingValidator(constants.PrimaryNetworkID, nodeID)
   338  	require.ErrorIs(err, database.ErrNotFound)
   339  
   340  	// Test VM validators
   341  	require.NoError(block.Accept(context.Background()))
   342  	_, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, nodeID)
   343  	require.True(ok)
   344  }
   345  
   346  // Ensure semantic verification updates the current and pending staker sets correctly.
   347  // Namely, it should add pending stakers whose start time is at or before the timestamp.
   348  // It will not remove primary network stakers; that happens in rewardTxs.
   349  func TestBanffStandardBlockUpdateStakers(t *testing.T) {
   350  	// Chronological order (not in scale):
   351  	// Staker1:    |----------------------------------------------------------|
   352  	// Staker2:        |------------------------|
   353  	// Staker3:            |------------------------|
   354  	// Staker3sub:             |----------------|
   355  	// Staker4:            |------------------------|
   356  	// Staker5:                                 |--------------------|
   357  
   358  	// In this test multiple stakers may join and leave the staker set at the same time.
   359  	// The order in which they do it is asserted; the order may depend on the staker.TxID,
   360  	// which in turns depend on every feature of the transaction creating the staker.
   361  	// So in this test we avoid ids.GenerateTestNodeID, in favour of ids.BuildTestNodeID
   362  	// so that TxID does not depend on the order we run tests.
   363  	staker1 := staker{
   364  		nodeID:        ids.BuildTestNodeID([]byte{0xf1}),
   365  		rewardAddress: ids.ShortID{0xf1},
   366  		startTime:     genesistest.DefaultValidatorStartTime.Add(1 * time.Minute),
   367  		endTime:       genesistest.DefaultValidatorStartTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute),
   368  	}
   369  	staker2 := staker{
   370  		nodeID:        ids.BuildTestNodeID([]byte{0xf2}),
   371  		rewardAddress: ids.ShortID{0xf2},
   372  		startTime:     staker1.startTime.Add(1 * time.Minute),
   373  		endTime:       staker1.startTime.Add(1 * time.Minute).Add(defaultMinStakingDuration),
   374  	}
   375  	staker3 := staker{
   376  		nodeID:        ids.BuildTestNodeID([]byte{0xf3}),
   377  		rewardAddress: ids.ShortID{0xf3},
   378  		startTime:     staker2.startTime.Add(1 * time.Minute),
   379  		endTime:       staker2.endTime.Add(1 * time.Minute),
   380  	}
   381  	staker3Sub := staker{
   382  		nodeID:        ids.BuildTestNodeID([]byte{0xf3}),
   383  		rewardAddress: ids.ShortID{0xff},
   384  		startTime:     staker3.startTime.Add(1 * time.Minute),
   385  		endTime:       staker3.endTime.Add(-1 * time.Minute),
   386  	}
   387  	staker4 := staker{
   388  		nodeID:        ids.BuildTestNodeID([]byte{0xf4}),
   389  		rewardAddress: ids.ShortID{0xf4},
   390  		startTime:     staker3.startTime,
   391  		endTime:       staker3.endTime,
   392  	}
   393  	staker5 := staker{
   394  		nodeID:        ids.BuildTestNodeID([]byte{0xf5}),
   395  		rewardAddress: ids.ShortID{0xf5},
   396  		startTime:     staker2.endTime,
   397  		endTime:       staker2.endTime.Add(defaultMinStakingDuration),
   398  	}
   399  
   400  	tests := []test{
   401  		{
   402  			description:   "advance time to staker 1 start with subnet",
   403  			stakers:       []staker{staker1, staker2, staker3, staker4, staker5},
   404  			subnetStakers: []staker{staker1},
   405  			advanceTimeTo: []time.Time{staker1.startTime},
   406  			expectedStakers: map[ids.NodeID]stakerStatus{
   407  				staker1.nodeID: current,
   408  				staker2.nodeID: pending,
   409  				staker3.nodeID: pending,
   410  				staker4.nodeID: pending,
   411  				staker5.nodeID: pending,
   412  			},
   413  			expectedSubnetStakers: map[ids.NodeID]stakerStatus{
   414  				staker1.nodeID: current,
   415  				staker2.nodeID: pending,
   416  				staker3.nodeID: pending,
   417  				staker4.nodeID: pending,
   418  				staker5.nodeID: pending,
   419  			},
   420  		},
   421  		{
   422  			description:   "advance time to the staker2 start",
   423  			stakers:       []staker{staker1, staker2, staker3, staker4, staker5},
   424  			advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime},
   425  			expectedStakers: map[ids.NodeID]stakerStatus{
   426  				staker1.nodeID: current,
   427  				staker2.nodeID: current,
   428  				staker3.nodeID: pending,
   429  				staker4.nodeID: pending,
   430  				staker5.nodeID: pending,
   431  			},
   432  		},
   433  		{
   434  			description:   "staker3 should validate only primary network",
   435  			stakers:       []staker{staker1, staker2, staker3, staker4, staker5},
   436  			subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5},
   437  			advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime},
   438  			expectedStakers: map[ids.NodeID]stakerStatus{
   439  				staker1.nodeID: current,
   440  				staker2.nodeID: current,
   441  				staker3.nodeID: current,
   442  				staker4.nodeID: current,
   443  				staker5.nodeID: pending,
   444  			},
   445  			expectedSubnetStakers: map[ids.NodeID]stakerStatus{
   446  				staker1.nodeID:    current,
   447  				staker2.nodeID:    current,
   448  				staker3Sub.nodeID: pending,
   449  				staker4.nodeID:    current,
   450  				staker5.nodeID:    pending,
   451  			},
   452  		},
   453  		{
   454  			description:   "advance time to staker3 start with subnet",
   455  			stakers:       []staker{staker1, staker2, staker3, staker4, staker5},
   456  			subnetStakers: []staker{staker1, staker2, staker3Sub, staker4, staker5},
   457  			advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker3Sub.startTime},
   458  			expectedStakers: map[ids.NodeID]stakerStatus{
   459  				staker1.nodeID: current,
   460  				staker2.nodeID: current,
   461  				staker3.nodeID: current,
   462  				staker4.nodeID: current,
   463  				staker5.nodeID: pending,
   464  			},
   465  			expectedSubnetStakers: map[ids.NodeID]stakerStatus{
   466  				staker1.nodeID: current,
   467  				staker2.nodeID: current,
   468  				staker3.nodeID: current,
   469  				staker4.nodeID: current,
   470  				staker5.nodeID: pending,
   471  			},
   472  		},
   473  		{
   474  			description:   "advance time to staker5 start",
   475  			stakers:       []staker{staker1, staker2, staker3, staker4, staker5},
   476  			advanceTimeTo: []time.Time{staker1.startTime, staker2.startTime, staker3.startTime, staker5.startTime},
   477  			expectedStakers: map[ids.NodeID]stakerStatus{
   478  				staker1.nodeID: current,
   479  
   480  				// Staker2's end time matches staker5's start time, so typically
   481  				// the block builder would produce a ProposalBlock to remove
   482  				// staker2 when advancing the time. However, it is valid to only
   483  				// advance the time with a StandardBlock and not remove staker2,
   484  				// which is what this test does.
   485  				staker2.nodeID: current,
   486  				staker3.nodeID: current,
   487  				staker4.nodeID: current,
   488  				staker5.nodeID: current,
   489  			},
   490  		},
   491  	}
   492  
   493  	for _, test := range tests {
   494  		t.Run(test.description, func(t *testing.T) {
   495  			require := require.New(t)
   496  			env := newEnvironment(t, nil, upgradetest.Banff)
   497  
   498  			subnetID := testSubnet1.ID()
   499  			env.config.TrackedSubnets.Add(subnetID)
   500  
   501  			for _, staker := range test.stakers {
   502  				addPendingValidator(
   503  					t,
   504  					env,
   505  					staker.startTime,
   506  					staker.endTime,
   507  					staker.nodeID,
   508  					staker.rewardAddress,
   509  					[]*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]},
   510  				)
   511  			}
   512  
   513  			for _, staker := range test.subnetStakers {
   514  				wallet := newWallet(t, env, walletConfig{
   515  					subnetIDs: []ids.ID{subnetID},
   516  				})
   517  
   518  				tx, err := wallet.IssueAddSubnetValidatorTx(
   519  					&txs.SubnetValidator{
   520  						Validator: txs.Validator{
   521  							NodeID: staker.nodeID,
   522  							Start:  uint64(staker.startTime.Unix()),
   523  							End:    uint64(staker.endTime.Unix()),
   524  							Wght:   10,
   525  						},
   526  						Subnet: subnetID,
   527  					},
   528  				)
   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  				require.NoError(env.state.PutPendingValidator(staker))
   538  				env.state.AddTx(tx, status.Committed)
   539  			}
   540  			env.state.SetHeight( /*dummyHeight*/ 1)
   541  			require.NoError(env.state.Commit())
   542  
   543  			for _, newTime := range test.advanceTimeTo {
   544  				env.clk.Set(newTime)
   545  
   546  				// build standard block moving ahead chain time
   547  				preferredID := env.state.GetLastAccepted()
   548  				parentBlk, err := env.state.GetStatelessBlock(preferredID)
   549  				require.NoError(err)
   550  				statelessStandardBlock, err := block.NewBanffStandardBlock(
   551  					newTime,
   552  					parentBlk.ID(),
   553  					parentBlk.Height()+1,
   554  					nil, // txs nulled to simplify test
   555  				)
   556  				block := env.blkManager.NewBlock(statelessStandardBlock)
   557  
   558  				require.NoError(err)
   559  
   560  				// update staker set
   561  				require.NoError(block.Verify(context.Background()))
   562  				require.NoError(block.Accept(context.Background()))
   563  			}
   564  
   565  			for stakerNodeID, status := range test.expectedStakers {
   566  				switch status {
   567  				case pending:
   568  					_, err := env.state.GetPendingValidator(constants.PrimaryNetworkID, stakerNodeID)
   569  					require.NoError(err)
   570  					_, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, stakerNodeID)
   571  					require.False(ok)
   572  				case current:
   573  					_, err := env.state.GetCurrentValidator(constants.PrimaryNetworkID, stakerNodeID)
   574  					require.NoError(err)
   575  					_, ok := env.config.Validators.GetValidator(constants.PrimaryNetworkID, stakerNodeID)
   576  					require.True(ok)
   577  				}
   578  			}
   579  
   580  			for stakerNodeID, status := range test.expectedSubnetStakers {
   581  				switch status {
   582  				case pending:
   583  					_, ok := env.config.Validators.GetValidator(subnetID, stakerNodeID)
   584  					require.False(ok)
   585  				case current:
   586  					_, ok := env.config.Validators.GetValidator(subnetID, stakerNodeID)
   587  					require.True(ok)
   588  				}
   589  			}
   590  		})
   591  	}
   592  }
   593  
   594  // Regression test for https://github.com/ava-labs/avalanchego/pull/584
   595  // that ensures it fixes a bug where subnet validators are not removed
   596  // when timestamp is advanced and there is a pending staker whose start time
   597  // is after the new timestamp
   598  func TestBanffStandardBlockRemoveSubnetValidator(t *testing.T) {
   599  	require := require.New(t)
   600  	env := newEnvironment(t, nil, upgradetest.Banff)
   601  
   602  	subnetID := testSubnet1.ID()
   603  	env.config.TrackedSubnets.Add(subnetID)
   604  
   605  	wallet := newWallet(t, env, walletConfig{
   606  		subnetIDs: []ids.ID{subnetID},
   607  	})
   608  
   609  	// Add a subnet validator to the staker set
   610  	subnetValidatorNodeID := genesistest.DefaultNodeIDs[0]
   611  	subnetVdr1EndTime := genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration)
   612  	tx, err := wallet.IssueAddSubnetValidatorTx(
   613  		&txs.SubnetValidator{
   614  			Validator: txs.Validator{
   615  				NodeID: subnetValidatorNodeID,
   616  				Start:  genesistest.DefaultValidatorStartTimeUnix,
   617  				End:    uint64(subnetVdr1EndTime.Unix()),
   618  				Wght:   1,
   619  			},
   620  			Subnet: subnetID,
   621  		},
   622  	)
   623  	require.NoError(err)
   624  
   625  	addSubnetValTx := tx.Unsigned.(*txs.AddSubnetValidatorTx)
   626  	staker, err := state.NewCurrentStaker(
   627  		tx.ID(),
   628  		addSubnetValTx,
   629  		addSubnetValTx.StartTime(),
   630  		0,
   631  	)
   632  	require.NoError(err)
   633  
   634  	require.NoError(env.state.PutCurrentValidator(staker))
   635  	env.state.AddTx(tx, status.Committed)
   636  	require.NoError(env.state.Commit())
   637  
   638  	// The above validator is now part of the staking set
   639  
   640  	// Queue a staker that joins the staker set after the above validator leaves
   641  	subnetVdr2NodeID := genesistest.DefaultNodeIDs[1]
   642  	tx, err = wallet.IssueAddSubnetValidatorTx(
   643  		&txs.SubnetValidator{
   644  			Validator: txs.Validator{
   645  				NodeID: subnetVdr2NodeID,
   646  				Start:  uint64(subnetVdr1EndTime.Add(time.Second).Unix()),
   647  				End:    uint64(subnetVdr1EndTime.Add(time.Second).Add(defaultMinStakingDuration).Unix()),
   648  				Wght:   1,
   649  			},
   650  			Subnet: subnetID,
   651  		},
   652  	)
   653  	require.NoError(err)
   654  
   655  	staker, err = state.NewPendingStaker(
   656  		tx.ID(),
   657  		tx.Unsigned.(*txs.AddSubnetValidatorTx),
   658  	)
   659  	require.NoError(err)
   660  
   661  	require.NoError(env.state.PutPendingValidator(staker))
   662  	env.state.AddTx(tx, status.Committed)
   663  	require.NoError(env.state.Commit())
   664  
   665  	// The above validator is now in the pending staker set
   666  
   667  	// Advance time to the first staker's end time.
   668  	env.clk.Set(subnetVdr1EndTime)
   669  	// build standard block moving ahead chain time
   670  	preferredID := env.state.GetLastAccepted()
   671  	parentBlk, err := env.state.GetStatelessBlock(preferredID)
   672  	require.NoError(err)
   673  	statelessStandardBlock, err := block.NewBanffStandardBlock(
   674  		subnetVdr1EndTime,
   675  		parentBlk.ID(),
   676  		parentBlk.Height()+1,
   677  		nil, // txs nulled to simplify test
   678  	)
   679  	require.NoError(err)
   680  	block := env.blkManager.NewBlock(statelessStandardBlock)
   681  
   682  	// update staker set
   683  	require.NoError(block.Verify(context.Background()))
   684  
   685  	blkStateMap := env.blkManager.(*manager).blkIDToState
   686  	updatedState := blkStateMap[block.ID()].onAcceptState
   687  	_, err = updatedState.GetCurrentValidator(subnetID, subnetValidatorNodeID)
   688  	require.ErrorIs(err, database.ErrNotFound)
   689  
   690  	// Check VM Validators are removed successfully
   691  	require.NoError(block.Accept(context.Background()))
   692  	_, ok := env.config.Validators.GetValidator(subnetID, subnetVdr2NodeID)
   693  	require.False(ok)
   694  	_, ok = env.config.Validators.GetValidator(subnetID, subnetValidatorNodeID)
   695  	require.False(ok)
   696  }
   697  
   698  func TestBanffStandardBlockTrackedSubnet(t *testing.T) {
   699  	for _, tracked := range []bool{true, false} {
   700  		t.Run(fmt.Sprintf("tracked %t", tracked), func(t *testing.T) {
   701  			require := require.New(t)
   702  			env := newEnvironment(t, nil, upgradetest.Banff)
   703  
   704  			subnetID := testSubnet1.ID()
   705  			if tracked {
   706  				env.config.TrackedSubnets.Add(subnetID)
   707  			}
   708  
   709  			wallet := newWallet(t, env, walletConfig{
   710  				subnetIDs: []ids.ID{subnetID},
   711  			})
   712  
   713  			// Add a subnet validator to the staker set
   714  			subnetValidatorNodeID := genesistest.DefaultNodeIDs[0]
   715  			subnetVdr1StartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Minute)
   716  			subnetVdr1EndTime := genesistest.DefaultValidatorStartTime.Add(10 * defaultMinStakingDuration).Add(1 * time.Minute)
   717  			tx, err := wallet.IssueAddSubnetValidatorTx(
   718  				&txs.SubnetValidator{
   719  					Validator: txs.Validator{
   720  						NodeID: subnetValidatorNodeID,
   721  						Start:  uint64(subnetVdr1StartTime.Unix()),
   722  						End:    uint64(subnetVdr1EndTime.Unix()),
   723  						Wght:   1,
   724  					},
   725  					Subnet: subnetID,
   726  				},
   727  			)
   728  			require.NoError(err)
   729  
   730  			staker, err := state.NewPendingStaker(
   731  				tx.ID(),
   732  				tx.Unsigned.(*txs.AddSubnetValidatorTx),
   733  			)
   734  			require.NoError(err)
   735  
   736  			require.NoError(env.state.PutPendingValidator(staker))
   737  			env.state.AddTx(tx, status.Committed)
   738  			require.NoError(env.state.Commit())
   739  
   740  			// Advance time to the staker's start time.
   741  			env.clk.Set(subnetVdr1StartTime)
   742  
   743  			// build standard block moving ahead chain time
   744  			preferredID := env.state.GetLastAccepted()
   745  			parentBlk, err := env.state.GetStatelessBlock(preferredID)
   746  			require.NoError(err)
   747  			statelessStandardBlock, err := block.NewBanffStandardBlock(
   748  				subnetVdr1StartTime,
   749  				parentBlk.ID(),
   750  				parentBlk.Height()+1,
   751  				nil, // txs nulled to simplify test
   752  			)
   753  			require.NoError(err)
   754  			block := env.blkManager.NewBlock(statelessStandardBlock)
   755  
   756  			// update staker set
   757  			require.NoError(block.Verify(context.Background()))
   758  			require.NoError(block.Accept(context.Background()))
   759  			_, ok := env.config.Validators.GetValidator(subnetID, subnetValidatorNodeID)
   760  			require.True(ok)
   761  		})
   762  	}
   763  }
   764  
   765  func TestBanffStandardBlockDelegatorStakerWeight(t *testing.T) {
   766  	require := require.New(t)
   767  	env := newEnvironment(t, nil, upgradetest.Banff)
   768  
   769  	// Case: Timestamp is after next validator start time
   770  	// Add a pending validator
   771  	pendingValidatorStartTime := genesistest.DefaultValidatorStartTime.Add(1 * time.Second)
   772  	pendingValidatorEndTime := pendingValidatorStartTime.Add(defaultMaxStakingDuration)
   773  	nodeID := ids.GenerateTestNodeID()
   774  	rewardAddress := ids.GenerateTestShortID()
   775  	addPendingValidator(
   776  		t,
   777  		env,
   778  		pendingValidatorStartTime,
   779  		pendingValidatorEndTime,
   780  		nodeID,
   781  		rewardAddress,
   782  		[]*secp256k1.PrivateKey{genesistest.DefaultFundedKeys[0]},
   783  	)
   784  
   785  	// build standard block moving ahead chain time
   786  	preferredID := env.state.GetLastAccepted()
   787  	parentBlk, err := env.state.GetStatelessBlock(preferredID)
   788  	require.NoError(err)
   789  	statelessStandardBlock, err := block.NewBanffStandardBlock(
   790  		pendingValidatorStartTime,
   791  		parentBlk.ID(),
   792  		parentBlk.Height()+1,
   793  		nil, // txs nulled to simplify test
   794  	)
   795  	require.NoError(err)
   796  	blk := env.blkManager.NewBlock(statelessStandardBlock)
   797  	require.NoError(blk.Verify(context.Background()))
   798  	require.NoError(blk.Accept(context.Background()))
   799  	require.NoError(env.state.Commit())
   800  
   801  	// Test validator weight before delegation
   802  	vdrWeight := env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID)
   803  	require.Equal(env.config.MinValidatorStake, vdrWeight)
   804  
   805  	wallet := newWallet(t, env, walletConfig{})
   806  
   807  	// Add delegator
   808  	pendingDelegatorStartTime := pendingValidatorStartTime.Add(1 * time.Second)
   809  	pendingDelegatorEndTime := pendingDelegatorStartTime.Add(1 * time.Second)
   810  
   811  	addDelegatorTx, err := wallet.IssueAddDelegatorTx(
   812  		&txs.Validator{
   813  			NodeID: nodeID,
   814  			Start:  uint64(pendingDelegatorStartTime.Unix()),
   815  			End:    uint64(pendingDelegatorEndTime.Unix()),
   816  			Wght:   env.config.MinDelegatorStake,
   817  		},
   818  		&secp256k1fx.OutputOwners{
   819  			Threshold: 1,
   820  			Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
   821  		},
   822  	)
   823  	require.NoError(err)
   824  
   825  	staker, err := state.NewPendingStaker(
   826  		addDelegatorTx.ID(),
   827  		addDelegatorTx.Unsigned.(*txs.AddDelegatorTx),
   828  	)
   829  	require.NoError(err)
   830  
   831  	env.state.PutPendingDelegator(staker)
   832  	env.state.AddTx(addDelegatorTx, status.Committed)
   833  	env.state.SetHeight( /*dummyHeight*/ uint64(1))
   834  	require.NoError(env.state.Commit())
   835  
   836  	// Advance Time
   837  	preferredID = env.state.GetLastAccepted()
   838  	parentBlk, err = env.state.GetStatelessBlock(preferredID)
   839  	require.NoError(err)
   840  	statelessStandardBlock, err = block.NewBanffStandardBlock(
   841  		pendingDelegatorStartTime,
   842  		parentBlk.ID(),
   843  		parentBlk.Height()+1,
   844  		nil, // txs nulled to simplify test
   845  	)
   846  	require.NoError(err)
   847  	blk = env.blkManager.NewBlock(statelessStandardBlock)
   848  	require.NoError(blk.Verify(context.Background()))
   849  	require.NoError(blk.Accept(context.Background()))
   850  	require.NoError(env.state.Commit())
   851  
   852  	// Test validator weight after delegation
   853  	vdrWeight = env.config.Validators.GetWeight(constants.PrimaryNetworkID, nodeID)
   854  	require.Equal(env.config.MinDelegatorStake+env.config.MinValidatorStake, vdrWeight)
   855  }