github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/add_permissionless_validator_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  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/MetalBlockchain/metalgo/ids"
    11  	"github.com/MetalBlockchain/metalgo/snow"
    12  	"github.com/MetalBlockchain/metalgo/utils/constants"
    13  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    14  	"github.com/MetalBlockchain/metalgo/utils/math"
    15  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    16  	"github.com/MetalBlockchain/metalgo/vms/components/verify"
    17  	"github.com/MetalBlockchain/metalgo/vms/platformvm/fx"
    18  	"github.com/MetalBlockchain/metalgo/vms/platformvm/reward"
    19  	"github.com/MetalBlockchain/metalgo/vms/platformvm/signer"
    20  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    21  )
    22  
    23  var (
    24  	_ ValidatorTx     = (*AddPermissionlessValidatorTx)(nil)
    25  	_ ScheduledStaker = (*AddPermissionlessDelegatorTx)(nil)
    26  
    27  	errEmptyNodeID             = errors.New("validator nodeID cannot be empty")
    28  	errNoStake                 = errors.New("no stake")
    29  	errInvalidSigner           = errors.New("invalid signer")
    30  	errMultipleStakedAssets    = errors.New("multiple staked assets")
    31  	errValidatorWeightMismatch = errors.New("validator weight mismatch")
    32  )
    33  
    34  // AddPermissionlessValidatorTx is an unsigned addPermissionlessValidatorTx
    35  type AddPermissionlessValidatorTx struct {
    36  	// Metadata, inputs and outputs
    37  	BaseTx `serialize:"true"`
    38  	// Describes the validator
    39  	Validator `serialize:"true" json:"validator"`
    40  	// ID of the subnet this validator is validating
    41  	Subnet ids.ID `serialize:"true" json:"subnetID"`
    42  	// If the [Subnet] is the primary network, [Signer] is the BLS key for this
    43  	// validator. If the [Subnet] is not the primary network, this value is the
    44  	// empty signer
    45  	// Note: We do not enforce that the BLS key is unique across all validators.
    46  	//       This means that validators can share a key if they so choose.
    47  	//       However, a NodeID does uniquely map to a BLS key
    48  	Signer signer.Signer `serialize:"true" json:"signer"`
    49  	// Where to send staked tokens when done validating
    50  	StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"`
    51  	// Where to send validation rewards when done validating
    52  	ValidatorRewardsOwner fx.Owner `serialize:"true" json:"validationRewardsOwner"`
    53  	// Where to send delegation rewards when done validating
    54  	DelegatorRewardsOwner fx.Owner `serialize:"true" json:"delegationRewardsOwner"`
    55  	// Fee this validator charges delegators as a percentage, times 10,000
    56  	// For example, if this validator has DelegationShares=300,000 then they
    57  	// take 30% of rewards from delegators
    58  	DelegationShares uint32 `serialize:"true" json:"shares"`
    59  }
    60  
    61  // InitCtx sets the FxID fields in the inputs and outputs of this
    62  // [AddPermissionlessValidatorTx]. Also sets the [ctx] to the given [vm.ctx] so
    63  // that the addresses can be json marshalled into human readable format
    64  func (tx *AddPermissionlessValidatorTx) InitCtx(ctx *snow.Context) {
    65  	tx.BaseTx.InitCtx(ctx)
    66  	for _, out := range tx.StakeOuts {
    67  		out.FxID = secp256k1fx.ID
    68  		out.InitCtx(ctx)
    69  	}
    70  	tx.ValidatorRewardsOwner.InitCtx(ctx)
    71  	tx.DelegatorRewardsOwner.InitCtx(ctx)
    72  }
    73  
    74  func (tx *AddPermissionlessValidatorTx) SubnetID() ids.ID {
    75  	return tx.Subnet
    76  }
    77  
    78  func (tx *AddPermissionlessValidatorTx) NodeID() ids.NodeID {
    79  	return tx.Validator.NodeID
    80  }
    81  
    82  func (tx *AddPermissionlessValidatorTx) PublicKey() (*bls.PublicKey, bool, error) {
    83  	if err := tx.Signer.Verify(); err != nil {
    84  		return nil, false, err
    85  	}
    86  	key := tx.Signer.Key()
    87  	return key, key != nil, nil
    88  }
    89  
    90  func (tx *AddPermissionlessValidatorTx) PendingPriority() Priority {
    91  	if tx.Subnet == constants.PrimaryNetworkID {
    92  		return PrimaryNetworkValidatorPendingPriority
    93  	}
    94  	return SubnetPermissionlessValidatorPendingPriority
    95  }
    96  
    97  func (tx *AddPermissionlessValidatorTx) CurrentPriority() Priority {
    98  	if tx.Subnet == constants.PrimaryNetworkID {
    99  		return PrimaryNetworkValidatorCurrentPriority
   100  	}
   101  	return SubnetPermissionlessValidatorCurrentPriority
   102  }
   103  
   104  func (tx *AddPermissionlessValidatorTx) Stake() []*avax.TransferableOutput {
   105  	return tx.StakeOuts
   106  }
   107  
   108  func (tx *AddPermissionlessValidatorTx) ValidationRewardsOwner() fx.Owner {
   109  	return tx.ValidatorRewardsOwner
   110  }
   111  
   112  func (tx *AddPermissionlessValidatorTx) DelegationRewardsOwner() fx.Owner {
   113  	return tx.DelegatorRewardsOwner
   114  }
   115  
   116  func (tx *AddPermissionlessValidatorTx) Shares() uint32 {
   117  	return tx.DelegationShares
   118  }
   119  
   120  // SyntacticVerify returns nil iff [tx] is valid
   121  func (tx *AddPermissionlessValidatorTx) SyntacticVerify(ctx *snow.Context) error {
   122  	switch {
   123  	case tx == nil:
   124  		return ErrNilTx
   125  	case tx.SyntacticallyVerified: // already passed syntactic verification
   126  		return nil
   127  	case tx.Validator.NodeID == ids.EmptyNodeID:
   128  		return errEmptyNodeID
   129  	case len(tx.StakeOuts) == 0: // Ensure there is provided stake
   130  		return errNoStake
   131  	case tx.DelegationShares > reward.PercentDenominator:
   132  		return errTooManyShares
   133  	}
   134  
   135  	if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
   136  		return fmt.Errorf("failed to verify BaseTx: %w", err)
   137  	}
   138  	if err := verify.All(&tx.Validator, tx.Signer, tx.ValidatorRewardsOwner, tx.DelegatorRewardsOwner); err != nil {
   139  		return fmt.Errorf("failed to verify validator, signer, or rewards owners: %w", err)
   140  	}
   141  
   142  	hasKey := tx.Signer.Key() != nil
   143  	isPrimaryNetwork := tx.Subnet == constants.PrimaryNetworkID
   144  	if hasKey != isPrimaryNetwork {
   145  		return fmt.Errorf(
   146  			"%w: hasKey=%v != isPrimaryNetwork=%v",
   147  			errInvalidSigner,
   148  			hasKey,
   149  			isPrimaryNetwork,
   150  		)
   151  	}
   152  
   153  	for _, out := range tx.StakeOuts {
   154  		if err := out.Verify(); err != nil {
   155  			return fmt.Errorf("failed to verify output: %w", err)
   156  		}
   157  	}
   158  
   159  	firstStakeOutput := tx.StakeOuts[0]
   160  	stakedAssetID := firstStakeOutput.AssetID()
   161  	totalStakeWeight := firstStakeOutput.Output().Amount()
   162  	for _, out := range tx.StakeOuts[1:] {
   163  		newWeight, err := math.Add64(totalStakeWeight, out.Output().Amount())
   164  		if err != nil {
   165  			return err
   166  		}
   167  		totalStakeWeight = newWeight
   168  
   169  		assetID := out.AssetID()
   170  		if assetID != stakedAssetID {
   171  			return fmt.Errorf("%w: %q and %q", errMultipleStakedAssets, stakedAssetID, assetID)
   172  		}
   173  	}
   174  
   175  	switch {
   176  	case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec):
   177  		return errOutputsNotSorted
   178  	case totalStakeWeight != tx.Wght:
   179  		return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, tx.Wght, totalStakeWeight)
   180  	}
   181  
   182  	// cache that this is valid
   183  	tx.SyntacticallyVerified = true
   184  	return nil
   185  }
   186  
   187  func (tx *AddPermissionlessValidatorTx) Visit(visitor Visitor) error {
   188  	return visitor.AddPermissionlessValidatorTx(tx)
   189  }