Skip to content

Commit

Permalink
test(oracle):add test cases, events, logs
Browse files Browse the repository at this point in the history
  • Loading branch information
leonz789 committed Apr 9, 2024
1 parent 7670fe0 commit 052bf5e
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 73 deletions.
2 changes: 1 addition & 1 deletion testutil/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (suite *BaseTestSuite) SetupTest() {
suite.DoSetupTest()
}

// SetupWithGenesisValSet initializes a new EvmosApp with a validator set and genesis accounts
// SetupWithGenesisValSet initializes a new ExocoreApp with a validator set and genesis accounts
// that also act as delegators. For simplicity, each validator is bonded with a delegation
// of one consensus engine unit (10^6) in the default token of the simapp from first genesis
// account. A Nop logger is set in SimApp.
Expand Down
34 changes: 9 additions & 25 deletions x/oracle/keeper/aggregator/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

type cacheItemM struct {
feederId int32
pSources []*types.PriceWithSource
validator string
}

type priceItemKV struct {
TokenId int32
PriceTR types.PriceWithTimeAndRound
Expand Down Expand Up @@ -53,6 +47,7 @@ func (agc *AggregatorContext) sanityCheck(msg *types.MsgCreatePrice) error {
//TODO: check the msgCreatePrice's Decimal is correct with params setting
//TODO: check len(price.prices)>0, len(price.prices._range_eachPriceWithSource.Prices)>0, at least has one source, and for each source has at least one price
//TODO: check for each source, at most maxDetId count price (now in filter, ->anteHandler)

if agc.validatorsPower[msg.Creator] == nil {
return errors.New("signer is not validator")
}
Expand All @@ -72,20 +67,18 @@ func (agc *AggregatorContext) sanityCheck(msg *types.MsgCreatePrice) error {
}
//check with params is coressponding source is deteministic
if agc.params.IsDeterministicSource(pSource.SourceId) {
for _, pDetId := range pSource.Prices {
for _, pDetID := range pSource.Prices {
//TODO: verify the format of DetId is correct, since this is string, and we will make consensus with validator's power, so it's ok not to verify the format
//just make sure the DetId won't mess up with NS's placeholder id, the limitation of maximum count one validator can submit will be check by filter
if len(pDetId.DetId) == 0 {
if len(pDetID.DetId) == 0 {
//deterministic must have specified deterministicId
return errors.New("ds should have roundid")
}
//DS's price value will go through consensus process, so it's safe to skip the check here
}
} else {
//sanity check: NS submit only one price with detId==""
if len(pSource.Prices) > 1 || len(pSource.Prices[0].DetId) > 0 {
return errors.New("ns should not have roundid")
}
} else if len(pSource.Prices) > 1 || len(pSource.Prices[0].DetId) > 0 {
return errors.New("ns should not have roundid")
}
}
return nil
Expand Down Expand Up @@ -115,7 +108,6 @@ func (agc *AggregatorContext) checkMsg(msg *types.MsgCreatePrice) error {
}

func (agc *AggregatorContext) FillPrice(msg *types.MsgCreatePrice) (*priceItemKV, *cache.CacheItemM, error) {
// fmt.Println("debug agc fillprice")
feederWorker := agc.aggregators[msg.FeederId]
//worker initialzed here reduce workload for Endblocker
if feederWorker == nil {
Expand All @@ -124,7 +116,7 @@ func (agc *AggregatorContext) FillPrice(msg *types.MsgCreatePrice) (*priceItemKV
}

if feederWorker.sealed {
return nil, nil, errors.New("")
return nil, nil, types.ErrPriceProposalIgnored.Wrap("price aggregation for this round has sealed")
}

if listFilled := feederWorker.do(msg); listFilled != nil {
Expand All @@ -142,18 +134,16 @@ func (agc *AggregatorContext) FillPrice(msg *types.MsgCreatePrice) (*priceItemKV
return nil, &cache.CacheItemM{msg.FeederId, listFilled, msg.Creator}, nil
}

return nil, nil, errors.New("")
//return nil, nil, errors.New("no valid price proposal to add for aggregation")
return nil, nil, types.ErrPriceProposalIgnored
}

// NewCreatePrice receives msgCreatePrice message, and goes process: filter->aggregator, filter->calculator->aggregator
// non-deterministic data will goes directly into aggregator, and deterministic data will goes into calculator first to get consensus on the deterministic id.
func (agc *AggregatorContext) NewCreatePrice(ctx sdk.Context, msg *types.MsgCreatePrice) (*priceItemKV, *cache.CacheItemM, error) {
// fmt.Println("debug agc.newcreateprice")
if err := agc.checkMsg(msg); err != nil {
// fmt.Println("debug agc.newcreateprice.error", err)
return nil, nil, err
return nil, nil, types.ErrInvalidMsg.Wrap(err.Error())
}
// fmt.Println("debug before agc.fillprice")
return agc.FillPrice(msg)
}

Expand All @@ -176,7 +166,6 @@ func (agc *AggregatorContext) SealRound(ctx sdk.Context, force bool) (success []
expired := feeder.EndBlock > 0 && ctx.BlockHeight() >= feeder.EndBlock
outOfWindow := uint64(ctx.BlockHeight())-round.basedBlock >= uint64(common.MaxNonce)

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
if expired || outOfWindow || force {
//TODO: WRITE TO KVSTORE with previous round data for this round
failed = append(failed, feeder.TokenId)
if expired {
delete(agc.rounds, feederId)
Expand Down Expand Up @@ -206,21 +195,16 @@ func (agc *AggregatorContext) PrepareRound(ctx sdk.Context, block uint64) {
block = uint64(ctx.BlockHeight())

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
}

// fmt.Println("debug agc.prepareround, height:", block)
for feederId, feeder := range agc.params.GetTokenFeeders() {
if feederId == 0 {
continue
}
// fmt.Println("debug agc.prepareround, feederId:", feederId)
if (feeder.EndBlock > 0 && uint64(feeder.EndBlock) <= block) || uint64(feeder.StartBaseBlock) > block {

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion

// fmt.Println("debug agc.prepareround 2, feederId:", feederId, feeder.StartBaseBlock, block)
//this feeder is inactive
continue
}

// fmt.Println("debug agc.prepareround 3, feederId:", feederId)

delta := (block - uint64(feeder.StartBaseBlock))

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
left := delta % uint64(feeder.Interval)

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
count := delta / uint64(feeder.Interval)

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
Expand Down
2 changes: 0 additions & 2 deletions x/oracle/keeper/aggregator/aggregator_aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,13 @@ func (agg *aggregator) aggregate() *big.Int {
if agg.finalPrice != nil {
return agg.finalPrice
}
// fmt.Printf("debug aggregator.aggregate(), reportPower:%s, totalPower:%s\n", agg.reportPower.String(), agg.totalPower.String())
//TODO: implemetn different MODE for definition of consensus,
//currently: use rule_1+MODE_1: {rule:specified source:`chainlink`, MODE: asap when power exceeds the threshold}
//1. check OVA threshold
//2. check IVA consensus with rule, TODO: for v1 we only implement with mode=1&rule=1
if common.ExceedsThreshold(agg.reportPower, agg.totalPower) {
//TODO: this is kind of a mock way to suite V1, need update to check with params.rule
//check if IVA all reached consensus
// fmt.Printf("debug aggregator.aggregate() len(dsPrice):%d\n", len(agg.dsPrices))
if len(agg.dsPrices) > 0 {
validatorPrices := make([]*big.Int, 0, len(agg.reports))
//do the aggregation to find out the 'final price'
Expand Down
3 changes: 0 additions & 3 deletions x/oracle/keeper/aggregator/aggregator_calculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ func (c *calculator) getOrNewSourceId(sourceId int32) *roundPricesList {
// fillPrice called upon new MsgCreatPrice arrived, to trigger the calculation to get to consensus on the same roundID_of_deterministic_source
// v1 use mode1, TODO: switch modes
func (c *calculator) fillPrice(pSources []*types.PriceWithSource, validator string, power *big.Int) (confirmedRounds []*confirmedPrice) {
// fmt.Println("debug calculator.fillPrice, calculator.ds[1]", c.deterministicSource[1], pSources)
for _, pSource := range pSources {
rounds := c.getOrNewSourceId(pSource.SourceId)
if rounds.hasConfirmedDetId() {
Expand All @@ -145,7 +144,6 @@ func (c *calculator) fillPrice(pSources []*types.PriceWithSource, validator stri

roundPrice, _ := new(big.Int).SetString(pDetId.Price, 10)

// fmt.Printf("debug calculator.fillPrice before updatePriceAndPower. power%s, price%s\n", power.String(), roundPrice.String())
updated, confirmed := round.updatePriceAndPower(&priceAndPower{roundPrice, power}, c.totalPower)
if updated && confirmed {
//sourceId, detId, price
Expand All @@ -155,7 +153,6 @@ func (c *calculator) fillPrice(pSources []*types.PriceWithSource, validator stri
}
}
}
// fmt.Println("debug calculator.fillPrice, after calculator.ds[1]", c.deterministicSource[1])
return
}

Expand Down
2 changes: 0 additions & 2 deletions x/oracle/keeper/aggregator/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ func (w *worker) do(msg *types.MsgCreatePrice) []*types.PriceWithSource {
list4Calculator, list4Aggregator := w.f.filtrate(msg)
if list4Aggregator != nil {
w.a.fillPrice(list4Aggregator, validator, power)
// fmt.Printf("debug worker.do after fill aggregator\n")
if confirmedRounds := w.c.fillPrice(list4Calculator, validator, power); confirmedRounds != nil {
// fmt.Printf("debug worker.do after fill calculator and have confirmedRounds\n")
w.a.confirmDSPrice(confirmedRounds)
}
}
Expand Down
2 changes: 0 additions & 2 deletions x/oracle/keeper/cache/caches.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ func (c *Cache) GetCache(i any) bool {
item[addr] = power
}

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
}
Expand All @@ -180,7 +179,6 @@ func (c *Cache) GetCache(i any) bool {
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...)
Expand Down
5 changes: 0 additions & 5 deletions x/oracle/keeper/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,8 @@ func (p Params) CheckRules(feederId int32, prices []*types.PriceWithSource) (boo
if source.Valid {
notFound = true
for _, p := range prices {
// fmt.Println("debug rules check _0, p.sourceid", p.SourceId)
if p.SourceId == int32(sId) {

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
notFound = false
// fmt.Println("debug rules match _0")
break
}
}
Expand All @@ -88,18 +86,15 @@ func (p Params) CheckRules(feederId int32, prices []*types.PriceWithSource) (boo
for _, source := range rule.SourceIds {
notFound = true
for _, p := range prices {
// fmt.Println("debug rules check, p.sourceid", p.SourceId)
if p.SourceId == source {
notFound = false
// fmt.Println("debug rules match")
break
}
}
//return false, errors.New("price source not match with rule")
}
}
if notFound {
// fmt.Println("debug rules check, rule.sources", rule.SourceIds)
return false, errors.New("price source not match with rule")
}
}
Expand Down
35 changes: 26 additions & 9 deletions x/oracle/keeper/keeper_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@ import (
"context"
"testing"

"github.com/ExocoreNetwork/exocore/testutil"
"github.com/ExocoreNetwork/exocore/x/oracle/keeper"
"github.com/ExocoreNetwork/exocore/x/oracle/types"
sdk "github.com/cosmos/cosmos-sdk/types"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/suite"
gomock "go.uber.org/mock/gomock"
)

type KeeperSuite struct {
t *testing.T
k keeper.Keeper
ctx sdk.Context
ms types.MsgServer
ctrl *gomock.Controller
testutil.BaseTestSuite

t *testing.T
k keeper.Keeper
ctx sdk.Context
ms types.MsgServer
ctrl *gomock.Controller
valAddr1 sdk.ValAddress
valAddr2 sdk.ValAddress
}

var ks *KeeperSuite
Expand All @@ -29,13 +36,23 @@ func TestKeeper(t *testing.T) {
ks.ctx = sdk.UnwrapSDKContext(ctxW)
ks.t = t

suite.Run(t, ks)

RegisterFailHandler(Fail)
RunSpecs(t, "Keeper Suite")
}

func (k *KeeperSuite) Reset() {
func (suite *KeeperSuite) Reset() {
var ctxW context.Context
k.ms, ctxW, k.k = setupMsgServer(k.t)
k.ctx = sdk.UnwrapSDKContext(ctxW)
k.ctrl = gomock.NewController(k.t)
suite.ms, ctxW, suite.k = setupMsgServer(suite.t)
suite.ctx = sdk.UnwrapSDKContext(ctxW)
suite.ctrl = gomock.NewController(suite.t)
}

func (suite *KeeperSuite) SetupTest() {
suite.DoSetupTest()
suite.valAddr1, _ = sdk.ValAddressFromBech32(suite.Validators[0].OperatorAddress)
suite.valAddr2, _ = sdk.ValAddressFromBech32(suite.Validators[1].OperatorAddress)
keeper.ResetAggregatorContext()
keeper.ResetCache()
}
40 changes: 30 additions & 10 deletions x/oracle/keeper/msg_server_create_price.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,46 @@ package keeper

import (
"context"
"strconv"

"github.com/ExocoreNetwork/exocore/x/oracle/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// CreatePrice proposes price for new round of specific tokenFeeder
func (k msgServer) CreatePrice(goCtx context.Context, msg *types.MsgCreatePrice) (*types.MsgCreatePriceResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
/**
1. aggregator.rInfo.Tokenid->status == 0(1 ignore and return)
2. basedBlock is valid [roundInfo.basedBlock, *+5], each base only allow for one submit each validator, window for submition is 5 blocks while every validator only allowed to submit at most 3 transactions each round
3. check the rule fulfilled(sources check), check the decimal of the 1st mathc the params' definition(among prices the decimal had been checked in ante stage), timestamp:later than previous block's timestamp, [not future than now(+1s), this is checked in anteHandler], timestamp verification is not necessary
**/

//newItem, caches, _ := k.GetAggregatorContext(ctx, k.Keeper).NewCreatePrice(ctx, msg)
newItem, caches, _ := GetAggregatorContext(ctx, k.Keeper).NewCreatePrice(ctx, msg)
// fmt.Println("debug after NewCreatePrice", newItem, caches)

newItem, caches, err := GetAggregatorContext(ctx, k.Keeper).NewCreatePrice(ctx, msg)
if err != nil {
return nil, err
}

logger := k.Keeper.Logger(ctx)
logger.Info("add price proposal for aggregation", "feederID", msg.FeederId, "basedBlock", msg.BasedBlock, "proposer", msg.Creator)

ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeCreatePrice,
sdk.NewAttribute(types.AttributeKeyFeederID, strconv.Itoa(int(msg.FeederId))),
sdk.NewAttribute(types.AttributeKeyBasedBlock, strconv.FormatInt(int64(msg.BasedBlock), 10)),

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
sdk.NewAttribute(types.AttributeKeyProposer, msg.Creator),
),
)

if caches != nil {
if newItem != nil {
k.AppendPriceTR(ctx, newItem.TokenId, newItem.PriceTR)
//TODO: move related caches

logger.Info("final price aggregation done", "feederID", msg.FeederId, "roundID", newItem.PriceTR.RoundId, "price", newItem.PriceTR.Price)

ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeCreatePrice,
sdk.NewAttribute(types.AttributeKeyFeederID, strconv.Itoa(int(msg.FeederId))),
sdk.NewAttribute(types.AttributeKeyRoundID, strconv.FormatInt(int64(newItem.PriceTR.RoundId), 10)),

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion Error

Potential integer overflow by integer type conversion
sdk.NewAttribute(types.AttributeKeyFinalPrice, newItem.PriceTR.Price),
sdk.NewAttribute(types.AttributeKeyPriceUpdated, types.AttributeValuePriceUpdatedSuccess),
),
)
cs.RemoveCache(caches)
} else {
cs.AddCache(caches)
Expand Down
1 change: 0 additions & 1 deletion x/oracle/keeper/msg_server_create_price_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ var _ = Describe("MsgCreatePrice", func() {
c.GetCache(&iRes)
Expect(len(iRes)).Should(Equal(0))
prices := ks.k.GetAllPrices(sdk.UnwrapSDKContext(ks.ctx))
//fmt.Println("GetAllPrices", prices[0])
Expect(prices[0]).Should(BeEquivalentTo(types.Prices{
TokenId: 1,
NextRountId: 2,
Expand Down
Loading

0 comments on commit 052bf5e

Please sign in to comment.