github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/executor/standard_tx_executor_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  	"errors"
     9  	"math"
    10  	"math/rand"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  	"go.uber.org/mock/gomock"
    16  
    17  	"github.com/MetalBlockchain/metalgo/database"
    18  	"github.com/MetalBlockchain/metalgo/ids"
    19  	"github.com/MetalBlockchain/metalgo/snow"
    20  	"github.com/MetalBlockchain/metalgo/utils"
    21  	"github.com/MetalBlockchain/metalgo/utils/constants"
    22  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    23  	"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
    24  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    25  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    26  	"github.com/MetalBlockchain/metalgo/utils/units"
    27  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    28  	"github.com/MetalBlockchain/metalgo/vms/components/verify"
    29  	"github.com/MetalBlockchain/metalgo/vms/platformvm/config"
    30  	"github.com/MetalBlockchain/metalgo/vms/platformvm/fx"
    31  	"github.com/MetalBlockchain/metalgo/vms/platformvm/reward"
    32  	"github.com/MetalBlockchain/metalgo/vms/platformvm/signer"
    33  	"github.com/MetalBlockchain/metalgo/vms/platformvm/state"
    34  	"github.com/MetalBlockchain/metalgo/vms/platformvm/status"
    35  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    36  	"github.com/MetalBlockchain/metalgo/vms/platformvm/upgrade"
    37  	"github.com/MetalBlockchain/metalgo/vms/platformvm/utxo"
    38  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    39  	"github.com/MetalBlockchain/metalgo/wallet/subnet/primary/common"
    40  
    41  	walletsigner "github.com/MetalBlockchain/metalgo/wallet/chain/p/signer"
    42  )
    43  
    44  // This tests that the math performed during TransformSubnetTx execution can
    45  // never overflow
    46  const _ time.Duration = math.MaxUint32 * time.Second
    47  
    48  var errTest = errors.New("non-nil error")
    49  
    50  func TestStandardTxExecutorAddValidatorTxEmptyID(t *testing.T) {
    51  	require := require.New(t)
    52  	env := newEnvironment(t, apricotPhase5)
    53  	env.ctx.Lock.Lock()
    54  	defer env.ctx.Lock.Unlock()
    55  
    56  	chainTime := env.state.GetTimestamp()
    57  	startTime := defaultValidateStartTime.Add(1 * time.Second)
    58  
    59  	tests := []struct {
    60  		banffTime     time.Time
    61  		expectedError error
    62  	}{
    63  		{ // Case: Before banff
    64  			banffTime:     chainTime.Add(1),
    65  			expectedError: errEmptyNodeID,
    66  		},
    67  		{ // Case: At banff
    68  			banffTime:     chainTime,
    69  			expectedError: errEmptyNodeID,
    70  		},
    71  		{ // Case: After banff
    72  			banffTime:     chainTime.Add(-1),
    73  			expectedError: errEmptyNodeID,
    74  		},
    75  	}
    76  	for _, test := range tests {
    77  		// Case: Empty validator node ID after banff
    78  		env.config.UpgradeConfig.BanffTime = test.banffTime
    79  
    80  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
    81  		utx, err := builder.NewAddValidatorTx(
    82  			&txs.Validator{
    83  				NodeID: ids.EmptyNodeID,
    84  				Start:  uint64(startTime.Unix()),
    85  				End:    uint64(defaultValidateEndTime.Unix()),
    86  				Wght:   env.config.MinValidatorStake,
    87  			},
    88  			&secp256k1fx.OutputOwners{
    89  				Threshold: 1,
    90  				Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
    91  			},
    92  			reward.PercentDenominator,
    93  		)
    94  		require.NoError(err)
    95  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
    96  		require.NoError(err)
    97  
    98  		stateDiff, err := state.NewDiff(lastAcceptedID, env)
    99  		require.NoError(err)
   100  
   101  		executor := StandardTxExecutor{
   102  			Backend: &env.backend,
   103  			State:   stateDiff,
   104  			Tx:      tx,
   105  		}
   106  		err = tx.Unsigned.Visit(&executor)
   107  		require.ErrorIs(err, test.expectedError)
   108  	}
   109  }
   110  
   111  func TestStandardTxExecutorAddDelegator(t *testing.T) {
   112  	dummyHeight := uint64(1)
   113  	rewardAddress := preFundedKeys[0].PublicKey().Address()
   114  	nodeID := genesisNodeIDs[0]
   115  
   116  	newValidatorID := ids.GenerateTestNodeID()
   117  	newValidatorStartTime := defaultValidateStartTime.Add(5 * time.Second)
   118  	newValidatorEndTime := defaultValidateEndTime.Add(-5 * time.Second)
   119  
   120  	// [addMinStakeValidator] adds a new validator to the primary network's
   121  	// pending validator set with the minimum staking amount
   122  	addMinStakeValidator := func(env *environment) {
   123  		require := require.New(t)
   124  
   125  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
   126  		utx, err := builder.NewAddValidatorTx(
   127  			&txs.Validator{
   128  				NodeID: newValidatorID,
   129  				Start:  uint64(newValidatorStartTime.Unix()),
   130  				End:    uint64(newValidatorEndTime.Unix()),
   131  				Wght:   env.config.MinValidatorStake,
   132  			},
   133  			&secp256k1fx.OutputOwners{
   134  				Threshold: 1,
   135  				Addrs:     []ids.ShortID{rewardAddress},
   136  			},
   137  			reward.PercentDenominator,
   138  		)
   139  		require.NoError(err)
   140  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   141  		require.NoError(err)
   142  
   143  		addValTx := tx.Unsigned.(*txs.AddValidatorTx)
   144  		staker, err := state.NewCurrentStaker(
   145  			tx.ID(),
   146  			addValTx,
   147  			newValidatorStartTime,
   148  			0,
   149  		)
   150  		require.NoError(err)
   151  
   152  		env.state.PutCurrentValidator(staker)
   153  		env.state.AddTx(tx, status.Committed)
   154  		env.state.SetHeight(dummyHeight)
   155  		require.NoError(env.state.Commit())
   156  	}
   157  
   158  	// [addMaxStakeValidator] adds a new validator to the primary network's
   159  	// pending validator set with the maximum staking amount
   160  	addMaxStakeValidator := func(env *environment) {
   161  		require := require.New(t)
   162  
   163  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
   164  		utx, err := builder.NewAddValidatorTx(
   165  			&txs.Validator{
   166  				NodeID: newValidatorID,
   167  				Start:  uint64(newValidatorStartTime.Unix()),
   168  				End:    uint64(newValidatorEndTime.Unix()),
   169  				Wght:   env.config.MaxValidatorStake,
   170  			},
   171  			&secp256k1fx.OutputOwners{
   172  				Threshold: 1,
   173  				Addrs:     []ids.ShortID{rewardAddress},
   174  			},
   175  			reward.PercentDenominator,
   176  		)
   177  		require.NoError(err)
   178  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   179  		require.NoError(err)
   180  
   181  		addValTx := tx.Unsigned.(*txs.AddValidatorTx)
   182  		staker, err := state.NewCurrentStaker(
   183  			tx.ID(),
   184  			addValTx,
   185  			newValidatorStartTime,
   186  			0,
   187  		)
   188  		require.NoError(err)
   189  
   190  		env.state.PutCurrentValidator(staker)
   191  		env.state.AddTx(tx, status.Committed)
   192  		env.state.SetHeight(dummyHeight)
   193  		require.NoError(env.state.Commit())
   194  	}
   195  
   196  	env := newEnvironment(t, apricotPhase5)
   197  	currentTimestamp := env.state.GetTimestamp()
   198  
   199  	type test struct {
   200  		description          string
   201  		stakeAmount          uint64
   202  		startTime            time.Time
   203  		endTime              time.Time
   204  		nodeID               ids.NodeID
   205  		feeKeys              []*secp256k1.PrivateKey
   206  		setup                func(*environment)
   207  		AP3Time              time.Time
   208  		expectedExecutionErr error
   209  	}
   210  
   211  	tests := []test{
   212  		{
   213  			description:          "validator stops validating earlier than delegator",
   214  			stakeAmount:          env.config.MinDelegatorStake,
   215  			startTime:            defaultValidateStartTime.Add(time.Second),
   216  			endTime:              defaultValidateEndTime.Add(time.Second),
   217  			nodeID:               nodeID,
   218  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   219  			setup:                nil,
   220  			AP3Time:              defaultGenesisTime,
   221  			expectedExecutionErr: ErrPeriodMismatch,
   222  		},
   223  		{
   224  			description:          "validator not in the current or pending validator sets",
   225  			stakeAmount:          env.config.MinDelegatorStake,
   226  			startTime:            defaultValidateStartTime.Add(5 * time.Second),
   227  			endTime:              defaultValidateEndTime.Add(-5 * time.Second),
   228  			nodeID:               newValidatorID,
   229  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   230  			setup:                nil,
   231  			AP3Time:              defaultGenesisTime,
   232  			expectedExecutionErr: database.ErrNotFound,
   233  		},
   234  		{
   235  			description:          "delegator starts before validator",
   236  			stakeAmount:          env.config.MinDelegatorStake,
   237  			startTime:            newValidatorStartTime.Add(-1 * time.Second), // start validating subnet before primary network
   238  			endTime:              newValidatorEndTime,
   239  			nodeID:               newValidatorID,
   240  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   241  			setup:                addMinStakeValidator,
   242  			AP3Time:              defaultGenesisTime,
   243  			expectedExecutionErr: ErrPeriodMismatch,
   244  		},
   245  		{
   246  			description:          "delegator stops before validator",
   247  			stakeAmount:          env.config.MinDelegatorStake,
   248  			startTime:            newValidatorStartTime,
   249  			endTime:              newValidatorEndTime.Add(time.Second), // stop validating subnet after stopping validating primary network
   250  			nodeID:               newValidatorID,
   251  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   252  			setup:                addMinStakeValidator,
   253  			AP3Time:              defaultGenesisTime,
   254  			expectedExecutionErr: ErrPeriodMismatch,
   255  		},
   256  		{
   257  			description:          "valid",
   258  			stakeAmount:          env.config.MinDelegatorStake,
   259  			startTime:            newValidatorStartTime, // same start time as for primary network
   260  			endTime:              newValidatorEndTime,   // same end time as for primary network
   261  			nodeID:               newValidatorID,
   262  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   263  			setup:                addMinStakeValidator,
   264  			AP3Time:              defaultGenesisTime,
   265  			expectedExecutionErr: nil,
   266  		},
   267  		{
   268  			description:          "starts delegating at current timestamp",
   269  			stakeAmount:          env.config.MinDelegatorStake,              // weight
   270  			startTime:            currentTimestamp,                          // start time
   271  			endTime:              defaultValidateEndTime,                    // end time
   272  			nodeID:               nodeID,                                    // node ID
   273  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]}, // tx fee payer
   274  			setup:                nil,
   275  			AP3Time:              defaultGenesisTime,
   276  			expectedExecutionErr: ErrTimestampNotBeforeStartTime,
   277  		},
   278  		{
   279  			description: "tx fee paying key has no funds",
   280  			stakeAmount: env.config.MinDelegatorStake,              // weight
   281  			startTime:   defaultValidateStartTime.Add(time.Second), // start time
   282  			endTime:     defaultValidateEndTime,                    // end time
   283  			nodeID:      nodeID,                                    // node ID
   284  			feeKeys:     []*secp256k1.PrivateKey{preFundedKeys[1]}, // tx fee payer
   285  			setup: func(env *environment) { // Remove all UTXOs owned by keys[1]
   286  				utxoIDs, err := env.state.UTXOIDs(
   287  					preFundedKeys[1].PublicKey().Address().Bytes(),
   288  					ids.Empty,
   289  					math.MaxInt32)
   290  				require.NoError(t, err)
   291  
   292  				for _, utxoID := range utxoIDs {
   293  					env.state.DeleteUTXO(utxoID)
   294  				}
   295  				env.state.SetHeight(dummyHeight)
   296  				require.NoError(t, env.state.Commit())
   297  			},
   298  			AP3Time:              defaultGenesisTime,
   299  			expectedExecutionErr: ErrFlowCheckFailed,
   300  		},
   301  		{
   302  			description:          "over delegation before AP3",
   303  			stakeAmount:          env.config.MinDelegatorStake,
   304  			startTime:            newValidatorStartTime, // same start time as for primary network
   305  			endTime:              newValidatorEndTime,   // same end time as for primary network
   306  			nodeID:               newValidatorID,
   307  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   308  			setup:                addMaxStakeValidator,
   309  			AP3Time:              defaultValidateEndTime,
   310  			expectedExecutionErr: nil,
   311  		},
   312  		{
   313  			description:          "over delegation after AP3",
   314  			stakeAmount:          env.config.MinDelegatorStake,
   315  			startTime:            newValidatorStartTime, // same start time as for primary network
   316  			endTime:              newValidatorEndTime,   // same end time as for primary network
   317  			nodeID:               newValidatorID,
   318  			feeKeys:              []*secp256k1.PrivateKey{preFundedKeys[0]},
   319  			setup:                addMaxStakeValidator,
   320  			AP3Time:              defaultGenesisTime,
   321  			expectedExecutionErr: ErrOverDelegated,
   322  		},
   323  	}
   324  
   325  	for _, tt := range tests {
   326  		t.Run(tt.description, func(t *testing.T) {
   327  			require := require.New(t)
   328  			env := newEnvironment(t, apricotPhase5)
   329  			env.config.UpgradeConfig.ApricotPhase3Time = tt.AP3Time
   330  
   331  			builder, signer := env.factory.NewWallet(tt.feeKeys...)
   332  			utx, err := builder.NewAddDelegatorTx(
   333  				&txs.Validator{
   334  					NodeID: tt.nodeID,
   335  					Start:  uint64(tt.startTime.Unix()),
   336  					End:    uint64(tt.endTime.Unix()),
   337  					Wght:   tt.stakeAmount,
   338  				},
   339  				&secp256k1fx.OutputOwners{
   340  					Threshold: 1,
   341  					Addrs:     []ids.ShortID{rewardAddress},
   342  				},
   343  			)
   344  			require.NoError(err)
   345  			tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   346  			require.NoError(err)
   347  
   348  			if tt.setup != nil {
   349  				tt.setup(env)
   350  			}
   351  
   352  			onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   353  			require.NoError(err)
   354  
   355  			env.config.UpgradeConfig.BanffTime = onAcceptState.GetTimestamp()
   356  
   357  			executor := StandardTxExecutor{
   358  				Backend: &env.backend,
   359  				State:   onAcceptState,
   360  				Tx:      tx,
   361  			}
   362  			err = tx.Unsigned.Visit(&executor)
   363  			require.ErrorIs(err, tt.expectedExecutionErr)
   364  		})
   365  	}
   366  }
   367  
   368  func TestApricotStandardTxExecutorAddSubnetValidator(t *testing.T) {
   369  	require := require.New(t)
   370  	env := newEnvironment(t, apricotPhase5)
   371  	env.ctx.Lock.Lock()
   372  	defer env.ctx.Lock.Unlock()
   373  
   374  	nodeID := genesisNodeIDs[0]
   375  
   376  	{
   377  		// Case: Proposed validator currently validating primary network
   378  		// but stops validating subnet after stops validating primary network
   379  		// (note that keys[0] is a genesis validator)
   380  		startTime := defaultValidateStartTime.Add(time.Second)
   381  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   382  		utx, err := builder.NewAddSubnetValidatorTx(
   383  			&txs.SubnetValidator{
   384  				Validator: txs.Validator{
   385  					NodeID: nodeID,
   386  					Start:  uint64(startTime.Unix()),
   387  					End:    uint64(defaultValidateEndTime.Unix()) + 1,
   388  					Wght:   defaultWeight,
   389  				},
   390  				Subnet: testSubnet1.ID(),
   391  			},
   392  		)
   393  		require.NoError(err)
   394  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   395  		require.NoError(err)
   396  
   397  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   398  		require.NoError(err)
   399  
   400  		executor := StandardTxExecutor{
   401  			Backend: &env.backend,
   402  			State:   onAcceptState,
   403  			Tx:      tx,
   404  		}
   405  		err = tx.Unsigned.Visit(&executor)
   406  		require.ErrorIs(err, ErrPeriodMismatch)
   407  	}
   408  
   409  	{
   410  		// Case: Proposed validator currently validating primary network
   411  		// and proposed subnet validation period is subset of
   412  		// primary network validation period
   413  		// (note that keys[0] is a genesis validator)
   414  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   415  		utx, err := builder.NewAddSubnetValidatorTx(
   416  			&txs.SubnetValidator{
   417  				Validator: txs.Validator{
   418  					NodeID: nodeID,
   419  					Start:  uint64(defaultValidateStartTime.Unix() + 1),
   420  					End:    uint64(defaultValidateEndTime.Unix()),
   421  					Wght:   defaultWeight,
   422  				},
   423  				Subnet: testSubnet1.ID(),
   424  			},
   425  		)
   426  		require.NoError(err)
   427  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   428  		require.NoError(err)
   429  
   430  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   431  		require.NoError(err)
   432  
   433  		executor := StandardTxExecutor{
   434  			Backend: &env.backend,
   435  			State:   onAcceptState,
   436  			Tx:      tx,
   437  		}
   438  		require.NoError(tx.Unsigned.Visit(&executor))
   439  	}
   440  
   441  	// Add a validator to pending validator set of primary network
   442  	// Starts validating primary network 10 seconds after genesis
   443  	pendingDSValidatorID := ids.GenerateTestNodeID()
   444  	dsStartTime := defaultGenesisTime.Add(10 * time.Second)
   445  	dsEndTime := dsStartTime.Add(5 * defaultMinStakingDuration)
   446  
   447  	builder, signer := env.factory.NewWallet(preFundedKeys[0])
   448  	utx, err := builder.NewAddValidatorTx(
   449  		&txs.Validator{
   450  			NodeID: pendingDSValidatorID,
   451  			Start:  uint64(dsStartTime.Unix()),
   452  			End:    uint64(dsEndTime.Unix()),
   453  			Wght:   env.config.MinValidatorStake,
   454  		},
   455  		&secp256k1fx.OutputOwners{
   456  			Threshold: 1,
   457  			Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
   458  		},
   459  		reward.PercentDenominator,
   460  	)
   461  	require.NoError(err)
   462  	addDSTx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   463  	require.NoError(err)
   464  
   465  	{
   466  		// Case: Proposed validator isn't in pending or current validator sets
   467  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   468  		utx, err := builder.NewAddSubnetValidatorTx(
   469  			&txs.SubnetValidator{
   470  				Validator: txs.Validator{
   471  					NodeID: pendingDSValidatorID,
   472  					Start:  uint64(dsStartTime.Unix()), // start validating subnet before primary network
   473  					End:    uint64(dsEndTime.Unix()),
   474  					Wght:   defaultWeight,
   475  				},
   476  				Subnet: testSubnet1.ID(),
   477  			},
   478  		)
   479  		require.NoError(err)
   480  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   481  		require.NoError(err)
   482  
   483  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   484  		require.NoError(err)
   485  
   486  		executor := StandardTxExecutor{
   487  			Backend: &env.backend,
   488  			State:   onAcceptState,
   489  			Tx:      tx,
   490  		}
   491  		err = tx.Unsigned.Visit(&executor)
   492  		require.ErrorIs(err, ErrNotValidator)
   493  	}
   494  
   495  	addValTx := addDSTx.Unsigned.(*txs.AddValidatorTx)
   496  	staker, err := state.NewCurrentStaker(
   497  		addDSTx.ID(),
   498  		addValTx,
   499  		dsStartTime,
   500  		0,
   501  	)
   502  	require.NoError(err)
   503  
   504  	env.state.PutCurrentValidator(staker)
   505  	env.state.AddTx(addDSTx, status.Committed)
   506  	dummyHeight := uint64(1)
   507  	env.state.SetHeight(dummyHeight)
   508  	require.NoError(env.state.Commit())
   509  
   510  	// Node with ID key.PublicKey().Address() now a pending validator for primary network
   511  
   512  	{
   513  		// Case: Proposed validator is pending validator of primary network
   514  		// but starts validating subnet before primary network
   515  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   516  		utx, err := builder.NewAddSubnetValidatorTx(
   517  			&txs.SubnetValidator{
   518  				Validator: txs.Validator{
   519  					NodeID: pendingDSValidatorID,
   520  					Start:  uint64(dsStartTime.Unix()) - 1, // start validating subnet before primary network
   521  					End:    uint64(dsEndTime.Unix()),
   522  					Wght:   defaultWeight,
   523  				},
   524  				Subnet: testSubnet1.ID(),
   525  			},
   526  		)
   527  		require.NoError(err)
   528  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   529  		require.NoError(err)
   530  
   531  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   532  		require.NoError(err)
   533  
   534  		executor := StandardTxExecutor{
   535  			Backend: &env.backend,
   536  			State:   onAcceptState,
   537  			Tx:      tx,
   538  		}
   539  		err = tx.Unsigned.Visit(&executor)
   540  		require.ErrorIs(err, ErrPeriodMismatch)
   541  	}
   542  
   543  	{
   544  		// Case: Proposed validator is pending validator of primary network
   545  		// but stops validating subnet after primary network
   546  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   547  		utx, err := builder.NewAddSubnetValidatorTx(
   548  			&txs.SubnetValidator{
   549  				Validator: txs.Validator{
   550  					NodeID: pendingDSValidatorID,
   551  					Start:  uint64(dsStartTime.Unix()),
   552  					End:    uint64(dsEndTime.Unix()) + 1, // stop validating subnet after stopping validating primary network
   553  					Wght:   defaultWeight,
   554  				},
   555  				Subnet: testSubnet1.ID(),
   556  			},
   557  		)
   558  		require.NoError(err)
   559  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   560  		require.NoError(err)
   561  
   562  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   563  		require.NoError(err)
   564  
   565  		executor := StandardTxExecutor{
   566  			Backend: &env.backend,
   567  			State:   onAcceptState,
   568  			Tx:      tx,
   569  		}
   570  		err = tx.Unsigned.Visit(&executor)
   571  		require.ErrorIs(err, ErrPeriodMismatch)
   572  	}
   573  
   574  	{
   575  		// Case: Proposed validator is pending validator of primary network and
   576  		// period validating subnet is subset of time validating primary network
   577  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   578  		utx, err := builder.NewAddSubnetValidatorTx(
   579  			&txs.SubnetValidator{
   580  				Validator: txs.Validator{
   581  					NodeID: pendingDSValidatorID,
   582  					Start:  uint64(dsStartTime.Unix()), // same start time as for primary network
   583  					End:    uint64(dsEndTime.Unix()),   // same end time as for primary network
   584  					Wght:   defaultWeight,
   585  				},
   586  				Subnet: testSubnet1.ID(),
   587  			},
   588  		)
   589  		require.NoError(err)
   590  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   591  		require.NoError(err)
   592  
   593  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   594  		require.NoError(err)
   595  		executor := StandardTxExecutor{
   596  			Backend: &env.backend,
   597  			State:   onAcceptState,
   598  			Tx:      tx,
   599  		}
   600  		require.NoError(tx.Unsigned.Visit(&executor))
   601  	}
   602  
   603  	// Case: Proposed validator start validating at/before current timestamp
   604  	// First, advance the timestamp
   605  	newTimestamp := defaultGenesisTime.Add(2 * time.Second)
   606  	env.state.SetTimestamp(newTimestamp)
   607  
   608  	{
   609  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   610  		utx, err := builder.NewAddSubnetValidatorTx(
   611  			&txs.SubnetValidator{
   612  				Validator: txs.Validator{
   613  					NodeID: nodeID,
   614  					Start:  uint64(newTimestamp.Unix()),
   615  					End:    uint64(newTimestamp.Add(defaultMinStakingDuration).Unix()),
   616  					Wght:   defaultWeight,
   617  				},
   618  				Subnet: testSubnet1.ID(),
   619  			},
   620  		)
   621  		require.NoError(err)
   622  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   623  		require.NoError(err)
   624  
   625  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   626  		require.NoError(err)
   627  
   628  		executor := StandardTxExecutor{
   629  			Backend: &env.backend,
   630  			State:   onAcceptState,
   631  			Tx:      tx,
   632  		}
   633  		err = tx.Unsigned.Visit(&executor)
   634  		require.ErrorIs(err, ErrTimestampNotBeforeStartTime)
   635  	}
   636  
   637  	// reset the timestamp
   638  	env.state.SetTimestamp(defaultGenesisTime)
   639  
   640  	// Case: Proposed validator already validating the subnet
   641  	// First, add validator as validator of subnet
   642  	builder, signer = env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   643  	uSubnetTx, err := builder.NewAddSubnetValidatorTx(
   644  		&txs.SubnetValidator{
   645  			Validator: txs.Validator{
   646  				NodeID: nodeID,
   647  				Start:  uint64(defaultValidateStartTime.Unix()),
   648  				End:    uint64(defaultValidateEndTime.Unix()),
   649  				Wght:   defaultWeight,
   650  			},
   651  			Subnet: testSubnet1.ID(),
   652  		},
   653  	)
   654  	require.NoError(err)
   655  	subnetTx, err := walletsigner.SignUnsigned(context.Background(), signer, uSubnetTx)
   656  	require.NoError(err)
   657  
   658  	addSubnetValTx := subnetTx.Unsigned.(*txs.AddSubnetValidatorTx)
   659  	staker, err = state.NewCurrentStaker(
   660  		subnetTx.ID(),
   661  		addSubnetValTx,
   662  		defaultValidateStartTime,
   663  		0,
   664  	)
   665  	require.NoError(err)
   666  
   667  	env.state.PutCurrentValidator(staker)
   668  	env.state.AddTx(subnetTx, status.Committed)
   669  	env.state.SetHeight(dummyHeight)
   670  	require.NoError(env.state.Commit())
   671  
   672  	{
   673  		// Node with ID nodeIDKey.PublicKey().Address() now validating subnet with ID testSubnet1.ID
   674  		startTime := defaultValidateStartTime.Add(time.Second)
   675  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   676  		utx, err := builder.NewAddSubnetValidatorTx(
   677  			&txs.SubnetValidator{
   678  				Validator: txs.Validator{
   679  					NodeID: nodeID,
   680  					Start:  uint64(startTime.Unix()),
   681  					End:    uint64(defaultValidateEndTime.Unix()),
   682  					Wght:   defaultWeight,
   683  				},
   684  				Subnet: testSubnet1.ID(),
   685  			},
   686  		)
   687  		require.NoError(err)
   688  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   689  		require.NoError(err)
   690  
   691  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   692  		require.NoError(err)
   693  
   694  		executor := StandardTxExecutor{
   695  			Backend: &env.backend,
   696  			State:   onAcceptState,
   697  			Tx:      tx,
   698  		}
   699  		err = tx.Unsigned.Visit(&executor)
   700  		require.ErrorIs(err, ErrDuplicateValidator)
   701  	}
   702  
   703  	env.state.DeleteCurrentValidator(staker)
   704  	env.state.SetHeight(dummyHeight)
   705  	require.NoError(env.state.Commit())
   706  
   707  	{
   708  		// Case: Duplicate signatures
   709  		startTime := defaultValidateStartTime.Add(time.Second)
   710  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1], testSubnet1ControlKeys[2])
   711  		utx, err := builder.NewAddSubnetValidatorTx(
   712  			&txs.SubnetValidator{
   713  				Validator: txs.Validator{
   714  					NodeID: nodeID,
   715  					Start:  uint64(startTime.Unix()),
   716  					End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()) + 1,
   717  					Wght:   defaultWeight,
   718  				},
   719  				Subnet: testSubnet1.ID(),
   720  			},
   721  		)
   722  		require.NoError(err)
   723  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   724  		require.NoError(err)
   725  
   726  		// Duplicate a signature
   727  		addSubnetValidatorTx := tx.Unsigned.(*txs.AddSubnetValidatorTx)
   728  		input := addSubnetValidatorTx.SubnetAuth.(*secp256k1fx.Input)
   729  		input.SigIndices = append(input.SigIndices, input.SigIndices[0])
   730  		// This tx was syntactically verified when it was created...pretend it wasn't so we don't use cache
   731  		addSubnetValidatorTx.SyntacticallyVerified = false
   732  
   733  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   734  		require.NoError(err)
   735  
   736  		executor := StandardTxExecutor{
   737  			Backend: &env.backend,
   738  			State:   onAcceptState,
   739  			Tx:      tx,
   740  		}
   741  		err = tx.Unsigned.Visit(&executor)
   742  		require.ErrorIs(err, secp256k1fx.ErrInputIndicesNotSortedUnique)
   743  	}
   744  
   745  	{
   746  		// Case: Too few signatures
   747  		startTime := defaultValidateStartTime.Add(time.Second)
   748  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[2])
   749  		utx, err := builder.NewAddSubnetValidatorTx(
   750  			&txs.SubnetValidator{
   751  				Validator: txs.Validator{
   752  					NodeID: nodeID,
   753  					Start:  uint64(startTime.Unix()),
   754  					End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()),
   755  					Wght:   defaultWeight,
   756  				},
   757  				Subnet: testSubnet1.ID(),
   758  			},
   759  		)
   760  		require.NoError(err)
   761  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   762  		require.NoError(err)
   763  
   764  		// Remove a signature
   765  		addSubnetValidatorTx := tx.Unsigned.(*txs.AddSubnetValidatorTx)
   766  		input := addSubnetValidatorTx.SubnetAuth.(*secp256k1fx.Input)
   767  		input.SigIndices = input.SigIndices[1:]
   768  		// This tx was syntactically verified when it was created...pretend it wasn't so we don't use cache
   769  		addSubnetValidatorTx.SyntacticallyVerified = false
   770  
   771  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   772  		require.NoError(err)
   773  
   774  		executor := StandardTxExecutor{
   775  			Backend: &env.backend,
   776  			State:   onAcceptState,
   777  			Tx:      tx,
   778  		}
   779  		err = tx.Unsigned.Visit(&executor)
   780  		require.ErrorIs(err, errUnauthorizedSubnetModification)
   781  	}
   782  
   783  	{
   784  		// Case: Control Signature from invalid key (keys[3] is not a control key)
   785  		startTime := defaultValidateStartTime.Add(time.Second)
   786  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], preFundedKeys[1])
   787  		utx, err := builder.NewAddSubnetValidatorTx(
   788  			&txs.SubnetValidator{
   789  				Validator: txs.Validator{
   790  					NodeID: nodeID,
   791  					Start:  uint64(startTime.Unix()),
   792  					End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()),
   793  					Wght:   defaultWeight,
   794  				},
   795  				Subnet: testSubnet1.ID(),
   796  			},
   797  		)
   798  		require.NoError(err)
   799  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   800  		require.NoError(err)
   801  
   802  		// Replace a valid signature with one from keys[3]
   803  		sig, err := preFundedKeys[3].SignHash(hashing.ComputeHash256(tx.Unsigned.Bytes()))
   804  		require.NoError(err)
   805  		copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig)
   806  
   807  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   808  		require.NoError(err)
   809  
   810  		executor := StandardTxExecutor{
   811  			Backend: &env.backend,
   812  			State:   onAcceptState,
   813  			Tx:      tx,
   814  		}
   815  		err = tx.Unsigned.Visit(&executor)
   816  		require.ErrorIs(err, errUnauthorizedSubnetModification)
   817  	}
   818  
   819  	{
   820  		// Case: Proposed validator in pending validator set for subnet
   821  		// First, add validator to pending validator set of subnet
   822  		startTime := defaultValidateStartTime.Add(time.Second)
   823  		builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   824  		utx, err := builder.NewAddSubnetValidatorTx(
   825  			&txs.SubnetValidator{
   826  				Validator: txs.Validator{
   827  					NodeID: nodeID,
   828  					Start:  uint64(startTime.Unix()) + 1,
   829  					End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()) + 1,
   830  					Wght:   defaultWeight,
   831  				},
   832  				Subnet: testSubnet1.ID(),
   833  			},
   834  		)
   835  		require.NoError(err)
   836  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   837  		require.NoError(err)
   838  
   839  		addSubnetValTx := subnetTx.Unsigned.(*txs.AddSubnetValidatorTx)
   840  		staker, err = state.NewCurrentStaker(
   841  			subnetTx.ID(),
   842  			addSubnetValTx,
   843  			defaultValidateStartTime,
   844  			0,
   845  		)
   846  		require.NoError(err)
   847  
   848  		env.state.PutCurrentValidator(staker)
   849  		env.state.AddTx(tx, status.Committed)
   850  		env.state.SetHeight(dummyHeight)
   851  		require.NoError(env.state.Commit())
   852  
   853  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   854  		require.NoError(err)
   855  
   856  		executor := StandardTxExecutor{
   857  			Backend: &env.backend,
   858  			State:   onAcceptState,
   859  			Tx:      tx,
   860  		}
   861  		err = tx.Unsigned.Visit(&executor)
   862  		require.ErrorIs(err, ErrDuplicateValidator)
   863  	}
   864  }
   865  
   866  func TestBanffStandardTxExecutorAddValidator(t *testing.T) {
   867  	require := require.New(t)
   868  	env := newEnvironment(t, banff)
   869  	env.ctx.Lock.Lock()
   870  	defer env.ctx.Lock.Unlock()
   871  
   872  	nodeID := ids.GenerateTestNodeID()
   873  
   874  	{
   875  		// Case: Validator's start time too early
   876  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
   877  		utx, err := builder.NewAddValidatorTx(
   878  			&txs.Validator{
   879  				NodeID: nodeID,
   880  				Start:  uint64(defaultValidateStartTime.Unix()) - 1,
   881  				End:    uint64(defaultValidateEndTime.Unix()),
   882  				Wght:   env.config.MinValidatorStake,
   883  			},
   884  			&secp256k1fx.OutputOwners{
   885  				Threshold: 1,
   886  				Addrs:     []ids.ShortID{ids.ShortEmpty},
   887  			},
   888  			reward.PercentDenominator,
   889  		)
   890  		require.NoError(err)
   891  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   892  		require.NoError(err)
   893  
   894  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   895  		require.NoError(err)
   896  
   897  		executor := StandardTxExecutor{
   898  			Backend: &env.backend,
   899  			State:   onAcceptState,
   900  			Tx:      tx,
   901  		}
   902  		err = tx.Unsigned.Visit(&executor)
   903  		require.ErrorIs(err, ErrTimestampNotBeforeStartTime)
   904  	}
   905  
   906  	{
   907  		// Case: Validator in current validator set of primary network
   908  		startTime := defaultValidateStartTime.Add(1 * time.Second)
   909  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
   910  		utx, err := builder.NewAddValidatorTx(
   911  			&txs.Validator{
   912  				NodeID: nodeID,
   913  				Start:  uint64(startTime.Unix()),
   914  				End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()),
   915  				Wght:   env.config.MinValidatorStake,
   916  			},
   917  			&secp256k1fx.OutputOwners{
   918  				Threshold: 1,
   919  				Addrs:     []ids.ShortID{ids.ShortEmpty},
   920  			},
   921  			reward.PercentDenominator,
   922  		)
   923  		require.NoError(err)
   924  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   925  		require.NoError(err)
   926  
   927  		addValTx := tx.Unsigned.(*txs.AddValidatorTx)
   928  		staker, err := state.NewCurrentStaker(
   929  			tx.ID(),
   930  			addValTx,
   931  			startTime,
   932  			0,
   933  		)
   934  		require.NoError(err)
   935  
   936  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   937  		require.NoError(err)
   938  
   939  		onAcceptState.PutCurrentValidator(staker)
   940  		onAcceptState.AddTx(tx, status.Committed)
   941  
   942  		executor := StandardTxExecutor{
   943  			Backend: &env.backend,
   944  			State:   onAcceptState,
   945  			Tx:      tx,
   946  		}
   947  		err = tx.Unsigned.Visit(&executor)
   948  		require.ErrorIs(err, ErrAlreadyValidator)
   949  	}
   950  
   951  	{
   952  		// Case: Validator in pending validator set of primary network
   953  		startTime := defaultValidateStartTime.Add(1 * time.Second)
   954  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
   955  		utx, err := builder.NewAddValidatorTx(
   956  			&txs.Validator{
   957  				NodeID: nodeID,
   958  				Start:  uint64(startTime.Unix()),
   959  				End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()),
   960  				Wght:   env.config.MinValidatorStake,
   961  			},
   962  			&secp256k1fx.OutputOwners{
   963  				Threshold: 1,
   964  				Addrs:     []ids.ShortID{ids.ShortEmpty},
   965  			},
   966  			reward.PercentDenominator,
   967  		)
   968  		require.NoError(err)
   969  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   970  		require.NoError(err)
   971  
   972  		staker, err := state.NewPendingStaker(
   973  			tx.ID(),
   974  			tx.Unsigned.(*txs.AddValidatorTx),
   975  		)
   976  		require.NoError(err)
   977  
   978  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
   979  		require.NoError(err)
   980  
   981  		onAcceptState.PutPendingValidator(staker)
   982  		onAcceptState.AddTx(tx, status.Committed)
   983  
   984  		executor := StandardTxExecutor{
   985  			Backend: &env.backend,
   986  			State:   onAcceptState,
   987  			Tx:      tx,
   988  		}
   989  		err = tx.Unsigned.Visit(&executor)
   990  		require.ErrorIs(err, ErrAlreadyValidator)
   991  	}
   992  
   993  	{
   994  		// Case: Validator doesn't have enough tokens to cover stake amount
   995  		startTime := defaultValidateStartTime.Add(1 * time.Second)
   996  		builder, signer := env.factory.NewWallet(preFundedKeys[0])
   997  		utx, err := builder.NewAddValidatorTx(
   998  			&txs.Validator{
   999  				NodeID: nodeID,
  1000  				Start:  uint64(startTime.Unix()),
  1001  				End:    uint64(startTime.Add(defaultMinStakingDuration).Unix()),
  1002  				Wght:   env.config.MinValidatorStake,
  1003  			},
  1004  			&secp256k1fx.OutputOwners{
  1005  				Threshold: 1,
  1006  				Addrs:     []ids.ShortID{ids.ShortEmpty},
  1007  			},
  1008  			reward.PercentDenominator,
  1009  		)
  1010  		require.NoError(err)
  1011  		tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1012  		require.NoError(err)
  1013  
  1014  		// Remove all UTXOs owned by preFundedKeys[0]
  1015  		utxoIDs, err := env.state.UTXOIDs(preFundedKeys[0].PublicKey().Address().Bytes(), ids.Empty, math.MaxInt32)
  1016  		require.NoError(err)
  1017  
  1018  		onAcceptState, err := state.NewDiff(lastAcceptedID, env)
  1019  		require.NoError(err)
  1020  
  1021  		for _, utxoID := range utxoIDs {
  1022  			onAcceptState.DeleteUTXO(utxoID)
  1023  		}
  1024  
  1025  		executor := StandardTxExecutor{
  1026  			Backend: &env.backend,
  1027  			State:   onAcceptState,
  1028  			Tx:      tx,
  1029  		}
  1030  		err = tx.Unsigned.Visit(&executor)
  1031  		require.ErrorIs(err, ErrFlowCheckFailed)
  1032  	}
  1033  }
  1034  
  1035  // Verifies that [AddValidatorTx] and [AddDelegatorTx] are disabled post-Durango
  1036  func TestDurangoDisabledTransactions(t *testing.T) {
  1037  	type test struct {
  1038  		name        string
  1039  		buildTx     func(*environment) *txs.Tx
  1040  		expectedErr error
  1041  	}
  1042  
  1043  	tests := []test{
  1044  		{
  1045  			name: "AddValidatorTx",
  1046  			buildTx: func(env *environment) *txs.Tx {
  1047  				var (
  1048  					nodeID    = ids.GenerateTestNodeID()
  1049  					chainTime = env.state.GetTimestamp()
  1050  					endTime   = chainTime.Add(defaultMaxStakingDuration)
  1051  				)
  1052  
  1053  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1054  				utx, err := builder.NewAddValidatorTx(
  1055  					&txs.Validator{
  1056  						NodeID: nodeID,
  1057  						Start:  0,
  1058  						End:    uint64(endTime.Unix()),
  1059  						Wght:   defaultMinValidatorStake,
  1060  					},
  1061  					&secp256k1fx.OutputOwners{
  1062  						Threshold: 1,
  1063  						Addrs:     []ids.ShortID{ids.ShortEmpty},
  1064  					},
  1065  					reward.PercentDenominator,
  1066  				)
  1067  				require.NoError(t, err)
  1068  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1069  				require.NoError(t, err)
  1070  
  1071  				return tx
  1072  			},
  1073  			expectedErr: ErrAddValidatorTxPostDurango,
  1074  		},
  1075  		{
  1076  			name: "AddDelegatorTx",
  1077  			buildTx: func(env *environment) *txs.Tx {
  1078  				var primaryValidator *state.Staker
  1079  				it, err := env.state.GetCurrentStakerIterator()
  1080  				require.NoError(t, err)
  1081  				for it.Next() {
  1082  					staker := it.Value()
  1083  					if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority {
  1084  						continue
  1085  					}
  1086  					primaryValidator = staker
  1087  					break
  1088  				}
  1089  				it.Release()
  1090  
  1091  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1092  				utx, err := builder.NewAddDelegatorTx(
  1093  					&txs.Validator{
  1094  						NodeID: primaryValidator.NodeID,
  1095  						Start:  0,
  1096  						End:    uint64(primaryValidator.EndTime.Unix()),
  1097  						Wght:   defaultMinValidatorStake,
  1098  					},
  1099  					&secp256k1fx.OutputOwners{
  1100  						Threshold: 1,
  1101  						Addrs:     []ids.ShortID{ids.ShortEmpty},
  1102  					},
  1103  				)
  1104  				require.NoError(t, err)
  1105  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1106  				require.NoError(t, err)
  1107  
  1108  				return tx
  1109  			},
  1110  			expectedErr: ErrAddDelegatorTxPostDurango,
  1111  		},
  1112  	}
  1113  
  1114  	for _, tt := range tests {
  1115  		t.Run(tt.name, func(t *testing.T) {
  1116  			require := require.New(t)
  1117  
  1118  			env := newEnvironment(t, durango)
  1119  			env.ctx.Lock.Lock()
  1120  			defer env.ctx.Lock.Unlock()
  1121  
  1122  			onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1123  			require.NoError(err)
  1124  
  1125  			tx := tt.buildTx(env)
  1126  
  1127  			err = tx.Unsigned.Visit(&StandardTxExecutor{
  1128  				Backend: &env.backend,
  1129  				State:   onAcceptState,
  1130  				Tx:      tx,
  1131  			})
  1132  			require.ErrorIs(err, tt.expectedErr)
  1133  		})
  1134  	}
  1135  }
  1136  
  1137  // Verifies that the Memo field is required to be empty post-Durango
  1138  func TestDurangoMemoField(t *testing.T) {
  1139  	type test struct {
  1140  		name      string
  1141  		setupTest func(env *environment, memoField []byte) (*txs.Tx, state.Diff)
  1142  	}
  1143  
  1144  	tests := []test{
  1145  		{
  1146  			name: "AddSubnetValidatorTx",
  1147  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1148  				var primaryValidator *state.Staker
  1149  				it, err := env.state.GetCurrentStakerIterator()
  1150  				require.NoError(t, err)
  1151  				for it.Next() {
  1152  					staker := it.Value()
  1153  					if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority {
  1154  						continue
  1155  					}
  1156  					primaryValidator = staker
  1157  					break
  1158  				}
  1159  				it.Release()
  1160  
  1161  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1162  				utx, err := builder.NewAddSubnetValidatorTx(
  1163  					&txs.SubnetValidator{
  1164  						Validator: txs.Validator{
  1165  							NodeID: primaryValidator.NodeID,
  1166  							Start:  0,
  1167  							End:    uint64(primaryValidator.EndTime.Unix()),
  1168  							Wght:   defaultMinValidatorStake,
  1169  						},
  1170  						Subnet: testSubnet1.TxID,
  1171  					},
  1172  					common.WithMemo(memoField),
  1173  				)
  1174  				require.NoError(t, err)
  1175  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1176  				require.NoError(t, err)
  1177  
  1178  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1179  				require.NoError(t, err)
  1180  				return tx, onAcceptState
  1181  			},
  1182  		},
  1183  		{
  1184  			name: "CreateChainTx",
  1185  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1186  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1187  				utx, err := builder.NewCreateChainTx(
  1188  					testSubnet1.TxID,
  1189  					[]byte{},
  1190  					ids.GenerateTestID(),
  1191  					[]ids.ID{},
  1192  					"aaa",
  1193  					common.WithMemo(memoField),
  1194  				)
  1195  				require.NoError(t, err)
  1196  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1197  				require.NoError(t, err)
  1198  
  1199  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1200  				require.NoError(t, err)
  1201  
  1202  				return tx, onAcceptState
  1203  			},
  1204  		},
  1205  		{
  1206  			name: "CreateSubnetTx",
  1207  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1208  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1209  				utx, err := builder.NewCreateSubnetTx(
  1210  					&secp256k1fx.OutputOwners{
  1211  						Threshold: 1,
  1212  						Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
  1213  					},
  1214  					common.WithMemo(memoField),
  1215  				)
  1216  				require.NoError(t, err)
  1217  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1218  				require.NoError(t, err)
  1219  
  1220  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1221  				require.NoError(t, err)
  1222  
  1223  				return tx, onAcceptState
  1224  			},
  1225  		},
  1226  		{
  1227  			name: "ImportTx",
  1228  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1229  				// Skip shared memory checks
  1230  				env.backend.Bootstrapped.Set(false)
  1231  
  1232  				var (
  1233  					sourceChain  = env.ctx.XChainID
  1234  					sourceKey    = preFundedKeys[1]
  1235  					sourceAmount = 10 * units.Avax
  1236  				)
  1237  
  1238  				sharedMemory := fundedSharedMemory(
  1239  					t,
  1240  					env,
  1241  					sourceKey,
  1242  					sourceChain,
  1243  					map[ids.ID]uint64{
  1244  						env.ctx.AVAXAssetID: sourceAmount,
  1245  					},
  1246  					rand.NewSource(0),
  1247  				)
  1248  				env.msm.SharedMemory = sharedMemory
  1249  
  1250  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1251  				utx, err := builder.NewImportTx(
  1252  					sourceChain,
  1253  					&secp256k1fx.OutputOwners{
  1254  						Locktime:  0,
  1255  						Threshold: 1,
  1256  						Addrs:     []ids.ShortID{sourceKey.PublicKey().Address()},
  1257  					},
  1258  					common.WithMemo(memoField),
  1259  				)
  1260  				require.NoError(t, err)
  1261  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1262  				require.NoError(t, err)
  1263  
  1264  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1265  				require.NoError(t, err)
  1266  
  1267  				return tx, onAcceptState
  1268  			},
  1269  		},
  1270  		{
  1271  			name: "ExportTx",
  1272  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1273  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1274  				utx, err := builder.NewExportTx(
  1275  					env.ctx.XChainID,
  1276  					[]*avax.TransferableOutput{{
  1277  						Asset: avax.Asset{ID: env.ctx.AVAXAssetID},
  1278  						Out: &secp256k1fx.TransferOutput{
  1279  							Amt: units.Avax,
  1280  							OutputOwners: secp256k1fx.OutputOwners{
  1281  								Locktime:  0,
  1282  								Threshold: 1,
  1283  								Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
  1284  							},
  1285  						},
  1286  					}},
  1287  					common.WithMemo(memoField),
  1288  				)
  1289  				require.NoError(t, err)
  1290  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1291  				require.NoError(t, err)
  1292  
  1293  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1294  				require.NoError(t, err)
  1295  
  1296  				return tx, onAcceptState
  1297  			},
  1298  		},
  1299  		{
  1300  			name: "RemoveSubnetValidatorTx",
  1301  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1302  				var primaryValidator *state.Staker
  1303  				it, err := env.state.GetCurrentStakerIterator()
  1304  				require.NoError(t, err)
  1305  				for it.Next() {
  1306  					staker := it.Value()
  1307  					if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority {
  1308  						continue
  1309  					}
  1310  					primaryValidator = staker
  1311  					break
  1312  				}
  1313  				it.Release()
  1314  
  1315  				endTime := primaryValidator.EndTime
  1316  				builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
  1317  				utx, err := builder.NewAddSubnetValidatorTx(
  1318  					&txs.SubnetValidator{
  1319  						Validator: txs.Validator{
  1320  							NodeID: primaryValidator.NodeID,
  1321  							Start:  0,
  1322  							End:    uint64(endTime.Unix()),
  1323  							Wght:   defaultWeight,
  1324  						},
  1325  						Subnet: testSubnet1.ID(),
  1326  					},
  1327  				)
  1328  				require.NoError(t, err)
  1329  				subnetValTx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1330  				require.NoError(t, err)
  1331  
  1332  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1333  				require.NoError(t, err)
  1334  
  1335  				require.NoError(t, subnetValTx.Unsigned.Visit(&StandardTxExecutor{
  1336  					Backend: &env.backend,
  1337  					State:   onAcceptState,
  1338  					Tx:      subnetValTx,
  1339  				}))
  1340  
  1341  				builder, signer = env.factory.NewWallet(preFundedKeys...)
  1342  				utx2, err := builder.NewRemoveSubnetValidatorTx(
  1343  					primaryValidator.NodeID,
  1344  					testSubnet1.ID(),
  1345  					common.WithMemo(memoField),
  1346  				)
  1347  				require.NoError(t, err)
  1348  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx2)
  1349  				require.NoError(t, err)
  1350  
  1351  				return tx, onAcceptState
  1352  			},
  1353  		},
  1354  		{
  1355  			name: "TransformSubnetTx",
  1356  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1357  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1358  				utx, err := builder.NewTransformSubnetTx(
  1359  					testSubnet1.TxID,          // subnetID
  1360  					ids.GenerateTestID(),      // assetID
  1361  					10,                        // initial supply
  1362  					10,                        // max supply
  1363  					0,                         // min consumption rate
  1364  					reward.PercentDenominator, // max consumption rate
  1365  					2,                         // min validator stake
  1366  					10,                        // max validator stake
  1367  					time.Minute,               // min stake duration
  1368  					time.Hour,                 // max stake duration
  1369  					1,                         // min delegation fees
  1370  					10,                        // min delegator stake
  1371  					1,                         // max validator weight factor
  1372  					80,                        // uptime requirement
  1373  					common.WithMemo(memoField),
  1374  				)
  1375  				require.NoError(t, err)
  1376  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1377  				require.NoError(t, err)
  1378  
  1379  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1380  				require.NoError(t, err)
  1381  
  1382  				return tx, onAcceptState
  1383  			},
  1384  		},
  1385  		{
  1386  			name: "AddPermissionlessValidatorTx",
  1387  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1388  				var (
  1389  					nodeID    = ids.GenerateTestNodeID()
  1390  					chainTime = env.state.GetTimestamp()
  1391  					endTime   = chainTime.Add(defaultMaxStakingDuration)
  1392  				)
  1393  				sk, err := bls.NewSecretKey()
  1394  				require.NoError(t, err)
  1395  
  1396  				builder, txSigner := env.factory.NewWallet(preFundedKeys...)
  1397  				utx, err := builder.NewAddPermissionlessValidatorTx(
  1398  					&txs.SubnetValidator{
  1399  						Validator: txs.Validator{
  1400  							NodeID: nodeID,
  1401  							Start:  0,
  1402  							End:    uint64(endTime.Unix()),
  1403  							Wght:   env.config.MinValidatorStake,
  1404  						},
  1405  						Subnet: constants.PrimaryNetworkID,
  1406  					},
  1407  					signer.NewProofOfPossession(sk),
  1408  					env.ctx.AVAXAssetID,
  1409  					&secp256k1fx.OutputOwners{
  1410  						Threshold: 1,
  1411  						Addrs:     []ids.ShortID{ids.ShortEmpty},
  1412  					},
  1413  					&secp256k1fx.OutputOwners{
  1414  						Threshold: 1,
  1415  						Addrs:     []ids.ShortID{ids.ShortEmpty},
  1416  					},
  1417  					reward.PercentDenominator,
  1418  					common.WithMemo(memoField),
  1419  				)
  1420  				require.NoError(t, err)
  1421  				tx, err := walletsigner.SignUnsigned(context.Background(), txSigner, utx)
  1422  				require.NoError(t, err)
  1423  
  1424  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1425  				require.NoError(t, err)
  1426  
  1427  				return tx, onAcceptState
  1428  			},
  1429  		},
  1430  		{
  1431  			name: "AddPermissionlessDelegatorTx",
  1432  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1433  				var primaryValidator *state.Staker
  1434  				it, err := env.state.GetCurrentStakerIterator()
  1435  				require.NoError(t, err)
  1436  				for it.Next() {
  1437  					staker := it.Value()
  1438  					if staker.Priority != txs.PrimaryNetworkValidatorCurrentPriority {
  1439  						continue
  1440  					}
  1441  					primaryValidator = staker
  1442  					break
  1443  				}
  1444  				it.Release()
  1445  
  1446  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1447  				utx, err := builder.NewAddPermissionlessDelegatorTx(
  1448  					&txs.SubnetValidator{
  1449  						Validator: txs.Validator{
  1450  							NodeID: primaryValidator.NodeID,
  1451  							Start:  0,
  1452  							End:    uint64(primaryValidator.EndTime.Unix()),
  1453  							Wght:   defaultMinValidatorStake,
  1454  						},
  1455  						Subnet: constants.PrimaryNetworkID,
  1456  					},
  1457  					env.ctx.AVAXAssetID,
  1458  					&secp256k1fx.OutputOwners{
  1459  						Threshold: 1,
  1460  						Addrs:     []ids.ShortID{ids.ShortEmpty},
  1461  					},
  1462  					common.WithMemo(memoField),
  1463  				)
  1464  				require.NoError(t, err)
  1465  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1466  				require.NoError(t, err)
  1467  
  1468  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1469  				require.NoError(t, err)
  1470  
  1471  				return tx, onAcceptState
  1472  			},
  1473  		},
  1474  		{
  1475  			name: "TransferSubnetOwnershipTx",
  1476  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1477  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1478  				utx, err := builder.NewTransferSubnetOwnershipTx(
  1479  					testSubnet1.TxID,
  1480  					&secp256k1fx.OutputOwners{
  1481  						Threshold: 1,
  1482  						Addrs:     []ids.ShortID{ids.ShortEmpty},
  1483  					},
  1484  					common.WithMemo(memoField),
  1485  				)
  1486  				require.NoError(t, err)
  1487  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1488  				require.NoError(t, err)
  1489  
  1490  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1491  				require.NoError(t, err)
  1492  
  1493  				return tx, onAcceptState
  1494  			},
  1495  		},
  1496  		{
  1497  			name: "BaseTx",
  1498  			setupTest: func(env *environment, memoField []byte) (*txs.Tx, state.Diff) {
  1499  				builder, signer := env.factory.NewWallet(preFundedKeys...)
  1500  				utx, err := builder.NewBaseTx(
  1501  					[]*avax.TransferableOutput{
  1502  						{
  1503  							Asset: avax.Asset{ID: env.ctx.AVAXAssetID},
  1504  							Out: &secp256k1fx.TransferOutput{
  1505  								Amt: 1,
  1506  								OutputOwners: secp256k1fx.OutputOwners{
  1507  									Threshold: 1,
  1508  									Addrs:     []ids.ShortID{ids.ShortEmpty},
  1509  								},
  1510  							},
  1511  						},
  1512  					},
  1513  					common.WithMemo(memoField),
  1514  				)
  1515  				require.NoError(t, err)
  1516  				tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
  1517  				require.NoError(t, err)
  1518  
  1519  				onAcceptState, err := state.NewDiff(env.state.GetLastAccepted(), env)
  1520  				require.NoError(t, err)
  1521  
  1522  				return tx, onAcceptState
  1523  			},
  1524  		},
  1525  	}
  1526  
  1527  	for _, tt := range tests {
  1528  		t.Run(tt.name, func(t *testing.T) {
  1529  			require := require.New(t)
  1530  
  1531  			env := newEnvironment(t, durango)
  1532  			env.ctx.Lock.Lock()
  1533  			defer env.ctx.Lock.Unlock()
  1534  
  1535  			// Populated memo field should error
  1536  			tx, onAcceptState := tt.setupTest(env, []byte{'m', 'e', 'm', 'o'})
  1537  			err := tx.Unsigned.Visit(&StandardTxExecutor{
  1538  				Backend: &env.backend,
  1539  				State:   onAcceptState,
  1540  				Tx:      tx,
  1541  			})
  1542  			require.ErrorIs(err, avax.ErrMemoTooLarge)
  1543  
  1544  			// Empty memo field should not error
  1545  			tx, onAcceptState = tt.setupTest(env, []byte{})
  1546  			require.NoError(tx.Unsigned.Visit(&StandardTxExecutor{
  1547  				Backend: &env.backend,
  1548  				State:   onAcceptState,
  1549  				Tx:      tx,
  1550  			}))
  1551  		})
  1552  	}
  1553  }
  1554  
  1555  // Returns a RemoveSubnetValidatorTx that passes syntactic verification.
  1556  // Memo field is empty as required post Durango activation
  1557  func newRemoveSubnetValidatorTx(t *testing.T) (*txs.RemoveSubnetValidatorTx, *txs.Tx) {
  1558  	t.Helper()
  1559  
  1560  	creds := []verify.Verifiable{
  1561  		&secp256k1fx.Credential{
  1562  			Sigs: make([][65]byte, 1),
  1563  		},
  1564  		&secp256k1fx.Credential{
  1565  			Sigs: make([][65]byte, 1),
  1566  		},
  1567  	}
  1568  	unsignedTx := &txs.RemoveSubnetValidatorTx{
  1569  		BaseTx: txs.BaseTx{
  1570  			BaseTx: avax.BaseTx{
  1571  				Ins: []*avax.TransferableInput{{
  1572  					UTXOID: avax.UTXOID{
  1573  						TxID: ids.GenerateTestID(),
  1574  					},
  1575  					Asset: avax.Asset{
  1576  						ID: ids.GenerateTestID(),
  1577  					},
  1578  					In: &secp256k1fx.TransferInput{
  1579  						Amt: 1,
  1580  						Input: secp256k1fx.Input{
  1581  							SigIndices: []uint32{0, 1},
  1582  						},
  1583  					},
  1584  				}},
  1585  				Outs: []*avax.TransferableOutput{
  1586  					{
  1587  						Asset: avax.Asset{
  1588  							ID: ids.GenerateTestID(),
  1589  						},
  1590  						Out: &secp256k1fx.TransferOutput{
  1591  							Amt: 1,
  1592  							OutputOwners: secp256k1fx.OutputOwners{
  1593  								Threshold: 1,
  1594  								Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
  1595  							},
  1596  						},
  1597  					},
  1598  				},
  1599  			},
  1600  		},
  1601  		Subnet: ids.GenerateTestID(),
  1602  		NodeID: ids.GenerateTestNodeID(),
  1603  		SubnetAuth: &secp256k1fx.Credential{
  1604  			Sigs: make([][65]byte, 1),
  1605  		},
  1606  	}
  1607  	tx := &txs.Tx{
  1608  		Unsigned: unsignedTx,
  1609  		Creds:    creds,
  1610  	}
  1611  	require.NoError(t, tx.Initialize(txs.Codec))
  1612  	return unsignedTx, tx
  1613  }
  1614  
  1615  // mock implementations that can be used in tests
  1616  // for verifying RemoveSubnetValidatorTx.
  1617  type removeSubnetValidatorTxVerifyEnv struct {
  1618  	latestForkTime time.Time
  1619  	fx             *fx.MockFx
  1620  	flowChecker    *utxo.MockVerifier
  1621  	unsignedTx     *txs.RemoveSubnetValidatorTx
  1622  	tx             *txs.Tx
  1623  	state          *state.MockDiff
  1624  	staker         *state.Staker
  1625  }
  1626  
  1627  // Returns mock implementations that can be used in tests
  1628  // for verifying RemoveSubnetValidatorTx.
  1629  func newValidRemoveSubnetValidatorTxVerifyEnv(t *testing.T, ctrl *gomock.Controller) removeSubnetValidatorTxVerifyEnv {
  1630  	t.Helper()
  1631  
  1632  	now := time.Now()
  1633  	mockFx := fx.NewMockFx(ctrl)
  1634  	mockFlowChecker := utxo.NewMockVerifier(ctrl)
  1635  	unsignedTx, tx := newRemoveSubnetValidatorTx(t)
  1636  	mockState := state.NewMockDiff(ctrl)
  1637  	return removeSubnetValidatorTxVerifyEnv{
  1638  		latestForkTime: now,
  1639  		fx:             mockFx,
  1640  		flowChecker:    mockFlowChecker,
  1641  		unsignedTx:     unsignedTx,
  1642  		tx:             tx,
  1643  		state:          mockState,
  1644  		staker: &state.Staker{
  1645  			TxID:     ids.GenerateTestID(),
  1646  			NodeID:   ids.GenerateTestNodeID(),
  1647  			Priority: txs.SubnetPermissionedValidatorCurrentPriority,
  1648  		},
  1649  	}
  1650  }
  1651  
  1652  func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) {
  1653  	type test struct {
  1654  		name        string
  1655  		newExecutor func(*gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor)
  1656  		expectedErr error
  1657  	}
  1658  
  1659  	tests := []test{
  1660  		{
  1661  			name: "valid tx",
  1662  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1663  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1664  
  1665  				// Set dependency expectations.
  1666  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1667  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil).Times(1)
  1668  				subnetOwner := fx.NewMockOwner(ctrl)
  1669  				env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1)
  1670  				env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1)
  1671  				env.flowChecker.EXPECT().VerifySpend(
  1672  					env.unsignedTx, env.state, env.unsignedTx.Ins, env.unsignedTx.Outs, env.tx.Creds[:len(env.tx.Creds)-1], gomock.Any(),
  1673  				).Return(nil).Times(1)
  1674  				env.state.EXPECT().DeleteCurrentValidator(env.staker)
  1675  				env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins))
  1676  				env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs))
  1677  				e := &StandardTxExecutor{
  1678  					Backend: &Backend{
  1679  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1680  						Bootstrapped: &utils.Atomic[bool]{},
  1681  						Fx:           env.fx,
  1682  						FlowChecker:  env.flowChecker,
  1683  						Ctx:          &snow.Context{},
  1684  					},
  1685  					Tx:    env.tx,
  1686  					State: env.state,
  1687  				}
  1688  				e.Bootstrapped.Set(true)
  1689  				return env.unsignedTx, e
  1690  			},
  1691  			expectedErr: nil,
  1692  		},
  1693  		{
  1694  			name: "tx fails syntactic verification",
  1695  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1696  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1697  				// Setting the subnet ID to the Primary Network ID makes the tx fail syntactic verification
  1698  				env.tx.Unsigned.(*txs.RemoveSubnetValidatorTx).Subnet = constants.PrimaryNetworkID
  1699  				env.state = state.NewMockDiff(ctrl)
  1700  				e := &StandardTxExecutor{
  1701  					Backend: &Backend{
  1702  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1703  						Bootstrapped: &utils.Atomic[bool]{},
  1704  						Fx:           env.fx,
  1705  						FlowChecker:  env.flowChecker,
  1706  						Ctx:          &snow.Context{},
  1707  					},
  1708  					Tx:    env.tx,
  1709  					State: env.state,
  1710  				}
  1711  				e.Bootstrapped.Set(true)
  1712  				return env.unsignedTx, e
  1713  			},
  1714  			expectedErr: txs.ErrRemovePrimaryNetworkValidator,
  1715  		},
  1716  		{
  1717  			name: "node isn't a validator of the subnet",
  1718  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1719  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1720  				env.state = state.NewMockDiff(ctrl)
  1721  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1722  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound)
  1723  				env.state.EXPECT().GetPendingValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound)
  1724  				e := &StandardTxExecutor{
  1725  					Backend: &Backend{
  1726  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1727  						Bootstrapped: &utils.Atomic[bool]{},
  1728  						Fx:           env.fx,
  1729  						FlowChecker:  env.flowChecker,
  1730  						Ctx:          &snow.Context{},
  1731  					},
  1732  					Tx:    env.tx,
  1733  					State: env.state,
  1734  				}
  1735  				e.Bootstrapped.Set(true)
  1736  				return env.unsignedTx, e
  1737  			},
  1738  			expectedErr: ErrNotValidator,
  1739  		},
  1740  		{
  1741  			name: "validator is permissionless",
  1742  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1743  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1744  
  1745  				staker := *env.staker
  1746  				staker.Priority = txs.SubnetPermissionlessValidatorCurrentPriority
  1747  
  1748  				// Set dependency expectations.
  1749  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1750  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(&staker, nil).Times(1)
  1751  				e := &StandardTxExecutor{
  1752  					Backend: &Backend{
  1753  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1754  						Bootstrapped: &utils.Atomic[bool]{},
  1755  						Fx:           env.fx,
  1756  						FlowChecker:  env.flowChecker,
  1757  						Ctx:          &snow.Context{},
  1758  					},
  1759  					Tx:    env.tx,
  1760  					State: env.state,
  1761  				}
  1762  				e.Bootstrapped.Set(true)
  1763  				return env.unsignedTx, e
  1764  			},
  1765  			expectedErr: ErrRemovePermissionlessValidator,
  1766  		},
  1767  		{
  1768  			name: "tx has no credentials",
  1769  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1770  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1771  				// Remove credentials
  1772  				env.tx.Creds = nil
  1773  				env.state = state.NewMockDiff(ctrl)
  1774  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1775  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil)
  1776  				e := &StandardTxExecutor{
  1777  					Backend: &Backend{
  1778  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1779  						Bootstrapped: &utils.Atomic[bool]{},
  1780  						Fx:           env.fx,
  1781  						FlowChecker:  env.flowChecker,
  1782  						Ctx:          &snow.Context{},
  1783  					},
  1784  					Tx:    env.tx,
  1785  					State: env.state,
  1786  				}
  1787  				e.Bootstrapped.Set(true)
  1788  				return env.unsignedTx, e
  1789  			},
  1790  			expectedErr: errWrongNumberOfCredentials,
  1791  		},
  1792  		{
  1793  			name: "can't find subnet",
  1794  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1795  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1796  				env.state = state.NewMockDiff(ctrl)
  1797  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1798  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil)
  1799  				env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound)
  1800  				e := &StandardTxExecutor{
  1801  					Backend: &Backend{
  1802  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1803  						Bootstrapped: &utils.Atomic[bool]{},
  1804  						Fx:           env.fx,
  1805  						FlowChecker:  env.flowChecker,
  1806  						Ctx:          &snow.Context{},
  1807  					},
  1808  					Tx:    env.tx,
  1809  					State: env.state,
  1810  				}
  1811  				e.Bootstrapped.Set(true)
  1812  				return env.unsignedTx, e
  1813  			},
  1814  			expectedErr: database.ErrNotFound,
  1815  		},
  1816  		{
  1817  			name: "no permission to remove validator",
  1818  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1819  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1820  				env.state = state.NewMockDiff(ctrl)
  1821  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1822  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil)
  1823  				subnetOwner := fx.NewMockOwner(ctrl)
  1824  				env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil)
  1825  				env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(errTest)
  1826  				e := &StandardTxExecutor{
  1827  					Backend: &Backend{
  1828  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1829  						Bootstrapped: &utils.Atomic[bool]{},
  1830  						Fx:           env.fx,
  1831  						FlowChecker:  env.flowChecker,
  1832  						Ctx:          &snow.Context{},
  1833  					},
  1834  					Tx:    env.tx,
  1835  					State: env.state,
  1836  				}
  1837  				e.Bootstrapped.Set(true)
  1838  				return env.unsignedTx, e
  1839  			},
  1840  			expectedErr: errUnauthorizedSubnetModification,
  1841  		},
  1842  		{
  1843  			name: "flow checker failed",
  1844  			newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
  1845  				env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
  1846  				env.state = state.NewMockDiff(ctrl)
  1847  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  1848  				env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil)
  1849  				subnetOwner := fx.NewMockOwner(ctrl)
  1850  				env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil)
  1851  				env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil)
  1852  				env.flowChecker.EXPECT().VerifySpend(
  1853  					gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
  1854  				).Return(errTest)
  1855  				e := &StandardTxExecutor{
  1856  					Backend: &Backend{
  1857  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  1858  						Bootstrapped: &utils.Atomic[bool]{},
  1859  						Fx:           env.fx,
  1860  						FlowChecker:  env.flowChecker,
  1861  						Ctx:          &snow.Context{},
  1862  					},
  1863  					Tx:    env.tx,
  1864  					State: env.state,
  1865  				}
  1866  				e.Bootstrapped.Set(true)
  1867  				return env.unsignedTx, e
  1868  			},
  1869  			expectedErr: ErrFlowCheckFailed,
  1870  		},
  1871  	}
  1872  
  1873  	for _, tt := range tests {
  1874  		t.Run(tt.name, func(t *testing.T) {
  1875  			require := require.New(t)
  1876  			ctrl := gomock.NewController(t)
  1877  
  1878  			unsignedTx, executor := tt.newExecutor(ctrl)
  1879  			err := executor.RemoveSubnetValidatorTx(unsignedTx)
  1880  			require.ErrorIs(err, tt.expectedErr)
  1881  		})
  1882  	}
  1883  }
  1884  
  1885  // Returns a TransformSubnetTx that passes syntactic verification.
  1886  // Memo field is empty as required post Durango activation
  1887  func newTransformSubnetTx(t *testing.T) (*txs.TransformSubnetTx, *txs.Tx) {
  1888  	t.Helper()
  1889  
  1890  	creds := []verify.Verifiable{
  1891  		&secp256k1fx.Credential{
  1892  			Sigs: make([][65]byte, 1),
  1893  		},
  1894  		&secp256k1fx.Credential{
  1895  			Sigs: make([][65]byte, 1),
  1896  		},
  1897  	}
  1898  	unsignedTx := &txs.TransformSubnetTx{
  1899  		BaseTx: txs.BaseTx{
  1900  			BaseTx: avax.BaseTx{
  1901  				Ins: []*avax.TransferableInput{{
  1902  					UTXOID: avax.UTXOID{
  1903  						TxID: ids.GenerateTestID(),
  1904  					},
  1905  					Asset: avax.Asset{
  1906  						ID: ids.GenerateTestID(),
  1907  					},
  1908  					In: &secp256k1fx.TransferInput{
  1909  						Amt: 1,
  1910  						Input: secp256k1fx.Input{
  1911  							SigIndices: []uint32{0, 1},
  1912  						},
  1913  					},
  1914  				}},
  1915  				Outs: []*avax.TransferableOutput{
  1916  					{
  1917  						Asset: avax.Asset{
  1918  							ID: ids.GenerateTestID(),
  1919  						},
  1920  						Out: &secp256k1fx.TransferOutput{
  1921  							Amt: 1,
  1922  							OutputOwners: secp256k1fx.OutputOwners{
  1923  								Threshold: 1,
  1924  								Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
  1925  							},
  1926  						},
  1927  					},
  1928  				},
  1929  			},
  1930  		},
  1931  		Subnet:                   ids.GenerateTestID(),
  1932  		AssetID:                  ids.GenerateTestID(),
  1933  		InitialSupply:            10,
  1934  		MaximumSupply:            10,
  1935  		MinConsumptionRate:       0,
  1936  		MaxConsumptionRate:       reward.PercentDenominator,
  1937  		MinValidatorStake:        2,
  1938  		MaxValidatorStake:        10,
  1939  		MinStakeDuration:         1,
  1940  		MaxStakeDuration:         2,
  1941  		MinDelegationFee:         reward.PercentDenominator,
  1942  		MinDelegatorStake:        1,
  1943  		MaxValidatorWeightFactor: 1,
  1944  		UptimeRequirement:        reward.PercentDenominator,
  1945  		SubnetAuth: &secp256k1fx.Credential{
  1946  			Sigs: make([][65]byte, 1),
  1947  		},
  1948  	}
  1949  	tx := &txs.Tx{
  1950  		Unsigned: unsignedTx,
  1951  		Creds:    creds,
  1952  	}
  1953  	require.NoError(t, tx.Initialize(txs.Codec))
  1954  	return unsignedTx, tx
  1955  }
  1956  
  1957  // mock implementations that can be used in tests
  1958  // for verifying TransformSubnetTx.
  1959  type transformSubnetTxVerifyEnv struct {
  1960  	latestForkTime time.Time
  1961  	fx             *fx.MockFx
  1962  	flowChecker    *utxo.MockVerifier
  1963  	unsignedTx     *txs.TransformSubnetTx
  1964  	tx             *txs.Tx
  1965  	state          *state.MockDiff
  1966  	staker         *state.Staker
  1967  }
  1968  
  1969  // Returns mock implementations that can be used in tests
  1970  // for verifying TransformSubnetTx.
  1971  func newValidTransformSubnetTxVerifyEnv(t *testing.T, ctrl *gomock.Controller) transformSubnetTxVerifyEnv {
  1972  	t.Helper()
  1973  
  1974  	now := time.Now()
  1975  	mockFx := fx.NewMockFx(ctrl)
  1976  	mockFlowChecker := utxo.NewMockVerifier(ctrl)
  1977  	unsignedTx, tx := newTransformSubnetTx(t)
  1978  	mockState := state.NewMockDiff(ctrl)
  1979  	return transformSubnetTxVerifyEnv{
  1980  		latestForkTime: now,
  1981  		fx:             mockFx,
  1982  		flowChecker:    mockFlowChecker,
  1983  		unsignedTx:     unsignedTx,
  1984  		tx:             tx,
  1985  		state:          mockState,
  1986  		staker: &state.Staker{
  1987  			TxID:   ids.GenerateTestID(),
  1988  			NodeID: ids.GenerateTestNodeID(),
  1989  		},
  1990  	}
  1991  }
  1992  
  1993  func TestStandardExecutorTransformSubnetTx(t *testing.T) {
  1994  	type test struct {
  1995  		name        string
  1996  		newExecutor func(*gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor)
  1997  		err         error
  1998  	}
  1999  
  2000  	tests := []test{
  2001  		{
  2002  			name: "tx fails syntactic verification",
  2003  			newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
  2004  				env := newValidTransformSubnetTxVerifyEnv(t, ctrl)
  2005  				// Setting the tx to nil makes the tx fail syntactic verification
  2006  				env.tx.Unsigned = (*txs.TransformSubnetTx)(nil)
  2007  				env.state = state.NewMockDiff(ctrl)
  2008  				e := &StandardTxExecutor{
  2009  					Backend: &Backend{
  2010  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  2011  						Bootstrapped: &utils.Atomic[bool]{},
  2012  						Fx:           env.fx,
  2013  						FlowChecker:  env.flowChecker,
  2014  						Ctx:          &snow.Context{},
  2015  					},
  2016  					Tx:    env.tx,
  2017  					State: env.state,
  2018  				}
  2019  				e.Bootstrapped.Set(true)
  2020  				return env.unsignedTx, e
  2021  			},
  2022  			err: txs.ErrNilTx,
  2023  		},
  2024  		{
  2025  			name: "max stake duration too large",
  2026  			newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
  2027  				env := newValidTransformSubnetTxVerifyEnv(t, ctrl)
  2028  				env.unsignedTx.MaxStakeDuration = math.MaxUint32
  2029  				env.state = state.NewMockDiff(ctrl)
  2030  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  2031  				e := &StandardTxExecutor{
  2032  					Backend: &Backend{
  2033  						Config:       defaultTestConfig(t, durango, env.latestForkTime),
  2034  						Bootstrapped: &utils.Atomic[bool]{},
  2035  						Fx:           env.fx,
  2036  						FlowChecker:  env.flowChecker,
  2037  						Ctx:          &snow.Context{},
  2038  					},
  2039  					Tx:    env.tx,
  2040  					State: env.state,
  2041  				}
  2042  				e.Bootstrapped.Set(true)
  2043  				return env.unsignedTx, e
  2044  			},
  2045  			err: errMaxStakeDurationTooLarge,
  2046  		},
  2047  		{
  2048  			name: "fail subnet authorization",
  2049  			newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
  2050  				env := newValidTransformSubnetTxVerifyEnv(t, ctrl)
  2051  				// Remove credentials
  2052  				env.tx.Creds = nil
  2053  				env.state = state.NewMockDiff(ctrl)
  2054  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  2055  
  2056  				cfg := defaultTestConfig(t, durango, env.latestForkTime)
  2057  				cfg.MaxStakeDuration = math.MaxInt64
  2058  
  2059  				e := &StandardTxExecutor{
  2060  					Backend: &Backend{
  2061  						Config:       cfg,
  2062  						Bootstrapped: &utils.Atomic[bool]{},
  2063  						Fx:           env.fx,
  2064  						FlowChecker:  env.flowChecker,
  2065  						Ctx:          &snow.Context{},
  2066  					},
  2067  					Tx:    env.tx,
  2068  					State: env.state,
  2069  				}
  2070  				e.Bootstrapped.Set(true)
  2071  				return env.unsignedTx, e
  2072  			},
  2073  			err: errWrongNumberOfCredentials,
  2074  		},
  2075  		{
  2076  			name: "flow checker failed",
  2077  			newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
  2078  				env := newValidTransformSubnetTxVerifyEnv(t, ctrl)
  2079  				env.state = state.NewMockDiff(ctrl)
  2080  				subnetOwner := fx.NewMockOwner(ctrl)
  2081  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  2082  				env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil)
  2083  				env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1)
  2084  				env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil)
  2085  				env.flowChecker.EXPECT().VerifySpend(
  2086  					gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
  2087  				).Return(ErrFlowCheckFailed)
  2088  
  2089  				cfg := defaultTestConfig(t, durango, env.latestForkTime)
  2090  				cfg.MaxStakeDuration = math.MaxInt64
  2091  
  2092  				e := &StandardTxExecutor{
  2093  					Backend: &Backend{
  2094  						Config:       cfg,
  2095  						Bootstrapped: &utils.Atomic[bool]{},
  2096  						Fx:           env.fx,
  2097  						FlowChecker:  env.flowChecker,
  2098  						Ctx:          &snow.Context{},
  2099  					},
  2100  					Tx:    env.tx,
  2101  					State: env.state,
  2102  				}
  2103  				e.Bootstrapped.Set(true)
  2104  				return env.unsignedTx, e
  2105  			},
  2106  			err: ErrFlowCheckFailed,
  2107  		},
  2108  		{
  2109  			name: "valid tx",
  2110  			newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
  2111  				env := newValidTransformSubnetTxVerifyEnv(t, ctrl)
  2112  
  2113  				// Set dependency expectations.
  2114  				subnetOwner := fx.NewMockOwner(ctrl)
  2115  				env.state.EXPECT().GetTimestamp().Return(env.latestForkTime)
  2116  				env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1)
  2117  				env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1)
  2118  				env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1)
  2119  				env.flowChecker.EXPECT().VerifySpend(
  2120  					env.unsignedTx, env.state, env.unsignedTx.Ins, env.unsignedTx.Outs, env.tx.Creds[:len(env.tx.Creds)-1], gomock.Any(),
  2121  				).Return(nil).Times(1)
  2122  				env.state.EXPECT().AddSubnetTransformation(env.tx)
  2123  				env.state.EXPECT().SetCurrentSupply(env.unsignedTx.Subnet, env.unsignedTx.InitialSupply)
  2124  				env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins))
  2125  				env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs))
  2126  
  2127  				cfg := defaultTestConfig(t, durango, env.latestForkTime)
  2128  				cfg.MaxStakeDuration = math.MaxInt64
  2129  
  2130  				e := &StandardTxExecutor{
  2131  					Backend: &Backend{
  2132  						Config:       cfg,
  2133  						Bootstrapped: &utils.Atomic[bool]{},
  2134  						Fx:           env.fx,
  2135  						FlowChecker:  env.flowChecker,
  2136  						Ctx:          &snow.Context{},
  2137  					},
  2138  					Tx:    env.tx,
  2139  					State: env.state,
  2140  				}
  2141  				e.Bootstrapped.Set(true)
  2142  				return env.unsignedTx, e
  2143  			},
  2144  			err: nil,
  2145  		},
  2146  	}
  2147  
  2148  	for _, tt := range tests {
  2149  		t.Run(tt.name, func(t *testing.T) {
  2150  			ctrl := gomock.NewController(t)
  2151  
  2152  			unsignedTx, executor := tt.newExecutor(ctrl)
  2153  			err := executor.TransformSubnetTx(unsignedTx)
  2154  			require.ErrorIs(t, err, tt.err)
  2155  		})
  2156  	}
  2157  }
  2158  
  2159  func defaultTestConfig(t *testing.T, f fork, tm time.Time) *config.Config {
  2160  	c := &config.Config{
  2161  		UpgradeConfig: upgrade.Config{
  2162  			ApricotPhase3Time: mockable.MaxTime,
  2163  			ApricotPhase5Time: mockable.MaxTime,
  2164  			BanffTime:         mockable.MaxTime,
  2165  			CortinaTime:       mockable.MaxTime,
  2166  			DurangoTime:       mockable.MaxTime,
  2167  			EUpgradeTime:      mockable.MaxTime,
  2168  		},
  2169  	}
  2170  
  2171  	switch f {
  2172  	case eUpgrade:
  2173  		c.UpgradeConfig.EUpgradeTime = tm
  2174  		fallthrough
  2175  	case durango:
  2176  		c.UpgradeConfig.DurangoTime = tm
  2177  		fallthrough
  2178  	case cortina:
  2179  		c.UpgradeConfig.CortinaTime = tm
  2180  		fallthrough
  2181  	case banff:
  2182  		c.UpgradeConfig.BanffTime = tm
  2183  		fallthrough
  2184  	case apricotPhase5:
  2185  		c.UpgradeConfig.ApricotPhase5Time = tm
  2186  		fallthrough
  2187  	case apricotPhase3:
  2188  		c.UpgradeConfig.ApricotPhase3Time = tm
  2189  	default:
  2190  		require.FailNow(t, "unhandled fork", f)
  2191  	}
  2192  
  2193  	return c
  2194  }