forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 4
/
add_permissionless_delegator_tx.go
143 lines (122 loc) · 4.33 KB
/
add_permissionless_delegator_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
141
142
143
// Copyright (C) 2019-2024, 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/secp256k1fx"
)
var (
_ DelegatorTx = (*AddPermissionlessDelegatorTx)(nil)
_ ScheduledStaker = (*AddPermissionlessDelegatorTx)(nil)
)
// AddPermissionlessDelegatorTx is an unsigned addPermissionlessDelegatorTx
type AddPermissionlessDelegatorTx struct {
// Metadata, inputs and outputs
BaseTx `serialize:"true"`
// Describes the validator
Validator `serialize:"true" json:"validator"`
// ID of the subnet this validator is validating
Subnet ids.ID `serialize:"true" json:"subnetID"`
// Where to send staked tokens when done validating
StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"`
// Where to send staking rewards when done validating
DelegationRewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"`
}
// InitCtx sets the FxID fields in the inputs and outputs of this
// [AddPermissionlessDelegatorTx]. Also sets the [ctx] to the given [vm.ctx] so
// that the addresses can be json marshalled into human readable format
func (tx *AddPermissionlessDelegatorTx) InitCtx(ctx *snow.Context) {
tx.BaseTx.InitCtx(ctx)
for _, out := range tx.StakeOuts {
out.FxID = secp256k1fx.ID
out.InitCtx(ctx)
}
tx.DelegationRewardsOwner.InitCtx(ctx)
}
func (tx *AddPermissionlessDelegatorTx) SubnetID() ids.ID {
return tx.Subnet
}
func (tx *AddPermissionlessDelegatorTx) NodeID() ids.NodeID {
return tx.Validator.NodeID
}
func (*AddPermissionlessDelegatorTx) PublicKey() (*bls.PublicKey, bool, error) {
return nil, false, nil
}
func (tx *AddPermissionlessDelegatorTx) PendingPriority() Priority {
if tx.Subnet == constants.PrimaryNetworkID {
return PrimaryNetworkDelegatorBanffPendingPriority
}
return SubnetPermissionlessDelegatorPendingPriority
}
func (tx *AddPermissionlessDelegatorTx) CurrentPriority() Priority {
if tx.Subnet == constants.PrimaryNetworkID {
return PrimaryNetworkDelegatorCurrentPriority
}
return SubnetPermissionlessDelegatorCurrentPriority
}
func (tx *AddPermissionlessDelegatorTx) Stake() []*avax.TransferableOutput {
return tx.StakeOuts
}
func (tx *AddPermissionlessDelegatorTx) RewardsOwner() fx.Owner {
return tx.DelegationRewardsOwner
}
// SyntacticVerify returns nil iff [tx] is valid
func (tx *AddPermissionlessDelegatorTx) SyntacticVerify(ctx *snow.Context) error {
switch {
case tx == nil:
return ErrNilTx
case tx.SyntacticallyVerified: // already passed syntactic verification
return nil
case len(tx.StakeOuts) == 0: // Ensure there is provided stake
return errNoStake
}
if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
return fmt.Errorf("failed to verify BaseTx: %w", err)
}
if err := verify.All(&tx.Validator, tx.DelegationRewardsOwner); err != nil {
return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
}
for _, out := range tx.StakeOuts {
if err := out.Verify(); err != nil {
return fmt.Errorf("failed to verify output: %w", err)
}
}
firstStakeOutput := tx.StakeOuts[0]
stakedAssetID := firstStakeOutput.AssetID()
totalStakeWeight := firstStakeOutput.Output().Amount()
for _, out := range tx.StakeOuts[1:] {
newWeight, err := math.Add(totalStakeWeight, out.Output().Amount())
if err != nil {
return err
}
totalStakeWeight = newWeight
assetID := out.AssetID()
if assetID != stakedAssetID {
return fmt.Errorf("%w: %q and %q", errMultipleStakedAssets, stakedAssetID, assetID)
}
}
switch {
case !avax.IsSortedTransferableOutputs(tx.StakeOuts, Codec):
return errOutputsNotSorted
case totalStakeWeight != tx.Wght:
return fmt.Errorf("%w, delegator weight %d total stake weight %d",
errDelegatorWeightMismatch,
tx.Wght,
totalStakeWeight,
)
}
// cache that this is valid
tx.SyntacticallyVerified = true
return nil
}
func (tx *AddPermissionlessDelegatorTx) Visit(visitor Visitor) error {
return visitor.AddPermissionlessDelegatorTx(tx)
}