Skip to content
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

feat(oracle): update params #83

Merged
merged 17 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x/oracle/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func GetTxCmd() *cobra.Command {
}

cmd.AddCommand(CmdCreatePrice())
cmd.AddCommand(CmdUpdateParams())
// this line is used by starport scaffolding # 1

return cmd
Expand Down
3 changes: 1 addition & 2 deletions x/oracle/client/cli/tx_create_price.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ func CmdCreatePrice() *cobra.Command {
// TODO: support v1 single sourceID for temporary
Use: "create-price feederid basedblock nonce sourceid decimal price timestamp detid optinoal(price timestamp detid) optional(desc)",
Short: "Broadcast message create-price",
// Args: cobra.ExactArgs(0),
Args: cobra.MinimumNArgs(8),
Args: cobra.MinimumNArgs(8),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
Expand Down
33 changes: 33 additions & 0 deletions x/oracle/client/cli/tx_update_params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cli

import (
"github.com/ExocoreNetwork/exocore/x/oracle/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/cobra"
)

func CmdUpdateParams() *cobra.Command {
cmd := &cobra.Command{
// TODO: support v1 single sourceID for temporary
Use: "update-params params",
Short: "Broadcast message update-params",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg := types.NewMsgUpdateParams(clientCtx.GetFromAddress().String(), args[0])
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
2 changes: 1 addition & 1 deletion x/oracle/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type (
storeKey storetypes.StoreKey
memKey storetypes.StoreKey
paramstore paramtypes.Subspace
authority string
// authority string
common.KeeperDogfood
}
)
Expand Down
42 changes: 35 additions & 7 deletions x/oracle/keeper/msg_server_update_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,46 @@ import (

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

func (ms msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

if ms.authority != msg.Authority {
return nil, govtypes.ErrInvalidSigner.Wrapf("invalid authority; expected %s, got %s", ms.authority, msg.Authority)
}

// store params
ms.SetParams(ctx, msg.Params)
// TODO: skip the authority check for test
// if ms.authority != msg.Authority {
// return nil, govtypes.ErrInvalidSigner.Wrapf("invalid authority; expected %s, got %s", ms.authority, msg.Authority)
// }
Comment on lines +13 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Consider security implications of the commented-out authority check.

The authority check is still commented out in the file x/oracle/keeper/msg_server_update_params.go. It's crucial to ensure this is re-enabled in production to prevent unauthorized updates to parameters.

  • File: x/oracle/keeper/msg_server_update_params.go
  • Lines: 13-16
Analysis chain

Consider security implications of the commented-out authority check.

The authority check is commented out with a TODO note for testing purposes. It's crucial to ensure this is re-enabled in production to prevent unauthorized updates to parameters.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if the authority check is re-enabled in production.

# Test: Search for the authority check in production branch. Expect: Authority check should be present.
rg --type go 'ms.authority != msg.Authority' --exec cat {}

Length of output: 471


Script:

#!/bin/bash
# Description: Verify if the authority check is re-enabled in production.

# Test: Search for the authority check in the codebase. Expect: Authority check should be present.
rg --type go 'ms.authority != msg.Authority'

Length of output: 129


p := ms.GetParams(ctx)
var err error
height := uint64(ctx.BlockHeight())
// add sources
if p, err = p.AddSources(msg.Params.Sources...); err != nil {
return nil, err
}
// add chains
if p, err = p.AddChains(msg.Params.Chains...); err != nil {
return nil, err
}
// add tokens
if p, err = p.UpdateTokens(height, msg.Params.Tokens...); err != nil {
return nil, err
}
// add rules
if p, err = p.AddRules(msg.Params.Rules...); err != nil {
return nil, err
}
// udpate tokenFeeders
for _, tokenFeeder := range msg.Params.TokenFeeders {
if p, err = p.UpdateTokenFeeder(tokenFeeder, height); err != nil {
return nil, err
}
}
// validate params
if err = p.Validate(); err != nil {
return nil, err
}
// set updated new params
ms.SetParams(ctx, p)
return &types.MsgUpdateParamsResponse{}, nil
}
155 changes: 155 additions & 0 deletions x/oracle/keeper/msg_server_update_params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package keeper_test

import (
"github.com/ExocoreNetwork/exocore/x/oracle/types"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("MsgUpdateParams", func() {
var defaultParams types.Params
BeforeEach(func() {
ks.Reset()
Expect(ks.ms).ToNot(BeNil())
defaultParams = ks.k.GetParams(ks.ctx)
})

Context("Update Chains", func() {
inputAddChains := []string{
`{"chains":[{"name":"Bitcoin", "desc":"-"}]}`,
`{"chains":[{"name":"Ethereum", "desc":"-"}]}`,
}
It("add chain with new name", func() {
msg := types.NewMsgUpdateParams("", inputAddChains[0])
_, err := ks.ms.UpdateParams(ks.ctx, msg)
Expect(err).Should(BeNil())
p := ks.k.GetParams(ks.ctx)
Expect(p.Chains[2].Name).Should(BeEquivalentTo("Bitcoin"))

})
It("add chain with duplicated name", func() {
_, err := ks.ms.UpdateParams(ks.ctx, types.NewMsgUpdateParams("", inputAddChains[1]))
Expect(err).Should(MatchError(types.ErrInvalidParams.Wrap("invalid source to add, duplicated")))
})
})
Context("Update Sources", func() {
inputAddSources := []string{
`{"sources":[{"name":"CoinGecko", "desc":"-", "valid":true}]}`,
`{"sources":[{"name":"CoinGecko", "desc":"-"}]}`,
`{"sources":[{"name":"Chainlink", "desc":"-", "valid":true}]}`,
}
It("add valid source with new name", func() {
_, err := ks.ms.UpdateParams(ks.ctx, types.NewMsgUpdateParams("", inputAddSources[0]))
Expect(err).Should(BeNil())
p := ks.k.GetParams(ks.ctx)
Expect(p.Sources[2].Name).Should(BeEquivalentTo("CoinGecko"))
})
It("add invalid source with new name", func() {
_, err := ks.ms.UpdateParams(ks.ctx, types.NewMsgUpdateParams("", inputAddSources[1]))
Expect(err).Should(MatchError(types.ErrInvalidParams.Wrap("invalid source to add, new source should be valid")))
})
It("add source with duplicated name", func() {
_, err := ks.ms.UpdateParams(ks.ctx, types.NewMsgUpdateParams("", inputAddSources[2]))
Expect(err).Should(MatchError(types.ErrInvalidParams.Wrap("invalid source to add, duplicated")))
})
})
Context("Update Tokens", func() {
startBasedBlocks := []uint64{1, 3, 3, 3, 1, 1, 1}
inputUpdateTokens := []string{
`{"tokens":[{"name":"UNI", "chain_id":"1"}]}`,
`{"tokens":[{"name":"ETH", "chain_id":"1", "decimal":8}]}`,
`{"tokens":[{"name":"ETH", "chain_id":"1", "asset_id":"assetID"}]}`,
`{"tokens":[{"name":"ETH", "chain_id":"1", "contract_address":"contractAddress"}]}`,
`{"tokens":[{"name":"ETH", "chain_id":"1", "decimal":8}]}`,
`{"tokens":[{"name":"ETH", "chain_id":"0"}]}`,
`{"tokens":[{"name":"ETH", "chain_id":"3"}]}`,
}
errs := []error{
nil,
nil,
nil,
nil,
nil,
types.ErrInvalidParams.Wrap("invalid token to add, chain not found"),
types.ErrInvalidParams.Wrap("invalid token to add, chain not found"),
}
token := types.DefaultParams().Tokens[1]
token1 := *token
token1.Decimal = 8

token2 := *token
token2.AssetID = "assetID"

token3 := *token
token3.ContractAddress = "0x123"

updatedTokenETH := []*types.Token{
nil,
&token1,
&token2,
&token3,
token,
nil,
nil,
}

for i, input := range inputUpdateTokens {
It("", func() { //})
if startBasedBlocks[i] > 1 {
p := defaultParams
p.TokenFeeders[1].StartBaseBlock = startBasedBlocks[i]
ks.k.SetParams(ks.ctx, p)
}
_, err := ks.ms.UpdateParams(ks.ctx, types.NewMsgUpdateParams("", input))
if errs[i] == nil {
Expect(err).Should(BeNil())
} else {
Expect(err).Should(MatchError(errs[i]))
}
if updatedTokenETH[i] != nil {
p := ks.k.GetParams(ks.ctx)
Expect(p.Tokens[1]).Should(BeEquivalentTo(updatedTokenETH[i]))
}
})
}

})

Context("", func() {
It("update StartBaseBlock for TokenFeeder", func() {
p := defaultParams
p.TokenFeeders[1].StartBaseBlock = 10
ks.k.SetParams(ks.ctx, p)
p.TokenFeeders[1].StartBaseBlock = 5
_, err := ks.ms.UpdateParams(ks.ctx, &types.MsgUpdateParams{
Params: types.Params{
TokenFeeders: []*types.TokenFeeder{
{
TokenID: 1,
StartBaseBlock: 5,
},
},
},
})
Expect(err).Should(BeNil())
p = ks.k.GetParams(ks.ctx)
Expect(p.TokenFeeders[1].StartBaseBlock).Should(BeEquivalentTo(5))
})
It("Add AssetID for Token", func() {
_, err := ks.ms.UpdateParams(ks.ctx, &types.MsgUpdateParams{
Params: types.Params{
Tokens: []*types.Token{
{
Name: "ETH",
ChainID: 1,
AssetID: "0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1",
},
},
},
})
Expect(err).Should(BeNil())
p := ks.k.GetParams(ks.ctx)
Expect(p.Tokens[1].AssetID).Should(BeEquivalentTo("0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1"))
})
})
})
114 changes: 114 additions & 0 deletions x/oracle/keeper/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,117 @@ func TestGetParams(t *testing.T) {

require.EqualValues(t, params, k.GetParams(ctx))
}

func TestUpdateTokenFeeder(t *testing.T) {
tests := []struct {
name string
tokenFeeder types.TokenFeeder
height uint64
err error
}{
// fail when add/update fields, before validation
{
name: "invalid update, empty fields to update",
tokenFeeder: types.TokenFeeder{
TokenID: 1,
},
height: 1,
err: types.ErrInvalidParams.Wrap("invalid tokenFeeder to update, no valid field set"),
},
{
name: "invalid udpate, for not-start feeder, set StartbaseBlock to history height",
tokenFeeder: types.TokenFeeder{
TokenID: 1,
// set current height to 100 to test fail case
StartBaseBlock: 10,
},
height: 100,
err: types.ErrInvalidParams.Wrap("invalid tokenFeeder to update, invalid StartBaseBlock"),
},
{
name: "invalid update, for running feeder, set EndBlock to history height",
tokenFeeder: types.TokenFeeder{
TokenID: 1,
// set current height to 2000000 to test fail case
EndBlock: 1500000,
},
height: 2000000,
err: types.ErrInvalidParams.Wrap("invalid tokenFeeder to update, invalid EndBlock"),
},
{
name: "invalid update, for stopped feeder, restart a feeder with wrong StartRoundID",
tokenFeeder: types.TokenFeeder{
TokenID: 2,
RuleID: 1,
// set current height to 100
StartBaseBlock: 1000,
// should be 4
StartRoundID: 5,
Interval: 10,
},
height: 100,
err: types.ErrInvalidParams.Wrap("invalid tokenFeeder to update"),
},
// success adding/updating, but fail validation
{
name: "invalid update, for new feeder, EndBlock is not set properly",
tokenFeeder: types.TokenFeeder{
TokenID: 3,
StartBaseBlock: 10,
StartRoundID: 1,
Interval: 10,
EndBlock: 51,
},
height: 1,
err: types.ErrInvalidParams.Wrap("invalid tokenFeeder, invalid EndBlock"),
},
{
name: "invalid update, for new feeder, tokenID not exists",
tokenFeeder: types.TokenFeeder{
TokenID: 4,
StartBaseBlock: 10,
StartRoundID: 1,
Interval: 10,
EndBlock: 58,
},
height: 1,
err: types.ErrInvalidParams.Wrap("invalid tokenFeeder, non-exist tokenID referred"),
},
}
p := types.DefaultParams()
p.Tokens = append(p.Tokens, &types.Token{
Name: "TEST",
ChainID: 1,
ContractAddress: "0x",
Decimal: 8,
Active: true,
AssetID: "",
})
p.Tokens = append(p.Tokens, &types.Token{
Name: "TEST_NEW",
ChainID: 1,
ContractAddress: "0x",
Decimal: 8,
Active: true,
AssetID: "",
})
p.TokenFeeders = append(p.TokenFeeders, &types.TokenFeeder{
TokenID: 2,
RuleID: 1,
StartRoundID: 1,
StartBaseBlock: 10,
Interval: 10,
EndBlock: 38,
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p, err := p.UpdateTokenFeeder(&tt.tokenFeeder, tt.height)
if err == nil {
err = p.Validate()
}
if tt.err != nil {
require.ErrorIs(t, err, tt.err)
}
})
}
}
Copy link
Contributor

@cloud8little cloud8little Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add UT for update end_block for different conditions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there're cases for end_block update:

  1. history block
  2. (endblock-startblock)%interval>=maxNonce

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add UT for update end_block for different conditions.

add some new cases:

  1. endblock<=startbasedblock
  2. for existing not started feeder, set endblock to history
  3. for existing started feeder, set endblock to history

Loading
Loading