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