forked from babylonlabs-io/babylon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvoting_power_table.go
167 lines (144 loc) · 5.94 KB
/
voting_power_table.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package keeper
import (
"context"
"fmt"
"github.com/cosmos/cosmos-sdk/runtime"
"cosmossdk.io/store/prefix"
bbn "github.com/babylonlabs-io/babylon/types"
"github.com/babylonlabs-io/babylon/x/btcstaking/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// IterateActiveFPs iterates over all finality providers that are not slashed
func (k Keeper) IterateActiveFPs(ctx context.Context, handler func(fp *types.FinalityProvider) (shouldContinue bool)) {
k.IterateFPs(ctx, func(fp *types.FinalityProvider) (shouldContinue bool) {
if fp.IsSlashed() {
// slashed finality provider is removed from finality provider set
return true
}
return handler(fp)
})
}
// IterateFPs iterates over all finality providers.
func (k Keeper) IterateFPs(ctx context.Context, handler func(fp *types.FinalityProvider) (shouldContinue bool)) {
// filter out all finality providers with positive voting power
fpIter := k.finalityProviderStore(ctx).Iterator(nil, nil)
defer fpIter.Close()
for ; fpIter.Valid(); fpIter.Next() {
var fp types.FinalityProvider
k.cdc.MustUnmarshal(fpIter.Value(), &fp)
shouldContinue := handler(&fp)
if !shouldContinue {
return
}
}
}
func (k Keeper) SetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64, power uint64) {
store := k.votingPowerBbnBlockHeightStore(ctx, height)
store.Set(fpBTCPK, sdk.Uint64ToBigEndian(power))
}
// GetVotingPower gets the voting power of a given finality provider at a given Babylon height
func (k Keeper) GetVotingPower(ctx context.Context, fpBTCPK []byte, height uint64) uint64 {
if !k.HasFinalityProvider(ctx, fpBTCPK) {
return 0
}
store := k.votingPowerBbnBlockHeightStore(ctx, height)
powerBytes := store.Get(fpBTCPK)
if len(powerBytes) == 0 {
return 0
}
return sdk.BigEndianToUint64(powerBytes)
}
// GetCurrentVotingPower gets the voting power of a given finality provider at the current height
// NOTE: it's possible that the voting power table is 1 block behind CometBFT, e.g., when `BeginBlock`
// hasn't executed yet
func (k Keeper) GetCurrentVotingPower(ctx context.Context, fpBTCPK []byte) (uint64, uint64) {
// find the last recorded voting power table via iterator
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
store := prefix.NewStore(storeAdapter, types.VotingPowerKey)
iter := store.ReverseIterator(nil, nil)
defer iter.Close()
// no voting power table is known yet, return 0
if !iter.Valid() {
return 0, 0
}
// there is known voting power table, find the last height
lastHeight := sdk.BigEndianToUint64(iter.Key())
storeAtHeight := prefix.NewStore(store, sdk.Uint64ToBigEndian(lastHeight))
// if the finality provider is not known, return 0 voting power
if !k.HasFinalityProvider(ctx, fpBTCPK) {
return lastHeight, 0
}
// find the voting power of this finality provider
powerBytes := storeAtHeight.Get(fpBTCPK)
if len(powerBytes) == 0 {
return lastHeight, 0
}
return lastHeight, sdk.BigEndianToUint64(powerBytes)
}
// HasVotingPowerTable checks if the voting power table exists at a given height
func (k Keeper) HasVotingPowerTable(ctx context.Context, height uint64) bool {
store := k.votingPowerBbnBlockHeightStore(ctx, height)
iter := store.Iterator(nil, nil)
defer iter.Close()
return iter.Valid()
}
// GetVotingPowerTable gets the voting power table, i.e., finality provider set at a given height
func (k Keeper) GetVotingPowerTable(ctx context.Context, height uint64) map[string]uint64 {
store := k.votingPowerBbnBlockHeightStore(ctx, height)
iter := store.Iterator(nil, nil)
defer iter.Close()
// if no finality provider at this height, return nil
if !iter.Valid() {
return nil
}
// get all finality providers at this height
fpSet := map[string]uint64{}
for ; iter.Valid(); iter.Next() {
fpBTCPK, err := bbn.NewBIP340PubKey(iter.Key())
if err != nil {
// failing to unmarshal finality provider BTC PK in KVStore is a programming error
panic(fmt.Errorf("%w: %w", bbn.ErrUnmarshal, err))
}
fpSet[fpBTCPK.MarshalHex()] = sdk.BigEndianToUint64(iter.Value())
}
return fpSet
}
// GetBTCStakingActivatedHeight returns the height when the BTC staking protocol is activated
// i.e., the first height where a finality provider has voting power
// Before the BTC staking protocol is activated, we don't index or tally any block
func (k Keeper) GetBTCStakingActivatedHeight(ctx context.Context) (uint64, error) {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
votingPowerStore := prefix.NewStore(storeAdapter, types.VotingPowerKey)
iter := votingPowerStore.Iterator(nil, nil)
defer iter.Close()
// if the iterator is valid, then there exists a height that has a finality provider with voting power
if iter.Valid() {
return sdk.BigEndianToUint64(iter.Key()), nil
} else {
return 0, types.ErrBTCStakingNotActivated
}
}
func (k Keeper) IsBTCStakingActivated(ctx context.Context) bool {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
votingPowerStore := prefix.NewStore(storeAdapter, types.VotingPowerKey)
iter := votingPowerStore.Iterator(nil, nil)
defer iter.Close()
// if the iterator is valid, then BTC staking is already activated
return iter.Valid()
}
// votingPowerBbnBlockHeightStore returns the KVStore of the finality providers' voting power
// prefix: (VotingPowerKey || Babylon block height)
// key: Bitcoin secp256k1 PK
// value: voting power quantified in Satoshi
func (k Keeper) votingPowerBbnBlockHeightStore(ctx context.Context, height uint64) prefix.Store {
votingPowerStore := k.votingPowerStore(ctx)
return prefix.NewStore(votingPowerStore, sdk.Uint64ToBigEndian(height))
}
// votingPowerStore returns the KVStore of the finality providers' voting power
// prefix: (VotingPowerKey)
// key: Babylon block height || Bitcoin secp256k1 PK
// value: voting power quantified in Satoshi
func (k Keeper) votingPowerStore(ctx context.Context) prefix.Store {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
return prefix.NewStore(storeAdapter, types.VotingPowerKey)
}