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