github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/add_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  	"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/platformvm/reward"
    18  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    19  )
    20  
    21  var (
    22  	_ ValidatorTx     = (*AddValidatorTx)(nil)
    23  	_ ScheduledStaker = (*AddValidatorTx)(nil)
    24  
    25  	errTooManyShares = fmt.Errorf("a staker can only require at most %d shares from delegators", reward.PercentDenominator)
    26  )
    27  
    28  // AddValidatorTx is an unsigned addValidatorTx
    29  type AddValidatorTx struct {
    30  	// Metadata, inputs and outputs
    31  	BaseTx `serialize:"true"`
    32  	// Describes the delegatee
    33  	Validator `serialize:"true" json:"validator"`
    34  	// Where to send staked tokens when done validating
    35  	StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"`
    36  	// Where to send staking rewards when done validating
    37  	RewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"`
    38  	// Fee this validator charges delegators as a percentage, times 10,000
    39  	// For example, if this validator has DelegationShares=300,000 then they
    40  	// take 30% of rewards from delegators
    41  	DelegationShares uint32 `serialize:"true" json:"shares"`
    42  }
    43  
    44  // InitCtx sets the FxID fields in the inputs and outputs of this
    45  // [AddValidatorTx]. Also sets the [ctx] to the given [vm.ctx] so that
    46  // the addresses can be json marshalled into human readable format
    47  func (tx *AddValidatorTx) InitCtx(ctx *snow.Context) {
    48  	tx.BaseTx.InitCtx(ctx)
    49  	for _, out := range tx.StakeOuts {
    50  		out.FxID = secp256k1fx.ID
    51  		out.InitCtx(ctx)
    52  	}
    53  	tx.RewardsOwner.InitCtx(ctx)
    54  }
    55  
    56  func (*AddValidatorTx) SubnetID() ids.ID {
    57  	return constants.PrimaryNetworkID
    58  }
    59  
    60  func (tx *AddValidatorTx) NodeID() ids.NodeID {
    61  	return tx.Validator.NodeID
    62  }
    63  
    64  func (*AddValidatorTx) PublicKey() (*bls.PublicKey, bool, error) {
    65  	return nil, false, nil
    66  }
    67  
    68  func (*AddValidatorTx) PendingPriority() Priority {
    69  	return PrimaryNetworkValidatorPendingPriority
    70  }
    71  
    72  func (*AddValidatorTx) CurrentPriority() Priority {
    73  	return PrimaryNetworkValidatorCurrentPriority
    74  }
    75  
    76  func (tx *AddValidatorTx) Stake() []*avax.TransferableOutput {
    77  	return tx.StakeOuts
    78  }
    79  
    80  func (tx *AddValidatorTx) ValidationRewardsOwner() fx.Owner {
    81  	return tx.RewardsOwner
    82  }
    83  
    84  func (tx *AddValidatorTx) DelegationRewardsOwner() fx.Owner {
    85  	return tx.RewardsOwner
    86  }
    87  
    88  func (tx *AddValidatorTx) Shares() uint32 {
    89  	return tx.DelegationShares
    90  }
    91  
    92  // SyntacticVerify returns nil iff [tx] is valid
    93  func (tx *AddValidatorTx) SyntacticVerify(ctx *snow.Context) error {
    94  	switch {
    95  	case tx == nil:
    96  		return ErrNilTx
    97  	case tx.SyntacticallyVerified: // already passed syntactic verification
    98  		return nil
    99  	case tx.DelegationShares > reward.PercentDenominator: // Ensure delegators shares are in the allowed amount
   100  		return errTooManyShares
   101  	}
   102  
   103  	if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
   104  		return fmt.Errorf("failed to verify BaseTx: %w", err)
   105  	}
   106  	if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil {
   107  		return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
   108  	}
   109  
   110  	totalStakeWeight := uint64(0)
   111  	for _, out := range tx.StakeOuts {
   112  		if err := out.Verify(); err != nil {
   113  			return fmt.Errorf("failed to verify output: %w", err)
   114  		}
   115  		newWeight, err := math.Add64(totalStakeWeight, out.Output().Amount())
   116  		if err != nil {
   117  			return err
   118  		}
   119  		totalStakeWeight = newWeight
   120  
   121  		assetID := out.AssetID()
   122  		if assetID != ctx.AVAXAssetID {
   123  			return fmt.Errorf("%w but is %q", errStakeMustBeAVAX, assetID)
   124  		}
   125  	}
   126  
   127  	switch {
   128  	case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec):
   129  		return errOutputsNotSorted
   130  	case totalStakeWeight != tx.Wght:
   131  		return fmt.Errorf("%w: weight %d != stake %d", errValidatorWeightMismatch, tx.Wght, totalStakeWeight)
   132  	}
   133  
   134  	// cache that this is valid
   135  	tx.SyntacticallyVerified = true
   136  	return nil
   137  }
   138  
   139  func (tx *AddValidatorTx) Visit(visitor Visitor) error {
   140  	return visitor.AddValidatorTx(tx)
   141  }