github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/add_permissionless_delegator_tx.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package txs
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/MetalBlockchain/metalgo/ids"
    10  	"github.com/MetalBlockchain/metalgo/snow"
    11  	"github.com/MetalBlockchain/metalgo/utils/constants"
    12  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    13  	"github.com/MetalBlockchain/metalgo/utils/math"
    14  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    15  	"github.com/MetalBlockchain/metalgo/vms/components/verify"
    16  	"github.com/MetalBlockchain/metalgo/vms/platformvm/fx"
    17  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    18  )
    19  
    20  var (
    21  	_ DelegatorTx     = (*AddPermissionlessDelegatorTx)(nil)
    22  	_ ScheduledStaker = (*AddPermissionlessDelegatorTx)(nil)
    23  )
    24  
    25  // AddPermissionlessDelegatorTx is an unsigned addPermissionlessDelegatorTx
    26  type AddPermissionlessDelegatorTx struct {
    27  	// Metadata, inputs and outputs
    28  	BaseTx `serialize:"true"`
    29  	// Describes the validator
    30  	Validator `serialize:"true" json:"validator"`
    31  	// ID of the subnet this validator is validating
    32  	Subnet ids.ID `serialize:"true" json:"subnetID"`
    33  	// Where to send staked tokens when done validating
    34  	StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"`
    35  	// Where to send staking rewards when done validating
    36  	DelegationRewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"`
    37  }
    38  
    39  // InitCtx sets the FxID fields in the inputs and outputs of this
    40  // [AddPermissionlessDelegatorTx]. Also sets the [ctx] to the given [vm.ctx] so
    41  // that the addresses can be json marshalled into human readable format
    42  func (tx *AddPermissionlessDelegatorTx) InitCtx(ctx *snow.Context) {
    43  	tx.BaseTx.InitCtx(ctx)
    44  	for _, out := range tx.StakeOuts {
    45  		out.FxID = secp256k1fx.ID
    46  		out.InitCtx(ctx)
    47  	}
    48  	tx.DelegationRewardsOwner.InitCtx(ctx)
    49  }
    50  
    51  func (tx *AddPermissionlessDelegatorTx) SubnetID() ids.ID {
    52  	return tx.Subnet
    53  }
    54  
    55  func (tx *AddPermissionlessDelegatorTx) NodeID() ids.NodeID {
    56  	return tx.Validator.NodeID
    57  }
    58  
    59  func (*AddPermissionlessDelegatorTx) PublicKey() (*bls.PublicKey, bool, error) {
    60  	return nil, false, nil
    61  }
    62  
    63  func (tx *AddPermissionlessDelegatorTx) PendingPriority() Priority {
    64  	if tx.Subnet == constants.PrimaryNetworkID {
    65  		return PrimaryNetworkDelegatorBanffPendingPriority
    66  	}
    67  	return SubnetPermissionlessDelegatorPendingPriority
    68  }
    69  
    70  func (tx *AddPermissionlessDelegatorTx) CurrentPriority() Priority {
    71  	if tx.Subnet == constants.PrimaryNetworkID {
    72  		return PrimaryNetworkDelegatorCurrentPriority
    73  	}
    74  	return SubnetPermissionlessDelegatorCurrentPriority
    75  }
    76  
    77  func (tx *AddPermissionlessDelegatorTx) Stake() []*avax.TransferableOutput {
    78  	return tx.StakeOuts
    79  }
    80  
    81  func (tx *AddPermissionlessDelegatorTx) RewardsOwner() fx.Owner {
    82  	return tx.DelegationRewardsOwner
    83  }
    84  
    85  // SyntacticVerify returns nil iff [tx] is valid
    86  func (tx *AddPermissionlessDelegatorTx) SyntacticVerify(ctx *snow.Context) error {
    87  	switch {
    88  	case tx == nil:
    89  		return ErrNilTx
    90  	case tx.SyntacticallyVerified: // already passed syntactic verification
    91  		return nil
    92  	case len(tx.StakeOuts) == 0: // Ensure there is provided stake
    93  		return errNoStake
    94  	}
    95  
    96  	if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
    97  		return fmt.Errorf("failed to verify BaseTx: %w", err)
    98  	}
    99  	if err := verify.All(&tx.Validator, tx.DelegationRewardsOwner); err != nil {
   100  		return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
   101  	}
   102  
   103  	for _, out := range tx.StakeOuts {
   104  		if err := out.Verify(); err != nil {
   105  			return fmt.Errorf("failed to verify output: %w", err)
   106  		}
   107  	}
   108  
   109  	firstStakeOutput := tx.StakeOuts[0]
   110  	stakedAssetID := firstStakeOutput.AssetID()
   111  	totalStakeWeight := firstStakeOutput.Output().Amount()
   112  	for _, out := range tx.StakeOuts[1:] {
   113  		newWeight, err := math.Add64(totalStakeWeight, out.Output().Amount())
   114  		if err != nil {
   115  			return err
   116  		}
   117  		totalStakeWeight = newWeight
   118  
   119  		assetID := out.AssetID()
   120  		if assetID != stakedAssetID {
   121  			return fmt.Errorf("%w: %q and %q", errMultipleStakedAssets, stakedAssetID, assetID)
   122  		}
   123  	}
   124  
   125  	switch {
   126  	case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec):
   127  		return errOutputsNotSorted
   128  	case totalStakeWeight != tx.Wght:
   129  		return fmt.Errorf("%w, delegator weight %d total stake weight %d",
   130  			errDelegatorWeightMismatch,
   131  			tx.Wght,
   132  			totalStakeWeight,
   133  		)
   134  	}
   135  
   136  	// cache that this is valid
   137  	tx.SyntacticallyVerified = true
   138  	return nil
   139  }
   140  
   141  func (tx *AddPermissionlessDelegatorTx) Visit(visitor Visitor) error {
   142  	return visitor.AddPermissionlessDelegatorTx(tx)
   143  }