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