Skip to content

Commit 00c589e

Browse files
authored
[Phase 1] Feeds optimization (#164)
* optimize validator by power access in endblock * fix from comments * remove get oracle status from loop * change validator price to store as a list * grpc query validator prices returns empty slice * fix test for validator prices query * change bothan version * fix from comment * [Phase] Fix feeds by spec (#165) * validate submit signals max size by max supported feeds * make new error type for submit too large signals list * change transition time to grace period and add description * change price service to price service config * power threshold to power step threshold * move deviation to only query * change blocks per feeds update to supported feeds update interval * put power in feeds, use it to calculate deviation, change deviation param name * remove deviation from median calculation * change SubmitPrice to SignalPrice * remove MaxSignalIDCharacters from param * delete signal total power when the power is zero * fix lint * fix from comments * fix readme to match spec * remove unnecessary map * merge validator price query path * separate check miss report from calculate price fn * fix from comments * update some naming * initiate slice with capacity * fix from comments * move MaximumGuaranteeBlockTime to constant.go * fix CalculatePrice Test * fix CheckMissReport * fix lint * update reference source config * update SignalID of bench test to fit the new format * fix grogu errors * change deviation in thousandth to deviation basis point
1 parent 6025d8b commit 00c589e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2699
-2272
lines changed

benchmark/feeds_bench_test.go

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package benchmark
33
import (
44
"fmt"
55
"math/rand"
6+
"sort"
67
"testing"
78

89
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -15,8 +16,41 @@ import (
1516
oracletypes "github.com/bandprotocol/chain/v2/x/oracle/types"
1617
)
1718

18-
// benchmark test for delivering MsgSubmitPrices
19-
func BenchmarkSubmitPricesDeliver(b *testing.B) {
19+
var (
20+
ValidValidator = sdk.ValAddress("1000000001")
21+
ValidValidator2 = sdk.ValAddress("1000000002")
22+
)
23+
24+
func BenchmarkSortMap(b *testing.B) {
25+
b.ResetTimer()
26+
b.StopTimer()
27+
ba := InitializeBenchmarkApp(b, -1)
28+
expValPrices := generateValidatorPrices(300, ValidValidator.String(), ba.Ctx.BlockTime().Unix())
29+
valPricesMap := make(map[string]types.ValidatorPrice)
30+
for _, valPrice := range expValPrices {
31+
valPricesMap[valPrice.SignalID] = valPrice
32+
}
33+
for i := 0; i < b.N; i++ {
34+
for j := 0; j < 2000; j++ {
35+
b.StartTimer()
36+
keys := make([]string, len(valPricesMap))
37+
k := int(0)
38+
for key := range valPricesMap {
39+
keys[k] = key
40+
k++
41+
}
42+
sort.Strings(keys)
43+
valPrices := make([]types.ValidatorPrice, len(valPricesMap))
44+
for idx, key := range keys {
45+
valPrices[idx] = valPricesMap[key]
46+
}
47+
b.StopTimer()
48+
}
49+
}
50+
}
51+
52+
// benchmark test for delivering MsgSubmitSignalPrices
53+
func BenchmarkSubmitSignalPricesDeliver(b *testing.B) {
2054
b.ResetTimer()
2155
b.StopTimer()
2256

@@ -31,13 +65,16 @@ func BenchmarkSubmitPricesDeliver(b *testing.B) {
3165
err = setupFeeds(ba)
3266
require.NoError(b, err)
3367

68+
err = setupValidatorPriceList(ba, vals)
69+
require.NoError(b, err)
70+
3471
ba.CallBeginBlock()
3572

3673
txs := []sdk.Tx{}
3774
for _, val := range vals {
3875
tx := GenSequenceOfTxs(
3976
ba.TxConfig,
40-
GenMsgSubmitPrices(
77+
GenMsgSubmitSignalPrices(
4178
val,
4279
ba.FeedsKeeper.GetSupportedFeeds(ba.Ctx).Feeds,
4380
ba.Ctx.BlockTime().Unix(),
@@ -105,9 +142,8 @@ func setupFeeds(ba *BenchmarkApp) error {
105142
feeds := []types.Feed{}
106143
for i := int64(0); i < numFeeds; i++ {
107144
feeds = append(feeds, types.Feed{
108-
SignalID: fmt.Sprintf("signal.%d", i),
109-
Interval: 60,
110-
DeviationInThousandth: 5,
145+
SignalID: fmt.Sprintf("signal.%d", i),
146+
Interval: 60,
111147
})
112148
}
113149
ba.FeedsKeeper.SetSupportedFeeds(ba.Ctx, feeds)
@@ -118,22 +154,51 @@ func setupFeeds(ba *BenchmarkApp) error {
118154
return nil
119155
}
120156

157+
func setupValidatorPriceList(ba *BenchmarkApp, vals []*Account) error {
158+
sfs := ba.FeedsKeeper.GetSupportedFeeds(ba.Ctx)
159+
160+
ba.CallBeginBlock()
161+
for valIdx, val := range vals {
162+
valPrices := []types.ValidatorPrice{}
163+
for _, feed := range sfs.Feeds {
164+
valPrices = append(valPrices, types.ValidatorPrice{
165+
PriceStatus: types.PriceStatusAvailable,
166+
Validator: val.ValAddress.String(),
167+
SignalID: feed.SignalID,
168+
Price: (10000 + uint64(valIdx)) * 10e9,
169+
Timestamp: ba.Ctx.BlockTime().Unix() - 40,
170+
})
171+
}
172+
err := ba.FeedsKeeper.SetValidatorPriceList(ba.Ctx, val.ValAddress, valPrices)
173+
if err != nil {
174+
return err
175+
}
176+
}
177+
ba.CallEndBlock()
178+
ba.Commit()
179+
180+
return nil
181+
}
182+
121183
func setupValidatorPrices(ba *BenchmarkApp, vals []*Account) error {
122184
sfs := ba.FeedsKeeper.GetSupportedFeeds(ba.Ctx)
123185

124186
ba.CallBeginBlock()
125-
for _, feed := range sfs.Feeds {
126-
for valIdx, val := range vals {
127-
err := ba.FeedsKeeper.SetValidatorPrice(ba.Ctx, types.ValidatorPrice{
187+
for valIdx, val := range vals {
188+
valPrices := []types.ValidatorPrice{}
189+
for _, feed := range sfs.Feeds {
190+
valPrices = append(valPrices, types.ValidatorPrice{
128191
PriceStatus: types.PriceStatusAvailable,
129192
Validator: val.ValAddress.String(),
130193
SignalID: feed.SignalID,
131194
Price: (10000 + uint64(valIdx)) * 10e9,
132195
Timestamp: ba.Ctx.BlockTime().Unix(),
133196
})
134-
if err != nil {
135-
return err
136-
}
197+
}
198+
199+
err := ba.FeedsKeeper.SetValidatorPriceList(ba.Ctx, val.ValAddress, valPrices)
200+
if err != nil {
201+
return err
137202
}
138203
}
139204
ba.CallEndBlock()
@@ -213,3 +278,18 @@ func generateValidators(ba *BenchmarkApp, num int) ([]*Account, error) {
213278

214279
return accs, nil
215280
}
281+
282+
// generateValidatorPrices generates a slice of ValidatorPrice with the specified number of elements.
283+
func generateValidatorPrices(numElements int, validatorAddress string, timestamp int64) []types.ValidatorPrice {
284+
prices := make([]types.ValidatorPrice, numElements)
285+
286+
for i := 0; i < numElements; i++ {
287+
prices[i] = types.ValidatorPrice{
288+
Validator: validatorAddress,
289+
SignalID: fmt.Sprintf("CS:BAND%d-USD", i),
290+
Price: 1e10,
291+
Timestamp: timestamp,
292+
}
293+
}
294+
return prices
295+
}

benchmark/helper_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,21 @@ func GenMsgRequestData(
7171
return []sdk.Msg{&msg}
7272
}
7373

74-
func GenMsgSubmitPrices(
74+
func GenMsgSubmitSignalPrices(
7575
sender *Account,
7676
feeds []feedstypes.Feed,
7777
timestamp int64,
7878
) []sdk.Msg {
79-
prices := []feedstypes.SubmitPrice{}
79+
prices := make([]feedstypes.SignalPrice, 0, len(feeds))
8080
for _, feed := range feeds {
81-
prices = append(prices, feedstypes.SubmitPrice{
81+
prices = append(prices, feedstypes.SignalPrice{
8282
PriceStatus: feedstypes.PriceStatusAvailable,
8383
SignalID: feed.SignalID,
8484
Price: 60000,
8585
})
8686
}
8787

88-
msg := feedstypes.NewMsgSubmitPrices(sender.ValAddress.String(), timestamp, prices)
88+
msg := feedstypes.NewMsgSubmitSignalPrices(sender.ValAddress.String(), timestamp, prices)
8989

9090
return []sdk.Msg{msg}
9191
}

grogu/cmd/run.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func createRunE(ctx *context.Context) func(cmd *cobra.Command, args []string) er
127127
}
128128

129129
// Create submit channel
130-
submitPriceCh := make(chan []types.SubmitPrice, 300)
130+
submitSignalPriceCh := make(chan []types.SignalPrice, 300)
131131

132132
// Parse validator address
133133
valAddr, err := sdk.ValAddressFromBech32(ctx.Config.Validator)
@@ -155,7 +155,7 @@ func createRunE(ctx *context.Context) func(cmd *cobra.Command, args []string) er
155155
feedQuerier,
156156
bothanService,
157157
time.Second,
158-
submitPriceCh,
158+
submitSignalPriceCh,
159159
l,
160160
valAddr,
161161
&pendingSignalIDs,
@@ -168,7 +168,7 @@ func createRunE(ctx *context.Context) func(cmd *cobra.Command, args []string) er
168168
clients,
169169
l,
170170
ctx.Keyring,
171-
submitPriceCh,
171+
submitSignalPriceCh,
172172
authQuerier,
173173
txQuerier,
174174
valAddr,

grogu/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func Main() {
3434
func createRootCmd(ctx *context.Context) *cobra.Command {
3535
rootCmd := &cobra.Command{
3636
Use: "grogu",
37-
Short: "BandChain daemon to submit prices for feeds module",
37+
Short: "BandChain daemon to submit signal prices for feeds module",
3838
}
3939

4040
rootCmd.AddCommand(

grogu/signaller/signaller.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ type Signaller struct {
2626
bothanClient bothan.Client
2727
// How often to check for signal changes
2828
interval time.Duration
29-
submitCh chan<- []types.SubmitPrice
29+
submitCh chan<- []types.SignalPrice
3030
logger *logger.Logger
3131
valAddress sdk.ValAddress
3232
pendingSignalIDs *sync.Map
3333

3434
distributionStartPercentage uint64
3535
distributionOffsetPercentage uint64
3636

37-
signalIDToFeed map[string]types.Feed
37+
signalIDToFeed map[string]types.FeedWithDeviation
3838
signalIDToValidatorPrice map[string]types.ValidatorPrice
3939
params *types.Params
4040
}
@@ -43,7 +43,7 @@ func New(
4343
feedQuerier *querier.FeedQuerier,
4444
bothanClient bothan.Client,
4545
interval time.Duration,
46-
submitCh chan<- []types.SubmitPrice,
46+
submitCh chan<- []types.SignalPrice,
4747
logger *logger.Logger,
4848
valAddress sdk.ValAddress,
4949
pendingSignalIDs *sync.Map,
@@ -60,7 +60,7 @@ func New(
6060
pendingSignalIDs: pendingSignalIDs,
6161
distributionStartPercentage: distributionStartPercentage,
6262
distributionOffsetPercentage: distributionOffsetPercentage,
63-
signalIDToFeed: make(map[string]types.Feed),
63+
signalIDToFeed: make(map[string]types.FeedWithDeviation),
6464
signalIDToValidatorPrice: make(map[string]types.ValidatorPrice),
6565
params: nil,
6666
}
@@ -135,7 +135,7 @@ func (h *Signaller) updateFeedMap() bool {
135135
return false
136136
}
137137

138-
h.signalIDToFeed = sliceToMap(resp.SupportedFeeds.Feeds, func(feed types.Feed) string {
138+
h.signalIDToFeed = sliceToMap(resp.SupportedFeeds.Feeds, func(feed types.FeedWithDeviation) string {
139139
return feed.SignalID
140140
})
141141

@@ -281,7 +281,7 @@ func (h *Signaller) isPriceValid(
281281
}
282282

283283
func (h *Signaller) shouldUpdatePrice(
284-
feed types.Feed,
284+
feed types.FeedWithDeviation,
285285
valPrice types.ValidatorPrice,
286286
newPrice uint64,
287287
now time.Time,
@@ -304,15 +304,15 @@ func (h *Signaller) shouldUpdatePrice(
304304
}
305305

306306
// Check if the price is deviated from the last submission, if it is, add it to the list of prices to update
307-
if isDeviated(feed.DeviationInThousandth, valPrice.Price, newPrice) {
307+
if isDeviated(feed.DeviationBasisPoint, valPrice.Price, newPrice) {
308308
return true
309309
}
310310

311311
return false
312312
}
313313

314-
func (h *Signaller) prepareSubmitPrices(filteredPrices []*proto.PriceData, now int64) ([]types.SubmitPrice, []string) {
315-
submitPrices := make([]types.SubmitPrice, 0, len(filteredPrices))
314+
func (h *Signaller) prepareSubmitPrices(filteredPrices []*proto.PriceData, now int64) ([]types.SignalPrice, []string) {
315+
submitPrices := make([]types.SignalPrice, 0, len(filteredPrices))
316316
unusedSignalIDs := make([]string, 0, len(filteredPrices))
317317

318318
for _, price := range filteredPrices {

grogu/signaller/utils.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import (
1515
)
1616

1717
// isDeviated checks if the deviation between the old price and the new price
18-
// exceeds a given threshold in thousandths.
18+
// exceeds a given threshold in basis points.
1919

2020
// Parameters:
21-
// - deviationInThousandth: the allowable deviation in thousandths (1/1000th)
21+
// - deviationBasisPoint: the allowable deviation in basis points (1/1000th)
2222
// - oldPrice: the original price
2323
// - newPrice: the new price to compare against the original
2424
//
@@ -28,48 +28,48 @@ import (
2828
//
2929
// The deviation is calculated as follows:
3030
// 1. Calculate the absolute difference between the new price and the old price.
31-
// 2. Compute the deviation in thousandths by dividing the difference by the
32-
// original price and multiplying by 1000.
31+
// 2. Compute the deviation in basis points by dividing the difference by the
32+
// original price and multiplying by 10000.
3333
// 3. Check if the calculated deviation meets or exceeds the allowable deviation.
34-
func isDeviated(deviationInThousandth int64, oldPrice uint64, newPrice uint64) bool {
34+
func isDeviated(deviationBasisPoint int64, oldPrice uint64, newPrice uint64) bool {
3535
// Calculate the deviation
3636
diff := math.Abs(float64(newPrice) - float64(oldPrice))
37-
dev := int64((diff / float64(oldPrice)) * 1000)
37+
dev := int64((diff / float64(oldPrice)) * 10000)
3838

3939
// Check if the new price deviation is meets or exceeds the bounds
40-
return deviationInThousandth <= dev
40+
return deviationBasisPoint <= dev
4141
}
4242

43-
func convertPriceData(priceData *proto.PriceData) (types.SubmitPrice, error) {
43+
func convertPriceData(priceData *proto.PriceData) (types.SignalPrice, error) {
4444
switch priceData.PriceStatus {
4545
case proto.PriceStatus_PRICE_STATUS_UNSPECIFIED:
4646
// This should never happen
4747
panic("unspecified price status")
4848
case proto.PriceStatus_PRICE_STATUS_UNSUPPORTED:
49-
return types.SubmitPrice{
49+
return types.SignalPrice{
5050
PriceStatus: types.PriceStatusUnsupported,
5151
SignalID: priceData.SignalId,
5252
Price: 0,
5353
}, nil
5454
case proto.PriceStatus_PRICE_STATUS_UNAVAILABLE:
55-
return types.SubmitPrice{
55+
return types.SignalPrice{
5656
PriceStatus: types.PriceStatusUnavailable,
5757
SignalID: priceData.SignalId,
5858
Price: 0,
5959
}, nil
6060
case proto.PriceStatus_PRICE_STATUS_AVAILABLE:
6161
price, err := safeConvert(priceData.Price)
6262
if err != nil {
63-
return types.SubmitPrice{}, err
63+
return types.SignalPrice{}, err
6464
}
65-
return types.SubmitPrice{
65+
return types.SignalPrice{
6666
PriceStatus: types.PriceStatusAvailable,
6767
SignalID: priceData.SignalId,
6868
Price: price,
6969
}, nil
7070
default:
7171
// Handle unexpected price status
72-
return types.SubmitPrice{}, fmt.Errorf("unexpected price status: %v", priceData.PriceStatus)
72+
return types.SignalPrice{}, fmt.Errorf("unexpected price status: %v", priceData.PriceStatus)
7373
}
7474
}
7575

0 commit comments

Comments
 (0)