-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[R4R] feat(oracle) #24
Changes from 1 commit
fc501d1
7e0cb8e
f5cf831
8d2d2ac
42f0f6c
30f2d25
4a3cd7d
1ae5769
28bd930
cba6cb1
58cc010
6787b14
7695c4f
2e65519
cc373c9
c0e19aa
81b69ff
1b1e17f
3fbf0a9
5df805d
4208917
c006454
a2aabfe
4a19ace
161f081
6cb3677
f15339c
5d08798
7557592
a945c0b
1a1d063
9800fb8
07bb9e5
8aa9a1b
9acca96
5ada640
86d7212
e988fc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. functions in this cache file seems no error handling and logging logic. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package cache | ||
|
||
import ( | ||
"math/big" | ||
|
||
"github.com/ExocoreNetwork/exocore/x/oracle/keeper/common" | ||
"github.com/ExocoreNetwork/exocore/x/oracle/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
var zeroBig = big.NewInt(0) | ||
|
||
type CacheItemV map[string]*big.Int | ||
type CacheItemP *common.Params | ||
type CacheItemM struct { | ||
FeederId int32 | ||
PSources []*types.PriceWithSource | ||
Validator string | ||
} | ||
|
||
type Cache struct { | ||
msg cacheMsgs | ||
validators *cacheValidator | ||
params *cacheParams | ||
} | ||
|
||
type cacheMsgs map[int32][]*CacheItemM | ||
|
||
// used to track validator change | ||
type cacheValidator struct { | ||
validators map[string]*big.Int | ||
update bool | ||
} | ||
|
||
// used to track params change | ||
type cacheParams struct { | ||
params *common.Params | ||
update bool | ||
} | ||
|
||
func (c cacheMsgs) add(item *CacheItemM) { | ||
if ims, ok := c[item.FeederId]; ok { | ||
for _, im := range ims { | ||
if im.Validator == item.Validator { | ||
for _, p := range im.PSources { | ||
for _, pInput := range item.PSources { | ||
if p.SourceId == pInput.SourceId { | ||
p.Prices = append(p.Prices, pInput.Prices...) | ||
return | ||
} | ||
} | ||
} | ||
im.PSources = append(im.PSources, item.PSources...) | ||
return | ||
} | ||
} | ||
} | ||
c[item.FeederId] = append(c[item.FeederId], item) | ||
} | ||
|
||
func (c cacheMsgs) remove(item *CacheItemM) { | ||
delete(c, item.FeederId) | ||
} | ||
|
||
func (c cacheMsgs) commit(ctx sdk.Context, k common.KeeperOracle) { | ||
block := uint64(ctx.BlockHeight()) | ||
recentMsgs := types.RecentMsg{ | ||
Block: block, | ||
Msgs: make([]*types.MsgItem, 0), | ||
} | ||
for _, msgs4Feeder := range c { | ||
for _, msg := range msgs4Feeder { | ||
recentMsgs.Msgs = append(recentMsgs.Msgs, &types.MsgItem{ | ||
FeederId: msg.FeederId, | ||
PSources: msg.PSources, | ||
Validator: msg.Validator, | ||
}) | ||
} | ||
} | ||
index, _ := k.GetIndexRecentMsg(ctx) | ||
for i, b := range index.Index { | ||
if b >= block-common.MaxNonce { | ||
index.Index = index.Index[i:] | ||
break | ||
} | ||
k.RemoveRecentMsg(ctx, b) | ||
} | ||
k.SetRecentMsg(ctx, recentMsgs) | ||
index.Index = append(index.Index, block) | ||
k.SetIndexRecentMsg(ctx, index) | ||
} | ||
|
||
func (c *cacheValidator) add(validators map[string]*big.Int) { | ||
for operator, newPower := range validators { | ||
if power, ok := c.validators[operator]; ok { | ||
if newPower.Cmp(zeroBig) == 0 { | ||
delete(c.validators, operator) | ||
c.update = true | ||
} else if power.Cmp(newPower) != 0 { | ||
c.validators[operator].Set(newPower) | ||
c.update = true | ||
} | ||
} else { | ||
c.update = true | ||
np := *newPower | ||
c.validators[operator] = &np | ||
} | ||
} | ||
|
||
} | ||
|
||
func (c *cacheValidator) commit(ctx sdk.Context, k common.KeeperOracle) { | ||
block := uint64(ctx.BlockHeight()) | ||
k.SetValidatorUpdateBlock(ctx, types.ValidatorUpdateBlock{Block: block}) | ||
} | ||
|
||
func (c *cacheParams) add(p *common.Params) { | ||
//params' update is triggered when params is actually updated, so no need to do comparison here, just udpate and mark the flag | ||
//TODO: add comparison check, that's something should be done for validation | ||
c.params = p | ||
c.update = true | ||
} | ||
|
||
func (c *cacheParams) commit(ctx sdk.Context, k common.KeeperOracle) { | ||
block := uint64(ctx.BlockHeight()) | ||
index, _ := k.GetIndexRecentParams(ctx) | ||
for i, b := range index.Index { | ||
if b >= block-common.MaxNonce { | ||
index.Index = index.Index[i:] | ||
break | ||
} | ||
k.RemoveRecentParams(ctx, b) | ||
} | ||
//remove and append for KVStore | ||
k.SetIndexRecentParams(ctx, index) | ||
index.Index = append(index.Index, block) | ||
k.SetIndexRecentParams(ctx, index) | ||
} | ||
|
||
// memory cache | ||
// func (c *Cache) AddCache(i any, k common.KeeperOracle) { | ||
func (c *Cache) AddCache(i any) { | ||
switch item := i.(type) { | ||
case *CacheItemM: | ||
c.msg.add(item) | ||
// case *params: | ||
case CacheItemP: | ||
c.params.add(item) | ||
case CacheItemV: | ||
c.validators.add(item) | ||
default: | ||
panic("no other types are support") | ||
Check warning Code scanning / CodeQL Panic in BeginBock or EndBlock consensus methods Warning
Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
|
||
} | ||
} | ||
|
||
// func (c *Cache) RemoveCache(i any, k common.KeeperOracle) { | ||
func (c *Cache) RemoveCache(i any) { | ||
switch item := i.(type) { | ||
case *CacheItemM: | ||
c.msg.remove(item) | ||
default: | ||
} | ||
} | ||
|
||
func (c *Cache) GetCache(i any) bool { | ||
switch item := i.(type) { | ||
case CacheItemV: | ||
if item == nil { | ||
return false | ||
} | ||
for addr, power := range c.validators.validators { | ||
item[addr] = power | ||
} | ||
Comment on lines
+170
to
+172
Check warning Code scanning / CodeQL Iteration over map Warning
Iteration over map may be a possible source of non-determinism
|
||
case CacheItemP: | ||
//fmt.Println("debug ", c.params.params) | ||
if item == nil { | ||
return false | ||
} | ||
*item = *(c.params.params) | ||
case *[]*CacheItemM: | ||
if item == nil { | ||
return false | ||
} | ||
// fmt.Println("debug getCacheM", c.msg) | ||
tmp := make([]*CacheItemM, 0, len(c.msg)) | ||
for _, msgs := range c.msg { | ||
tmp = append(tmp, msgs...) | ||
} | ||
*item = tmp | ||
default: | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func (c *Cache) CommitCache(ctx sdk.Context, reset bool, k common.KeeperOracle) { | ||
if len(c.msg) > 0 { | ||
c.msg.commit(ctx, k) | ||
c.msg = make(map[int32][]*CacheItemM) | ||
} | ||
|
||
if c.validators.update { | ||
c.validators.commit(ctx, k) | ||
c.validators.update = false | ||
} | ||
|
||
if c.params.update { | ||
c.params.commit(ctx, k) | ||
c.params.update = false | ||
} | ||
if reset { | ||
c.ResetCaches() | ||
} | ||
} | ||
|
||
func (c *Cache) ResetCaches() { | ||
*c = *(NewCache()) | ||
} | ||
|
||
func NewCache() *Cache { | ||
return &Cache{ | ||
msg: make(map[int32][]*CacheItemM), | ||
validators: &cacheValidator{ | ||
validators: make(map[string]*big.Int), | ||
}, | ||
params: &cacheParams{ | ||
params: &common.Params{}, | ||
}, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package cache | ||
|
||
import ( | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/ExocoreNetwork/exocore/x/oracle/keeper/common" | ||
"github.com/ExocoreNetwork/exocore/x/oracle/types" | ||
. "github.com/smartystreets/goconvey/convey" | ||
// "go.uber.org/mock/gomock" | ||
) | ||
|
||
func TestCache(t *testing.T) { | ||
c := NewCache() | ||
p := defaultParams | ||
pWrapped := common.Params(p) | ||
|
||
// ctrl := gomock.NewController(t) | ||
// defer ctrl.Finish() | ||
//ko := common.NewMockKeeperOracle(ctrl) | ||
//c.AddCache(CacheItemP(&pWrapped), ko) | ||
|
||
Convey("test cache", t, func() { | ||
Convey("add pramams item", func() { | ||
c.AddCache(CacheItemP(&pWrapped)) | ||
pReturn := &common.Params{} | ||
c.GetCache(CacheItemP(pReturn)) | ||
So(*pReturn, ShouldResemble, pWrapped) | ||
}) | ||
|
||
Convey("add validatorPower item", func() { | ||
validatorPowers := map[string]*big.Int{ | ||
"v1": big.NewInt(100), | ||
"v2": big.NewInt(109), | ||
"v3": big.NewInt(119), | ||
} | ||
c.AddCache(CacheItemV(validatorPowers)) | ||
vpReturn := make(map[string]*big.Int) | ||
Convey("for empty cache", func() { | ||
c.GetCache(CacheItemV(vpReturn)) | ||
So(vpReturn, ShouldResemble, validatorPowers) | ||
}) | ||
Convey("then update validatorPower item for this cache", func() { | ||
validaotrPowers := map[string]*big.Int{ | ||
//add v5 | ||
"v5": big.NewInt(123), | ||
//remove v1 | ||
"v1": big.NewInt(0), | ||
//update v2 | ||
"v2": big.NewInt(199), | ||
} | ||
c.AddCache(CacheItemV(validaotrPowers)) | ||
c.GetCache(CacheItemV(vpReturn)) | ||
So(vpReturn, ShouldNotContainKey, "v1") | ||
So(vpReturn, ShouldContainKey, "v5") | ||
So(vpReturn["v2"], ShouldResemble, big.NewInt(199)) | ||
}) | ||
}) | ||
|
||
Convey("add msg item", func() { | ||
msgItems := []*CacheItemM{ | ||
{ | ||
FeederId: 1, | ||
PSources: []*types.PriceWithSource{ | ||
{ | ||
SourceId: 1, | ||
Prices: []*types.PriceWithTimeAndDetId{ | ||
{Price: "600000", Decimal: 1, Timestamp: "-", DetId: "1"}, {Price: "620000", Decimal: 1, Timestamp: "-", DetId: "2"}, | ||
}, | ||
}, | ||
}, | ||
Validator: "v1", | ||
}, | ||
{ | ||
FeederId: 1, | ||
PSources: []*types.PriceWithSource{ | ||
{SourceId: 1, Prices: []*types.PriceWithTimeAndDetId{{Price: "600000", Decimal: 1, Timestamp: "-", DetId: "4"}, {Price: "620000", Decimal: 1, Timestamp: "-", DetId: "3"}}}}, | ||
Validator: "v1", | ||
}, | ||
{ | ||
FeederId: 2, | ||
PSources: []*types.PriceWithSource{{SourceId: 1, Prices: []*types.PriceWithTimeAndDetId{{Price: "30000", Decimal: 1, Timestamp: "-", DetId: "4"}, {Price: "32000", Decimal: 1, Timestamp: "-", DetId: "3"}}}}, | ||
Validator: "v2", | ||
}, | ||
} | ||
c.AddCache(msgItems[0]) | ||
msgItemsReturn := make([]*CacheItemM, 0, 3) | ||
Convey("add single item", func() { | ||
c.GetCache(&msgItemsReturn) | ||
So(msgItemsReturn, ShouldContain, msgItems[0]) | ||
}) | ||
Convey("add more items", func() { | ||
c.AddCache(msgItems[1]) | ||
c.AddCache(msgItems[2]) | ||
c.GetCache(&msgItemsReturn) | ||
So(msgItemsReturn, ShouldContain, msgItems[2]) | ||
So(msgItemsReturn, ShouldNotContain, msgItems[0]) | ||
}) | ||
}) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package cache | ||
|
||
import "github.com/ExocoreNetwork/exocore/x/oracle/types" | ||
|
||
var defaultParams = types.Params{ | ||
Chains: []*types.Chain{{Name: "-", Desc: "-"}, {Name: "Ethereum", Desc: "-"}}, | ||
Tokens: []*types.Token{{}, {Name: "eth", ChainId: 1, ContractAddress: "0xabc", Decimal: 18, Active: true}}, | ||
Sources: []*types.Source{{}, {Name: "chainLink", Entry: &types.Endpoint{}, Valid: true, Deterministic: true}}, | ||
Rules: []*types.RuleWithSource{{}, {SourceIds: []int32{1}}}, | ||
TokenFeeders: []*types.TokenFeeder{{}, {TokenId: 1, RuleId: 1, StartRoundId: 1, StartBaseBlock: 0, Interval: 10, EndBlock: 0}}, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im.PSources = append(im.PSources, item.PSources...)
This case is If there are new PSources to be added, it will add the entire PSources array to the existing entry, not just the new SourceId. If if the new PSources array contains some new and some old (already existing in the current entry) SourceIds, then the price sources associated with the old SourceId will be added repeatedly; over time, if new PSources are added frequently, the PSources array in each entry may become very large, affecting data processing and query efficiency。