-
Notifications
You must be signed in to change notification settings - Fork 18
/
msg_server_submit_bundle_proposal.go
140 lines (107 loc) · 5.38 KB
/
msg_server_submit_bundle_proposal.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
package keeper
import (
"context"
"github.com/KYVENetwork/chain/util"
"github.com/KYVENetwork/chain/x/bundles/types"
sdk "github.com/cosmos/cosmos-sdk/types"
// Delegation
delegationTypes "github.com/KYVENetwork/chain/x/delegation/types"
// Pool
poolTypes "github.com/KYVENetwork/chain/x/pool/types"
)
// SubmitBundleProposal handles the logic of an SDK message that allows protocol nodes to submit a new bundle proposal.
func (k msgServer) SubmitBundleProposal(goCtx context.Context, msg *types.MsgSubmitBundleProposal) (*types.MsgSubmitBundleProposalResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if err := k.AssertCanPropose(ctx, msg.PoolId, msg.Staker, msg.Creator, msg.FromIndex); err != nil {
return nil, err
}
bundleProposal, _ := k.GetBundleProposal(ctx, msg.PoolId)
// Validate submit bundle args.
if err := k.validateSubmitBundleArgs(ctx, &bundleProposal, msg); err != nil {
return nil, err
}
// Reset points of uploader as node has proven to be active.
k.resetPoints(ctx, msg.PoolId, msg.Staker)
// If previous bundle was dropped just register the new bundle.
// No previous round needs to be evaluated
if bundleProposal.StorageId == "" {
nextUploader := k.chooseNextUploader(ctx, msg.PoolId)
k.registerBundleProposalFromUploader(ctx, msg, nextUploader)
return &types.MsgSubmitBundleProposalResponse{}, nil
}
// Previous round contains a bundle which needs to be validated now.
// Increase points of stakers who did not vote at all + slash + remove if necessary.
// The protocol requires everybody to stay always active.
k.handleNonVoters(ctx, msg.PoolId)
// evaluate all votes and determine status based on the votes weighted with stake + delegation
voteDistribution := k.GetVoteDistribution(ctx, msg.PoolId)
// Handle tally outcome
switch voteDistribution.Status {
case types.BUNDLE_STATUS_VALID:
// If a bundle is valid the following things happen:
// 1. Funders and Inflation Pool are charged. The total payout is divided
// between the uploader, its delegators and the treasury.
// The appropriate funds are deducted from the total pool funds
// 2. The next uploader is randomly selected based on everybody who
// voted valid on this bundle.
// 3. The bundle is finalized by added it permanently to the state.
// 4. The sender immediately starts the next round by registering
// his new bundle proposal.
pool, _ := k.poolKeeper.GetPool(ctx, msg.PoolId)
// charge the operating cost from funders
fundersPayout, err := k.poolKeeper.ChargeFundersOfPool(ctx, msg.PoolId, pool.OperatingCost)
if err != nil {
return &types.MsgSubmitBundleProposalResponse{}, err
}
// charge the inflation pool
inflationPayout, err := k.poolKeeper.ChargeInflationPool(ctx, msg.PoolId)
if err != nil {
return &types.MsgSubmitBundleProposalResponse{}, err
}
// calculate payouts to the different stakeholders like treasury, uploader and delegators
bundleReward := k.calculatePayouts(ctx, msg.PoolId, fundersPayout+inflationPayout)
// payout rewards to treasury
if err := util.TransferFromModuleToTreasury(k.accountKeeper, k.distrkeeper, ctx, poolTypes.ModuleName, bundleReward.Treasury); err != nil {
return nil, err
}
// payout rewards to uploader through commission rewards
if err := k.stakerKeeper.IncreaseStakerCommissionRewards(ctx, bundleProposal.Uploader, bundleReward.Uploader); err != nil {
return nil, err
}
// payout rewards to delegators through delegation rewards
if err := k.delegationKeeper.PayoutRewards(ctx, bundleProposal.Uploader, bundleReward.Delegation, poolTypes.ModuleName); err != nil {
return nil, err
}
// slash stakers who voted incorrectly
for _, voter := range bundleProposal.VotersInvalid {
k.slashDelegatorsAndRemoveStaker(ctx, msg.PoolId, voter, delegationTypes.SLASH_TYPE_VOTE)
}
// Determine next uploader and register next bundle
// Get next uploader from stakers who voted `valid`
nextUploader := k.chooseNextUploaderFromList(ctx, msg.PoolId, bundleProposal.VotersValid)
k.finalizeCurrentBundleProposal(ctx, pool.Id, voteDistribution, fundersPayout, inflationPayout, bundleReward, nextUploader)
// Register the provided bundle as a new proposal for the next round
k.registerBundleProposalFromUploader(ctx, msg, nextUploader)
return &types.MsgSubmitBundleProposalResponse{}, nil
case types.BUNDLE_STATUS_INVALID:
// If the bundles is invalid, everybody who voted incorrectly gets slashed.
// The bundle provided by the message-sender is of no mean, because the previous bundle
// turned out to be incorrect.
// There this round needs to start again and the message-sender stays uploader.
// slash stakers who voted incorrectly - uploader receives upload slash
for _, voter := range bundleProposal.VotersValid {
if voter == bundleProposal.Uploader {
k.slashDelegatorsAndRemoveStaker(ctx, msg.PoolId, voter, delegationTypes.SLASH_TYPE_UPLOAD)
} else {
k.slashDelegatorsAndRemoveStaker(ctx, msg.PoolId, voter, delegationTypes.SLASH_TYPE_VOTE)
}
}
// Drop current bundle. Can't register the provided bundle because the previous bundles
// needs to be resubmitted first.
k.dropCurrentBundleProposal(ctx, msg.PoolId, voteDistribution, bundleProposal.NextUploader)
return &types.MsgSubmitBundleProposalResponse{}, nil
default:
// If the bundle is neither valid nor invalid the quorum has not been reached yet.
return nil, types.ErrQuorumNotReached
}
}