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