From 0967104ea32817b9d88a58bc86a34f643d61cb67 Mon Sep 17 00:00:00 2001 From: leonz789 Date: Wed, 13 Nov 2024 10:21:45 +0800 Subject: [PATCH] add oracle test cases --- tests/e2e/oracle/create-price.go | 67 --------- tests/e2e/oracle/create_price.go | 184 +++++++++++++++++++++++ tests/e2e/oracle/data.go | 16 ++ tests/e2e/oracle/oracle_test.go | 4 +- testutil/network/network.go | 2 +- x/oracle/keeper/aggregator/aggregator.go | 12 +- x/oracle/keeper/aggregator/calculator.go | 4 +- x/oracle/keeper/aggregator/util.go | 10 ++ 8 files changed, 221 insertions(+), 78 deletions(-) delete mode 100644 tests/e2e/oracle/create-price.go create mode 100644 tests/e2e/oracle/create_price.go create mode 100644 x/oracle/keeper/aggregator/util.go diff --git a/tests/e2e/oracle/create-price.go b/tests/e2e/oracle/create-price.go deleted file mode 100644 index 388ae149b..000000000 --- a/tests/e2e/oracle/create-price.go +++ /dev/null @@ -1,67 +0,0 @@ -package oracle - -import ( - "context" - - oracletypes "github.com/ExocoreNetwork/exocore/x/oracle/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -const layout = "2006-01-02 15:04:05" - -func (s *E2ETestSuite) TestCreatePriceLST() { - kr := s.network.Validators[0].ClientCtx.Keyring - creator := sdk.AccAddress(s.network.Validators[0].PubKey.Address()) - priceTest1 := price1.updateTimestamp() - priceTimeDetID := priceTest1.getPriceTimeDetID("9") - priceSource := oracletypes.PriceSource{ - SourceID: 1, - Prices: []*oracletypes.PriceTimeDetID{ - &priceTimeDetID, - }, - } - msg := oracletypes.NewMsgCreatePrice(creator.String(), 1, []*oracletypes.PriceSource{&priceSource}, 10, 1) - - s.moveToAndCheck(10) - - // send create-price from validator-0 - err := s.network.SendTxOracleCreateprice([]sdk.Msg{msg}, "valconskey0", kr) - s.Require().NoError(err) - - // final price not aggregated - _, err = s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) - errStatus, _ := status.FromError(err) - s.Require().Equal(codes.NotFound, errStatus.Code()) - - s.moveToAndCheck(11) - // send create-price from validator-1 - err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg}, "valconskey1", kr) - - s.moveToAndCheck(12) - - res, err := s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) - s.Require().NoError(err) - s.Require().Equal(priceTest1.getPriceTimeRound(1), res.Price) -} - -func (s *E2ETestSuite) TestCreatePriceNST() { - -} - -func (s *E2ETestSuite) TestSlashing() { - -} - -func (s *E2ETestSuite) moveToAndCheck(height int64) { - _, err := s.network.WaitForHeight(height) - s.Require().NoError(err) -} - -func (s *E2ETestSuite) moveNAndCheck(n int64) { - for i := int64(0); i < n; i++ { - err := s.network.WaitForNextBlock() - s.Require().NoError(err) - } -} diff --git a/tests/e2e/oracle/create_price.go b/tests/e2e/oracle/create_price.go new file mode 100644 index 000000000..cb8e5b7e3 --- /dev/null +++ b/tests/e2e/oracle/create_price.go @@ -0,0 +1,184 @@ +package oracle + +import ( + "context" + "time" + + oracletypes "github.com/ExocoreNetwork/exocore/x/oracle/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const layout = "2006-01-02 15:04:05" + +/* + cases: + we need more than 2/3 power, so that at least 3 out of 4 validators power should be enough + 1. block_1_1: v1 sendPrice{p1}, [no round_1 price after block_1_1 committed], block_1_2:v2&v3 sendPrice{p1}, [got round_1 price{p1} after block_1_2 committed] + 2. block_2_1: v3 sendPrice{p2}, block_2_2: v1 sendPrice{p2}, [no round_2 price after block_2_2 committed], block_2_3:nothing, [got round_2 price{p1} equals to round_1 after block_2_3 committed] + 3. block_3_1: v1 sendPrice{p1}, block_3_2: v2&v3 sendPrice{p2}, block_3_3: v3 sendPrice{p2}, [got final price{p2} after block_3_3 committed] + 4. block_4_1: v1&v2&v3 sendPrice{p1}, [got round_4 price{p1} after block_4_1 committed]] + + --- nonce: +*/ + +func (s *E2ETestSuite) TestCreatePriceLST() { + kr0 := s.network.Validators[0].ClientCtx.Keyring + creator0 := sdk.AccAddress(s.network.Validators[0].PubKey.Address()) + + kr1 := s.network.Validators[1].ClientCtx.Keyring + creator1 := sdk.AccAddress(s.network.Validators[1].PubKey.Address()) + + kr2 := s.network.Validators[2].ClientCtx.Keyring + creator2 := sdk.AccAddress(s.network.Validators[2].PubKey.Address()) + + // kr3 := s.network.Validators[2].ClientCtx.Keyring + // creator3 := sdk.AccAddress(s.network.Validators[2].PubKey.Address()) + + priceTest1R1 := price1.updateTimestamp() + priceTimeDetID1R1 := priceTest1R1.getPriceTimeDetID("9") + priceSource1R1 := oracletypes.PriceSource{ + SourceID: 1, + Prices: []*oracletypes.PriceTimeDetID{ + &priceTimeDetID1R1, + }, + } + + // case_1. + s.moveToAndCheck(10) + // send create-price from validator-0 + msg0 := oracletypes.NewMsgCreatePrice(creator0.String(), 1, []*oracletypes.PriceSource{&priceSource1R1}, 10, 1) + err := s.network.SendTxOracleCreateprice([]sdk.Msg{msg0}, "valconskey0", kr0) + s.Require().NoError(err) + + // query final price + _, err = s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) + errStatus, _ := status.FromError(err) + s.Require().Equal(codes.NotFound, errStatus.Code()) + + s.moveToAndCheck(11) + // send create-price from validator-1 + msg1 := oracletypes.NewMsgCreatePrice(creator1.String(), 1, []*oracletypes.PriceSource{&priceSource1R1}, 10, 1) + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg1}, "valconskey1", kr1) + s.Require().NoError(err) + + // send create-price from validator-2 + msg2 := oracletypes.NewMsgCreatePrice(creator2.String(), 1, []*oracletypes.PriceSource{&priceSource1R1}, 10, 1) + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg2}, "valconskey2", kr2) + s.Require().NoError(err) + + s.moveToAndCheck(12) + // query final price + res, err := s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) + s.Require().NoError(err) + s.Require().Equal(priceTest1R1.getPriceTimeRound(1), res.Price) + + // case_2. + // timestamp need to be updated + priceTest2R2 := price2.updateTimestamp() + priceTimeDetID2R2 := priceTest2R2.getPriceTimeDetID("10") + priceSource2R2 := oracletypes.PriceSource{ + SourceID: 1, + Prices: []*oracletypes.PriceTimeDetID{ + &priceTimeDetID2R2, + }, + } + msg0 = oracletypes.NewMsgCreatePrice(creator0.String(), 1, []*oracletypes.PriceSource{&priceSource2R2}, 20, 1) + msg2 = oracletypes.NewMsgCreatePrice(creator2.String(), 1, []*oracletypes.PriceSource{&priceSource2R2}, 20, 1) + + s.moveToAndCheck(20) + // send price{p2} from validator-2 + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg2}, "valconskey2", kr2) + s.Require().NoError(err) + s.moveToAndCheck(21) + // send price{p2} from validator-0 + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg0}, "valconskey0", kr0) + s.Require().NoError(err) + s.moveToAndCheck(24) + res, err = s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) + s.Require().NoError(err) + // price update fail, round 2 still have price{p1} + s.Require().Equal(priceTest1R1.getPriceTimeRound(2), res.Price) + + // case_3. + // update timestamp + priceTest2R3 := price2.updateTimestamp() + priceTimeDetID2R3 := priceTest2R3.getPriceTimeDetID("11") + priceSource2R3 := oracletypes.PriceSource{ + SourceID: 1, + Prices: []*oracletypes.PriceTimeDetID{ + &priceTimeDetID2R3, + }, + } + + msg0 = oracletypes.NewMsgCreatePrice(creator0.String(), 1, []*oracletypes.PriceSource{&priceSource2R3}, 30, 1) + msg1 = oracletypes.NewMsgCreatePrice(creator1.String(), 1, []*oracletypes.PriceSource{&priceSource2R3}, 30, 1) + msg2 = oracletypes.NewMsgCreatePrice(creator2.String(), 1, []*oracletypes.PriceSource{&priceSource2R3}, 30, 1) + s.moveToAndCheck(30) + // send price{p2} from validator-0 + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg0}, "valconskey0", kr0) + s.Require().NoError(err) + s.moveToAndCheck(31) + // send price{p2} from validator-1 + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg1}, "valconskey1", kr1) + s.Require().NoError(err) + + // send price{p2} from validator-2 + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg2}, "valconskey2", kr2) + s.Require().NoError(err) + + s.moveToAndCheck(32) + res, err = s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) + s.Require().NoError(err) + // price updated, round 3 has price{p2} + s.Require().Equal(priceTest2R3.getPriceTimeRound(3), res.Price) + + // case_4. + // update timestamp + // priceTest1R4 := price2.updateTimestamp() + // priceTimeDetID2R3 := priceTest2R3.getPriceTimeDetID("10") + // priceSource2R3 := oracletypes.PriceSource{ + // SourceID: 1, + // Prices: []*oracletypes.PriceTimeDetID{ + // &priceTimeDetID2R3, + // }, + // } + s.moveToAndCheck(40) + priceTest1R4, priceSource1R4 := price1.generateRealTimeStructs("12", 1) + msg0 = oracletypes.NewMsgCreatePrice(creator0.String(), 1, []*oracletypes.PriceSource{&priceSource1R4}, 40, 1) + msg1 = oracletypes.NewMsgCreatePrice(creator1.String(), 1, []*oracletypes.PriceSource{&priceSource1R4}, 40, 1) + msg2 = oracletypes.NewMsgCreatePrice(creator2.String(), 1, []*oracletypes.PriceSource{&priceSource1R4}, 40, 1) + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg0}, "valconskey0", kr0) + s.Require().NoError(err) + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg1}, "valconskey1", kr1) + s.Require().NoError(err) + err = s.network.SendTxOracleCreateprice([]sdk.Msg{msg2}, "valconskey2", kr2) + s.Require().NoError(err) + s.moveToAndCheck(42) + res, err = s.network.QueryOracle().LatestPrice(context.Background(), &oracletypes.QueryGetLatestPriceRequest{TokenId: 1}) + s.Require().NoError(err) + // price updated, round 4 has price{p1} + s.Require().Equal(priceTest1R4.getPriceTimeRound(4), res.Price) + +} + +func (s *E2ETestSuite) TestCreatePriceNST() { + +} + +func (s *E2ETestSuite) TestSlashing() { + +} + +func (s *E2ETestSuite) moveToAndCheck(height int64) { + _, err := s.network.WaitForHeightWithTimeout(height, 30*time.Second) + s.Require().NoError(err) +} + +func (s *E2ETestSuite) moveNAndCheck(n int64) { + for i := int64(0); i < n; i++ { + err := s.network.WaitForNextBlock() + s.Require().NoError(err) + } +} diff --git a/tests/e2e/oracle/data.go b/tests/e2e/oracle/data.go index 3fdc41d9a..f3fbb5aeb 100644 --- a/tests/e2e/oracle/data.go +++ b/tests/e2e/oracle/data.go @@ -35,10 +35,26 @@ func (p priceTime) updateTimestamp() priceTime { return p } +func (p priceTime) generateRealTimeStructs(detID string, sourceID uint64) (priceTime, oracletypes.PriceSource) { + retP := p.updateTimestamp() + pTimeDetID := retP.getPriceTimeDetID(detID) + return retP, oracletypes.PriceSource{ + SourceID: 1, + Prices: []*oracletypes.PriceTimeDetID{ + &pTimeDetID, + }, + } +} + var ( price1 = priceTime{ Price: "199999", Decimal: 18, Timestamp: time.Now().UTC().Format(layout), } + price2 = priceTime{ + Price: "299999", + Decimal: 18, + Timestamp: time.Now().UTC().Format(layout), + } ) diff --git a/tests/e2e/oracle/oracle_test.go b/tests/e2e/oracle/oracle_test.go index 67cf8d732..cbe84803f 100644 --- a/tests/e2e/oracle/oracle_test.go +++ b/tests/e2e/oracle/oracle_test.go @@ -9,8 +9,8 @@ import ( func TestE2ESuite(t *testing.T) { cfg := network.DefaultConfig() - cfg.NumValidators = 1 + cfg.NumValidators = 4 cfg.CleanupDir = true - // cfg.EnableTMLogging = true + cfg.EnableTMLogging = true suite.Run(t, NewE2ETestSuite(cfg)) } diff --git a/testutil/network/network.go b/testutil/network/network.go index f64fc3c34..6610b2098 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -339,7 +339,7 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { logger := log.NewNopLogger() if cfg.EnableTMLogging { logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger, _ = tmflags.ParseLogLevel("debug", logger, tmcfg.DefaultLogLevel) + logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel) } ctx.Logger = logger diff --git a/x/oracle/keeper/aggregator/aggregator.go b/x/oracle/keeper/aggregator/aggregator.go index 48e7778cd..517ba0a4f 100644 --- a/x/oracle/keeper/aggregator/aggregator.go +++ b/x/oracle/keeper/aggregator/aggregator.go @@ -50,9 +50,9 @@ type aggregator struct { func (agg *aggregator) copy4CheckTx() *aggregator { ret := &aggregator{ - finalPrice: big.NewInt(0).Set(agg.finalPrice), - reportPower: big.NewInt(0).Set(agg.reportPower), - totalPower: big.NewInt(0).Set(agg.totalPower), + finalPrice: copyBigInt(agg.finalPrice), + reportPower: copyBigInt(agg.reportPower), + totalPower: copyBigInt(agg.totalPower), reports: make([]*reportPrice, 0, len(agg.reports)), dsPrices: make(map[uint64]string), @@ -62,13 +62,13 @@ func (agg *aggregator) copy4CheckTx() *aggregator { } for _, report := range agg.reports { rTmp := *report - rTmp.price = big.NewInt(0).Set(report.price) - rTmp.power = big.NewInt(0).Set(report.power) + rTmp.price = copyBigInt(report.price) + rTmp.power = copyBigInt(report.power) for k, v := range report.prices { // prices are information submitted by validators, these data will not change under deterministic sources, but with non-deterministic sources they might be overwrite by later prices tmpV := *v - tmpV.price = big.NewInt(0).Set(v.price) + tmpV.price = copyBigInt(v.price) rTmp.prices[k] = &tmpV } diff --git a/x/oracle/keeper/aggregator/calculator.go b/x/oracle/keeper/aggregator/calculator.go index 9ee504d25..86749b6ac 100644 --- a/x/oracle/keeper/aggregator/calculator.go +++ b/x/oracle/keeper/aggregator/calculator.go @@ -75,14 +75,14 @@ func (r *roundPricesList) copy4CheckTx() *roundPricesList { for _, v := range r.roundPricesList { tmpRP := &roundPrices{ detID: v.detID, - price: big.NewInt(0).Set(v.price), + price: copyBigInt(v.price), prices: make([]*priceAndPower, 0, len(v.prices)), timestamp: v.timestamp, } for _, pNP := range v.prices { tmpPNP := *pNP // power will be modified during execution - tmpPNP.power = big.NewInt(0).Set(pNP.power) + tmpPNP.power = copyBigInt(pNP.power) tmpRP.prices = append(tmpRP.prices, &tmpPNP) } diff --git a/x/oracle/keeper/aggregator/util.go b/x/oracle/keeper/aggregator/util.go new file mode 100644 index 000000000..98fd7bd00 --- /dev/null +++ b/x/oracle/keeper/aggregator/util.go @@ -0,0 +1,10 @@ +package aggregator + +import "math/big" + +func copyBigInt(i *big.Int) *big.Int { + if i == nil { + return nil + } + return big.NewInt(0).Set(i) +}