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 }