github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/executor/standard_tx_executor.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  	"fmt"
    10  	"time"
    11  
    12  	"go.uber.org/zap"
    13  
    14  	"github.com/ava-labs/avalanchego/chains/atomic"
    15  	"github.com/ava-labs/avalanchego/ids"
    16  	"github.com/ava-labs/avalanchego/utils/constants"
    17  	"github.com/ava-labs/avalanchego/utils/set"
    18  	"github.com/ava-labs/avalanchego/vms/components/avax"
    19  	"github.com/ava-labs/avalanchego/vms/components/verify"
    20  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    21  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    22  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/fee"
    23  )
    24  
    25  var (
    26  	_ txs.Visitor = (*StandardTxExecutor)(nil)
    27  
    28  	errEmptyNodeID                = errors.New("validator nodeID cannot be empty")
    29  	errMaxStakeDurationTooLarge   = errors.New("max stake duration must be less than or equal to the global max stake duration")
    30  	errMissingStartTimePreDurango = errors.New("staker transactions must have a StartTime pre-Durango")
    31  	errTransformSubnetTxPostEtna  = errors.New("TransformSubnetTx is not permitted post-Etna")
    32  )
    33  
    34  type StandardTxExecutor struct {
    35  	// inputs, to be filled before visitor methods are called
    36  	*Backend
    37  	State         state.Diff // state is expected to be modified
    38  	FeeCalculator fee.Calculator
    39  	Tx            *txs.Tx
    40  
    41  	// outputs of visitor execution
    42  	OnAccept       func() // may be nil
    43  	Inputs         set.Set[ids.ID]
    44  	AtomicRequests map[ids.ID]*atomic.Requests // may be nil
    45  }
    46  
    47  func (*StandardTxExecutor) AdvanceTimeTx(*txs.AdvanceTimeTx) error {
    48  	return ErrWrongTxType
    49  }
    50  
    51  func (*StandardTxExecutor) RewardValidatorTx(*txs.RewardValidatorTx) error {
    52  	return ErrWrongTxType
    53  }
    54  
    55  func (e *StandardTxExecutor) CreateChainTx(tx *txs.CreateChainTx) error {
    56  	if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
    57  		return err
    58  	}
    59  
    60  	var (
    61  		currentTimestamp = e.State.GetTimestamp()
    62  		isDurangoActive  = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp)
    63  	)
    64  	if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil {
    65  		return err
    66  	}
    67  
    68  	baseTxCreds, err := verifyPoASubnetAuthorization(e.Backend, e.State, e.Tx, tx.SubnetID, tx.SubnetAuth)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	// Verify the flowcheck
    74  	fee, err := e.FeeCalculator.CalculateFee(tx)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	if err := e.FlowChecker.VerifySpend(
    79  		tx,
    80  		e.State,
    81  		tx.Ins,
    82  		tx.Outs,
    83  		baseTxCreds,
    84  		map[ids.ID]uint64{
    85  			e.Ctx.AVAXAssetID: fee,
    86  		},
    87  	); err != nil {
    88  		return err
    89  	}
    90  
    91  	txID := e.Tx.ID()
    92  
    93  	// Consume the UTXOS
    94  	avax.Consume(e.State, tx.Ins)
    95  	// Produce the UTXOS
    96  	avax.Produce(e.State, txID, tx.Outs)
    97  	// Add the new chain to the database
    98  	e.State.AddChain(e.Tx)
    99  
   100  	// If this proposal is committed and this node is a member of the subnet
   101  	// that validates the blockchain, create the blockchain
   102  	e.OnAccept = func() {
   103  		e.Config.CreateChain(txID, tx)
   104  	}
   105  	return nil
   106  }
   107  
   108  func (e *StandardTxExecutor) CreateSubnetTx(tx *txs.CreateSubnetTx) error {
   109  	// Make sure this transaction is well formed.
   110  	if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
   111  		return err
   112  	}
   113  
   114  	var (
   115  		currentTimestamp = e.State.GetTimestamp()
   116  		isDurangoActive  = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp)
   117  	)
   118  	if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil {
   119  		return err
   120  	}
   121  
   122  	// Verify the flowcheck
   123  	fee, err := e.FeeCalculator.CalculateFee(tx)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	if err := e.FlowChecker.VerifySpend(
   128  		tx,
   129  		e.State,
   130  		tx.Ins,
   131  		tx.Outs,
   132  		e.Tx.Creds,
   133  		map[ids.ID]uint64{
   134  			e.Ctx.AVAXAssetID: fee,
   135  		},
   136  	); err != nil {
   137  		return err
   138  	}
   139  
   140  	txID := e.Tx.ID()
   141  
   142  	// Consume the UTXOS
   143  	avax.Consume(e.State, tx.Ins)
   144  	// Produce the UTXOS
   145  	avax.Produce(e.State, txID, tx.Outs)
   146  	// Add the new subnet to the database
   147  	e.State.AddSubnet(txID)
   148  	e.State.SetSubnetOwner(txID, tx.Owner)
   149  	return nil
   150  }
   151  
   152  func (e *StandardTxExecutor) ImportTx(tx *txs.ImportTx) error {
   153  	if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
   154  		return err
   155  	}
   156  
   157  	var (
   158  		currentTimestamp = e.State.GetTimestamp()
   159  		isDurangoActive  = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp)
   160  	)
   161  	if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil {
   162  		return err
   163  	}
   164  
   165  	e.Inputs = set.NewSet[ids.ID](len(tx.ImportedInputs))
   166  	utxoIDs := make([][]byte, len(tx.ImportedInputs))
   167  	for i, in := range tx.ImportedInputs {
   168  		utxoID := in.UTXOID.InputID()
   169  
   170  		e.Inputs.Add(utxoID)
   171  		utxoIDs[i] = utxoID[:]
   172  	}
   173  
   174  	// Skip verification of the shared memory inputs if the other primary
   175  	// network chains are not guaranteed to be up-to-date.
   176  	if e.Bootstrapped.Get() && !e.Config.PartialSyncPrimaryNetwork {
   177  		if err := verify.SameSubnet(context.TODO(), e.Ctx, tx.SourceChain); err != nil {
   178  			return err
   179  		}
   180  
   181  		allUTXOBytes, err := e.Ctx.SharedMemory.Get(tx.SourceChain, utxoIDs)
   182  		if err != nil {
   183  			return fmt.Errorf("failed to get shared memory: %w", err)
   184  		}
   185  
   186  		utxos := make([]*avax.UTXO, len(tx.Ins)+len(tx.ImportedInputs))
   187  		for index, input := range tx.Ins {
   188  			utxo, err := e.State.GetUTXO(input.InputID())
   189  			if err != nil {
   190  				return fmt.Errorf("failed to get UTXO %s: %w", &input.UTXOID, err)
   191  			}
   192  			utxos[index] = utxo
   193  		}
   194  		for i, utxoBytes := range allUTXOBytes {
   195  			utxo := &avax.UTXO{}
   196  			if _, err := txs.Codec.Unmarshal(utxoBytes, utxo); err != nil {
   197  				return fmt.Errorf("failed to unmarshal UTXO: %w", err)
   198  			}
   199  			utxos[i+len(tx.Ins)] = utxo
   200  		}
   201  
   202  		ins := make([]*avax.TransferableInput, len(tx.Ins)+len(tx.ImportedInputs))
   203  		copy(ins, tx.Ins)
   204  		copy(ins[len(tx.Ins):], tx.ImportedInputs)
   205  
   206  		// Verify the flowcheck
   207  		fee, err := e.FeeCalculator.CalculateFee(tx)
   208  		if err != nil {
   209  			return err
   210  		}
   211  		if err := e.FlowChecker.VerifySpendUTXOs(
   212  			tx,
   213  			utxos,
   214  			ins,
   215  			tx.Outs,
   216  			e.Tx.Creds,
   217  			map[ids.ID]uint64{
   218  				e.Ctx.AVAXAssetID: fee,
   219  			},
   220  		); err != nil {
   221  			return err
   222  		}
   223  	}
   224  
   225  	txID := e.Tx.ID()
   226  
   227  	// Consume the UTXOS
   228  	avax.Consume(e.State, tx.Ins)
   229  	// Produce the UTXOS
   230  	avax.Produce(e.State, txID, tx.Outs)
   231  
   232  	// Note: We apply atomic requests even if we are not verifying atomic
   233  	// requests to ensure the shared state will be correct if we later start
   234  	// verifying the requests.
   235  	e.AtomicRequests = map[ids.ID]*atomic.Requests{
   236  		tx.SourceChain: {
   237  			RemoveRequests: utxoIDs,
   238  		},
   239  	}
   240  	return nil
   241  }
   242  
   243  func (e *StandardTxExecutor) ExportTx(tx *txs.ExportTx) error {
   244  	if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
   245  		return err
   246  	}
   247  
   248  	var (
   249  		currentTimestamp = e.State.GetTimestamp()
   250  		isDurangoActive  = e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp)
   251  	)
   252  	if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil {
   253  		return err
   254  	}
   255  
   256  	outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.ExportedOutputs))
   257  	copy(outs, tx.Outs)
   258  	copy(outs[len(tx.Outs):], tx.ExportedOutputs)
   259  
   260  	if e.Bootstrapped.Get() {
   261  		if err := verify.SameSubnet(context.TODO(), e.Ctx, tx.DestinationChain); err != nil {
   262  			return err
   263  		}
   264  	}
   265  
   266  	// Verify the flowcheck
   267  	fee, err := e.FeeCalculator.CalculateFee(tx)
   268  	if err != nil {
   269  		return err
   270  	}
   271  	if err := e.FlowChecker.VerifySpend(
   272  		tx,
   273  		e.State,
   274  		tx.Ins,
   275  		outs,
   276  		e.Tx.Creds,
   277  		map[ids.ID]uint64{
   278  			e.Ctx.AVAXAssetID: fee,
   279  		},
   280  	); err != nil {
   281  		return fmt.Errorf("failed verifySpend: %w", err)
   282  	}
   283  
   284  	txID := e.Tx.ID()
   285  
   286  	// Consume the UTXOS
   287  	avax.Consume(e.State, tx.Ins)
   288  	// Produce the UTXOS
   289  	avax.Produce(e.State, txID, tx.Outs)
   290  
   291  	// Note: We apply atomic requests even if we are not verifying atomic
   292  	// requests to ensure the shared state will be correct if we later start
   293  	// verifying the requests.
   294  	elems := make([]*atomic.Element, len(tx.ExportedOutputs))
   295  	for i, out := range tx.ExportedOutputs {
   296  		utxo := &avax.UTXO{
   297  			UTXOID: avax.UTXOID{
   298  				TxID:        txID,
   299  				OutputIndex: uint32(len(tx.Outs) + i),
   300  			},
   301  			Asset: avax.Asset{ID: out.AssetID()},
   302  			Out:   out.Out,
   303  		}
   304  
   305  		utxoBytes, err := txs.Codec.Marshal(txs.CodecVersion, utxo)
   306  		if err != nil {
   307  			return fmt.Errorf("failed to marshal UTXO: %w", err)
   308  		}
   309  		utxoID := utxo.InputID()
   310  		elem := &atomic.Element{
   311  			Key:   utxoID[:],
   312  			Value: utxoBytes,
   313  		}
   314  		if out, ok := utxo.Out.(avax.Addressable); ok {
   315  			elem.Traits = out.Addresses()
   316  		}
   317  
   318  		elems[i] = elem
   319  	}
   320  	e.AtomicRequests = map[ids.ID]*atomic.Requests{
   321  		tx.DestinationChain: {
   322  			PutRequests: elems,
   323  		},
   324  	}
   325  	return nil
   326  }
   327  
   328  func (e *StandardTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error {
   329  	if tx.Validator.NodeID == ids.EmptyNodeID {
   330  		return errEmptyNodeID
   331  	}
   332  
   333  	if _, err := verifyAddValidatorTx(
   334  		e.Backend,
   335  		e.FeeCalculator,
   336  		e.State,
   337  		e.Tx,
   338  		tx,
   339  	); err != nil {
   340  		return err
   341  	}
   342  
   343  	if err := e.putStaker(tx); err != nil {
   344  		return err
   345  	}
   346  
   347  	txID := e.Tx.ID()
   348  	avax.Consume(e.State, tx.Ins)
   349  	avax.Produce(e.State, txID, tx.Outs)
   350  
   351  	if e.Config.PartialSyncPrimaryNetwork && tx.Validator.NodeID == e.Ctx.NodeID {
   352  		e.Ctx.Log.Warn("verified transaction that would cause this node to become unhealthy",
   353  			zap.String("reason", "primary network is not being fully synced"),
   354  			zap.Stringer("txID", txID),
   355  			zap.String("txType", "addValidator"),
   356  			zap.Stringer("nodeID", tx.Validator.NodeID),
   357  		)
   358  	}
   359  	return nil
   360  }
   361  
   362  func (e *StandardTxExecutor) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error {
   363  	if err := verifyAddSubnetValidatorTx(
   364  		e.Backend,
   365  		e.FeeCalculator,
   366  		e.State,
   367  		e.Tx,
   368  		tx,
   369  	); err != nil {
   370  		return err
   371  	}
   372  
   373  	if err := e.putStaker(tx); err != nil {
   374  		return err
   375  	}
   376  
   377  	txID := e.Tx.ID()
   378  	avax.Consume(e.State, tx.Ins)
   379  	avax.Produce(e.State, txID, tx.Outs)
   380  	return nil
   381  }
   382  
   383  func (e *StandardTxExecutor) AddDelegatorTx(tx *txs.AddDelegatorTx) error {
   384  	if _, err := verifyAddDelegatorTx(
   385  		e.Backend,
   386  		e.FeeCalculator,
   387  		e.State,
   388  		e.Tx,
   389  		tx,
   390  	); err != nil {
   391  		return err
   392  	}
   393  
   394  	if err := e.putStaker(tx); err != nil {
   395  		return err
   396  	}
   397  
   398  	txID := e.Tx.ID()
   399  	avax.Consume(e.State, tx.Ins)
   400  	avax.Produce(e.State, txID, tx.Outs)
   401  	return nil
   402  }
   403  
   404  // Verifies a [*txs.RemoveSubnetValidatorTx] and, if it passes, executes it on
   405  // [e.State]. For verification rules, see [verifyRemoveSubnetValidatorTx]. This
   406  // transaction will result in [tx.NodeID] being removed as a validator of
   407  // [tx.SubnetID].
   408  // Note: [tx.NodeID] may be either a current or pending validator.
   409  func (e *StandardTxExecutor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error {
   410  	staker, isCurrentValidator, err := verifyRemoveSubnetValidatorTx(
   411  		e.Backend,
   412  		e.FeeCalculator,
   413  		e.State,
   414  		e.Tx,
   415  		tx,
   416  	)
   417  	if err != nil {
   418  		return err
   419  	}
   420  
   421  	if isCurrentValidator {
   422  		e.State.DeleteCurrentValidator(staker)
   423  	} else {
   424  		e.State.DeletePendingValidator(staker)
   425  	}
   426  
   427  	// Invariant: There are no permissioned subnet delegators to remove.
   428  
   429  	txID := e.Tx.ID()
   430  	avax.Consume(e.State, tx.Ins)
   431  	avax.Produce(e.State, txID, tx.Outs)
   432  
   433  	return nil
   434  }
   435  
   436  func (e *StandardTxExecutor) TransformSubnetTx(tx *txs.TransformSubnetTx) error {
   437  	currentTimestamp := e.State.GetTimestamp()
   438  	if e.Config.UpgradeConfig.IsEtnaActivated(currentTimestamp) {
   439  		return errTransformSubnetTxPostEtna
   440  	}
   441  
   442  	if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
   443  		return err
   444  	}
   445  
   446  	isDurangoActive := e.Config.UpgradeConfig.IsDurangoActivated(currentTimestamp)
   447  	if err := avax.VerifyMemoFieldLength(tx.Memo, isDurangoActive); err != nil {
   448  		return err
   449  	}
   450  
   451  	// Note: math.MaxInt32 * time.Second < math.MaxInt64 - so this can never
   452  	// overflow.
   453  	if time.Duration(tx.MaxStakeDuration)*time.Second > e.Backend.Config.MaxStakeDuration {
   454  		return errMaxStakeDurationTooLarge
   455  	}
   456  
   457  	baseTxCreds, err := verifyPoASubnetAuthorization(e.Backend, e.State, e.Tx, tx.Subnet, tx.SubnetAuth)
   458  	if err != nil {
   459  		return err
   460  	}
   461  
   462  	// Verify the flowcheck
   463  	fee, err := e.FeeCalculator.CalculateFee(tx)
   464  	if err != nil {
   465  		return err
   466  	}
   467  	totalRewardAmount := tx.MaximumSupply - tx.InitialSupply
   468  	if err := e.Backend.FlowChecker.VerifySpend(
   469  		tx,
   470  		e.State,
   471  		tx.Ins,
   472  		tx.Outs,
   473  		baseTxCreds,
   474  		// Invariant: [tx.AssetID != e.Ctx.AVAXAssetID]. This prevents the first
   475  		//            entry in this map literal from being overwritten by the
   476  		//            second entry.
   477  		map[ids.ID]uint64{
   478  			e.Ctx.AVAXAssetID: fee,
   479  			tx.AssetID:        totalRewardAmount,
   480  		},
   481  	); err != nil {
   482  		return err
   483  	}
   484  
   485  	txID := e.Tx.ID()
   486  
   487  	// Consume the UTXOS
   488  	avax.Consume(e.State, tx.Ins)
   489  	// Produce the UTXOS
   490  	avax.Produce(e.State, txID, tx.Outs)
   491  	// Transform the new subnet in the database
   492  	e.State.AddSubnetTransformation(e.Tx)
   493  	e.State.SetCurrentSupply(tx.Subnet, tx.InitialSupply)
   494  	return nil
   495  }
   496  
   497  func (e *StandardTxExecutor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error {
   498  	if err := verifyAddPermissionlessValidatorTx(
   499  		e.Backend,
   500  		e.FeeCalculator,
   501  		e.State,
   502  		e.Tx,
   503  		tx,
   504  	); err != nil {
   505  		return err
   506  	}
   507  
   508  	if err := e.putStaker(tx); err != nil {
   509  		return err
   510  	}
   511  
   512  	txID := e.Tx.ID()
   513  	avax.Consume(e.State, tx.Ins)
   514  	avax.Produce(e.State, txID, tx.Outs)
   515  
   516  	if e.Config.PartialSyncPrimaryNetwork &&
   517  		tx.Subnet == constants.PrimaryNetworkID &&
   518  		tx.Validator.NodeID == e.Ctx.NodeID {
   519  		e.Ctx.Log.Warn("verified transaction that would cause this node to become unhealthy",
   520  			zap.String("reason", "primary network is not being fully synced"),
   521  			zap.Stringer("txID", txID),
   522  			zap.String("txType", "addPermissionlessValidator"),
   523  			zap.Stringer("nodeID", tx.Validator.NodeID),
   524  		)
   525  	}
   526  
   527  	return nil
   528  }
   529  
   530  func (e *StandardTxExecutor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error {
   531  	if err := verifyAddPermissionlessDelegatorTx(
   532  		e.Backend,
   533  		e.FeeCalculator,
   534  		e.State,
   535  		e.Tx,
   536  		tx,
   537  	); err != nil {
   538  		return err
   539  	}
   540  
   541  	if err := e.putStaker(tx); err != nil {
   542  		return err
   543  	}
   544  
   545  	txID := e.Tx.ID()
   546  	avax.Consume(e.State, tx.Ins)
   547  	avax.Produce(e.State, txID, tx.Outs)
   548  	return nil
   549  }
   550  
   551  // Verifies a [*txs.TransferSubnetOwnershipTx] and, if it passes, executes it on
   552  // [e.State]. For verification rules, see [verifyTransferSubnetOwnershipTx].
   553  // This transaction will result in the ownership of [tx.Subnet] being transferred
   554  // to [tx.Owner].
   555  func (e *StandardTxExecutor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error {
   556  	err := verifyTransferSubnetOwnershipTx(
   557  		e.Backend,
   558  		e.FeeCalculator,
   559  		e.State,
   560  		e.Tx,
   561  		tx,
   562  	)
   563  	if err != nil {
   564  		return err
   565  	}
   566  
   567  	e.State.SetSubnetOwner(tx.Subnet, tx.Owner)
   568  
   569  	txID := e.Tx.ID()
   570  	avax.Consume(e.State, tx.Ins)
   571  	avax.Produce(e.State, txID, tx.Outs)
   572  	return nil
   573  }
   574  
   575  func (e *StandardTxExecutor) BaseTx(tx *txs.BaseTx) error {
   576  	var (
   577  		currentTimestamp = e.State.GetTimestamp()
   578  		upgrades         = e.Backend.Config.UpgradeConfig
   579  	)
   580  	if !upgrades.IsDurangoActivated(currentTimestamp) {
   581  		return ErrDurangoUpgradeNotActive
   582  	}
   583  
   584  	// Verify the tx is well-formed
   585  	if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
   586  		return err
   587  	}
   588  
   589  	if err := avax.VerifyMemoFieldLength(tx.Memo, true /*=isDurangoActive*/); err != nil {
   590  		return err
   591  	}
   592  
   593  	// Verify the flowcheck
   594  	fee, err := e.FeeCalculator.CalculateFee(tx)
   595  	if err != nil {
   596  		return err
   597  	}
   598  	if err := e.FlowChecker.VerifySpend(
   599  		tx,
   600  		e.State,
   601  		tx.Ins,
   602  		tx.Outs,
   603  		e.Tx.Creds,
   604  		map[ids.ID]uint64{
   605  			e.Ctx.AVAXAssetID: fee,
   606  		},
   607  	); err != nil {
   608  		return err
   609  	}
   610  
   611  	txID := e.Tx.ID()
   612  	// Consume the UTXOS
   613  	avax.Consume(e.State, tx.Ins)
   614  	// Produce the UTXOS
   615  	avax.Produce(e.State, txID, tx.Outs)
   616  	return nil
   617  }
   618  
   619  // Creates the staker as defined in [stakerTx] and adds it to [e.State].
   620  func (e *StandardTxExecutor) putStaker(stakerTx txs.Staker) error {
   621  	var (
   622  		chainTime = e.State.GetTimestamp()
   623  		txID      = e.Tx.ID()
   624  		staker    *state.Staker
   625  		err       error
   626  	)
   627  
   628  	if !e.Config.UpgradeConfig.IsDurangoActivated(chainTime) {
   629  		// Pre-Durango, stakers set a future [StartTime] and are added to the
   630  		// pending staker set. They are promoted to the current staker set once
   631  		// the chain time reaches [StartTime].
   632  		scheduledStakerTx, ok := stakerTx.(txs.ScheduledStaker)
   633  		if !ok {
   634  			return fmt.Errorf("%w: %T", errMissingStartTimePreDurango, stakerTx)
   635  		}
   636  		staker, err = state.NewPendingStaker(txID, scheduledStakerTx)
   637  	} else {
   638  		// Only calculate the potentialReward for permissionless stakers.
   639  		// Recall that we only need to check if this is a permissioned
   640  		// validator as there are no permissioned delegators
   641  		var potentialReward uint64
   642  		if !stakerTx.CurrentPriority().IsPermissionedValidator() {
   643  			subnetID := stakerTx.SubnetID()
   644  			currentSupply, err := e.State.GetCurrentSupply(subnetID)
   645  			if err != nil {
   646  				return err
   647  			}
   648  
   649  			rewards, err := GetRewardsCalculator(e.Backend, e.State, subnetID)
   650  			if err != nil {
   651  				return err
   652  			}
   653  
   654  			// Post-Durango, stakers are immediately added to the current staker
   655  			// set. Their [StartTime] is the current chain time.
   656  			stakeDuration := stakerTx.EndTime().Sub(chainTime)
   657  			potentialReward = rewards.Calculate(
   658  				stakeDuration,
   659  				stakerTx.Weight(),
   660  				currentSupply,
   661  			)
   662  
   663  			e.State.SetCurrentSupply(subnetID, currentSupply+potentialReward)
   664  		}
   665  
   666  		staker, err = state.NewCurrentStaker(txID, stakerTx, chainTime, potentialReward)
   667  	}
   668  	if err != nil {
   669  		return err
   670  	}
   671  
   672  	switch priority := staker.Priority; {
   673  	case priority.IsCurrentValidator():
   674  		if err := e.State.PutCurrentValidator(staker); err != nil {
   675  			return err
   676  		}
   677  	case priority.IsCurrentDelegator():
   678  		e.State.PutCurrentDelegator(staker)
   679  	case priority.IsPendingValidator():
   680  		if err := e.State.PutPendingValidator(staker); err != nil {
   681  			return err
   682  		}
   683  	case priority.IsPendingDelegator():
   684  		e.State.PutPendingDelegator(staker)
   685  	default:
   686  		return fmt.Errorf("staker %s, unexpected priority %d", staker.TxID, priority)
   687  	}
   688  	return nil
   689  }