From 454a4d11f2cc2ebe8b0d97cf5a52af0dd6b187e0 Mon Sep 17 00:00:00 2001 From: gyuguen Date: Tue, 6 Dec 2022 15:02:49 +0900 Subject: [PATCH 1/7] feat: implement oracle registration --- app/app.go | 25 ++- proto/panacea/oracle/v2/tx.proto | 5 +- types/testsuite/suite.go | 14 ++ x/oracle/client/cli/tx.go | 2 + x/oracle/client/cli/txRegisterOracle.go | 93 +++++++++++ x/oracle/genesis.go | 3 +- x/oracle/keeper/msg_server_oracle.go | 20 ++- x/oracle/keeper/oracle.go | 122 +++++++++++++++ x/oracle/keeper/oracle_test.go | 197 ++++++++++++++++++++++++ x/oracle/types/errors.go | 10 ++ x/oracle/types/events.go | 8 + x/oracle/types/keys.go | 26 ++++ x/oracle/types/message_oracle.go | 122 ++++++++++++++- x/oracle/types/oracle.go | 87 ++++++++++- x/oracle/types/tx.pb.go | 152 ++++++++++++------ 15 files changed, 818 insertions(+), 68 deletions(-) create mode 100644 x/oracle/client/cli/txRegisterOracle.go create mode 100644 x/oracle/keeper/oracle.go create mode 100644 x/oracle/keeper/oracle_test.go create mode 100644 x/oracle/types/errors.go create mode 100644 x/oracle/types/events.go diff --git a/app/app.go b/app/app.go index 68016a95..492f5d24 100644 --- a/app/app.go +++ b/app/app.go @@ -9,6 +9,9 @@ import ( "strings" "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/medibloc/panacea-core/v2/x/oracle" + oraclekeeper "github.com/medibloc/panacea-core/v2/x/oracle/keeper" + oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types" "github.com/CosmWasm/wasmd/x/wasm" wasmclient "github.com/CosmWasm/wasmd/x/wasm/client" @@ -261,10 +264,11 @@ type App struct { ScopedTransferKeeper capabilitykeeper.ScopedKeeper ScopedWasmKeeper capabilitykeeper.ScopedKeeper - aolKeeper aolkeeper.Keeper - didKeeper didkeeper.Keeper - burnKeeper burnkeeper.Keeper - wasmKeeper wasm.Keeper + aolKeeper aolkeeper.Keeper + didKeeper didkeeper.Keeper + burnKeeper burnkeeper.Keeper + wasmKeeper wasm.Keeper + oracleKeeper oraclekeeper.Keeper // the module manager mm *module.Manager @@ -309,6 +313,7 @@ func New( burntypes.StoreKey, wasm.StoreKey, feegrant.StoreKey, + oracletypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -448,6 +453,13 @@ func New( wasmOpts..., ) + app.oracleKeeper = *oraclekeeper.NewKeeper( + appCodec, + keys[oracletypes.StoreKey], + keys[oracletypes.MemStoreKey], + app.GetSubspace(oracletypes.ModuleName), + ) + // The gov proposal types can be individually enabled if len(enabledWasmProposals) != 0 { govRouter.AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.wasmKeeper, enabledWasmProposals)) @@ -500,6 +512,7 @@ func New( did.NewAppModule(appCodec, app.didKeeper), burn.NewAppModule(appCodec, app.burnKeeper), wasm.NewAppModule(appCodec, &app.wasmKeeper, app.StakingKeeper), + oracle.NewAppModule(appCodec, app.oracleKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -523,6 +536,7 @@ func New( authtypes.ModuleName, aoltypes.ModuleName, didtypes.ModuleName, + oracletypes.ModuleName, wasm.ModuleName, banktypes.ModuleName, crisistypes.ModuleName, @@ -551,6 +565,7 @@ func New( slashingtypes.ModuleName, ibctransfertypes.ModuleName, aoltypes.ModuleName, + oracletypes.ModuleName, wasm.ModuleName, paramstypes.ModuleName, authz.ModuleName, @@ -579,6 +594,7 @@ func New( aoltypes.ModuleName, didtypes.ModuleName, burntypes.ModuleName, + oracletypes.ModuleName, wasm.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, @@ -808,6 +824,7 @@ func initParamsKeeper(appCodec codec.Codec, legacyAmino *codec.LegacyAmino, key, paramsKeeper.Subspace(didtypes.ModuleName) paramsKeeper.Subspace(burntypes.ModuleName) paramsKeeper.Subspace(wasm.ModuleName) + paramsKeeper.Subspace(oracletypes.ModuleName) return paramsKeeper } diff --git a/proto/panacea/oracle/v2/tx.proto b/proto/panacea/oracle/v2/tx.proto index 0250e87d..76ad416b 100644 --- a/proto/panacea/oracle/v2/tx.proto +++ b/proto/panacea/oracle/v2/tx.proto @@ -47,8 +47,9 @@ message MsgApproveOracleRegistration { // ApproveOracleRegistration defines for oracle registration approval message ApproveOracleRegistration { string unique_id = 1; - string target_oracle_address = 2; - bytes encrypted_oracle_priv_key = 3; + string approved_oracle_address = 2; + string target_oracle_address = 3; + bytes encrypted_oracle_priv_key = 4; } // MsgApproveOracleRegistrationResponse defines the Msg/ApproveOracleRegistration diff --git a/types/testsuite/suite.go b/types/testsuite/suite.go index 5dff6c73..8a02c7e9 100644 --- a/types/testsuite/suite.go +++ b/types/testsuite/suite.go @@ -34,6 +34,8 @@ import ( aoltypes "github.com/medibloc/panacea-core/v2/x/aol/types" burnkeeper "github.com/medibloc/panacea-core/v2/x/burn/keeper" burntypes "github.com/medibloc/panacea-core/v2/x/burn/types" + oraclekeeper "github.com/medibloc/panacea-core/v2/x/oracle/keeper" + oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types" "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/libs/log" @@ -65,6 +67,8 @@ type TestSuite struct { TransferKeeper ibctransferkeeper.Keeper DIDMsgServer didtypes.MsgServer DIDKeeper didkeeper.Keeper + OracleKeeper oraclekeeper.Keeper + OracleMsgServer oracletypes.MsgServer WasmKeeper wasm.Keeper UpgradeKeeper upgradekeeper.Keeper } @@ -76,6 +80,7 @@ func (suite *TestSuite) SetupTest() { banktypes.StoreKey, paramstypes.StoreKey, didtypes.StoreKey, + oracletypes.StoreKey, wasm.StoreKey, ibchost.StoreKey, capabilitytypes.StoreKey, @@ -202,6 +207,15 @@ func (suite *TestSuite) SetupTest() { memKeys[didtypes.MemStoreKey], ) suite.DIDMsgServer = didkeeper.NewMsgServerImpl(suite.DIDKeeper) + + suite.OracleKeeper = *oraclekeeper.NewKeeper( + cdc.Marshaler, + keyParams[oracletypes.StoreKey], + memKeys[oracletypes.MemStoreKey], + paramsKeeper.Subspace(oracletypes.ModuleName), + ) + + suite.OracleMsgServer = oraclekeeper.NewMsgServerImpl(suite.OracleKeeper) } func (suite *TestSuite) BeforeTest(suiteName, testName string) { diff --git a/x/oracle/client/cli/tx.go b/x/oracle/client/cli/tx.go index bbd4017e..0c16d834 100644 --- a/x/oracle/client/cli/tx.go +++ b/x/oracle/client/cli/tx.go @@ -18,5 +18,7 @@ func GetTxCmd() *cobra.Command { RunE: client.ValidateCmd, } + cmd.AddCommand(CmdRegisterOracle()) + return cmd } diff --git a/x/oracle/client/cli/txRegisterOracle.go b/x/oracle/client/cli/txRegisterOracle.go new file mode 100644 index 00000000..c7b023da --- /dev/null +++ b/x/oracle/client/cli/txRegisterOracle.go @@ -0,0 +1,93 @@ +package cli + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/medibloc/panacea-core/v2/x/oracle/types" + "github.com/spf13/cobra" +) + +func CmdRegisterOracle() *cobra.Command { + cmd := &cobra.Command{ + Use: "register-oracle [unique ID] [node public key] [node public key remote report] [trusted block height] [trusted block hash] [endpoint] [oracle's commission rate]", + Short: "Register a new oracle", + Args: cobra.ExactArgs(7), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + oracleAddress := clientCtx.GetFromAddress().String() + nodePubKey, err := base64.StdEncoding.DecodeString(args[1]) + if err != nil { + return err + } + + nodePubKeyRemoteReport, err := base64.StdEncoding.DecodeString(args[2]) + if err != nil { + return err + } + + trustedBlockHeight, err := strconv.ParseInt(args[3], 10, 64) + if err != nil { + return err + } + + trustedBlockHash, err := hex.DecodeString(args[4]) + if err != nil { + return err + } + + endpoint := args[5] + + oracleCommissionRateStr := args[6] + + if len(oracleCommissionRateStr) == 0 { + return fmt.Errorf("oracleCommissionRate is empty") + } + + oracleCommissionRate, err := sdk.NewDecFromStr(oracleCommissionRateStr) + + msg := types.NewMsgRegisterOracle( + args[0], + oracleAddress, + nodePubKey, + nodePubKeyRemoteReport, + trustedBlockHeight, + trustedBlockHash, + endpoint, + oracleCommissionRate, + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func getOracleCommissionRate(oracleCommissionRateStr string) (*sdk.Dec, error) { + if len(oracleCommissionRateStr) == 1 { + return nil, fmt.Errorf("oracleCommissionRate is empty") + } + + if len(oracleCommissionRateStr) != 0 { + oracleCommissionRate, err := sdk.NewDecFromStr(oracleCommissionRateStr) + return &oracleCommissionRate, err + } + + return nil, nil +} diff --git a/x/oracle/genesis.go b/x/oracle/genesis.go index ecf6eaf8..00c99641 100644 --- a/x/oracle/genesis.go +++ b/x/oracle/genesis.go @@ -10,11 +10,10 @@ import ( // state. func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { //TODO implement me - panic("implement me") } // ExportGenesis returns the capability module's exported genesis. func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { //TODO implement me - panic("implement me") + return &types.GenesisState{} } diff --git a/x/oracle/keeper/msg_server_oracle.go b/x/oracle/keeper/msg_server_oracle.go index db1e12eb..41082b80 100644 --- a/x/oracle/keeper/msg_server_oracle.go +++ b/x/oracle/keeper/msg_server_oracle.go @@ -3,12 +3,26 @@ package keeper import ( "context" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/medibloc/panacea-core/v2/x/oracle/types" ) -func (m msgServer) RegisterOracle(ctx context.Context, oracle *types.MsgRegisterOracle) (*types.MsgRegisterOracleResponse, error) { - //TODO implement me - panic("implement me") +func (m msgServer) RegisterOracle(goCtx context.Context, msg *types.MsgRegisterOracle) (*types.MsgRegisterOracleResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := m.Keeper.RegisterOracle(ctx, msg); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + ) + + return &types.MsgRegisterOracleResponse{}, nil } func (m msgServer) ApproveOracleRegistration(ctx context.Context, registration *types.MsgApproveOracleRegistration) (*types.MsgApproveOracleRegistrationResponse, error) { diff --git a/x/oracle/keeper/oracle.go b/x/oracle/keeper/oracle.go new file mode 100644 index 00000000..0e2b0281 --- /dev/null +++ b/x/oracle/keeper/oracle.go @@ -0,0 +1,122 @@ +package keeper + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/medibloc/panacea-core/v2/x/oracle/types" +) + +func (k Keeper) RegisterOracle(ctx sdk.Context, msg *types.MsgRegisterOracle) error { + oracleRegistration := types.NewOracleRegistration(msg) + + if err := oracleRegistration.ValidateBasic(); err != nil { + return err + } + + // TODO implement required to params + /*params := k.GetParams(ctx) + if params.UniqueId != oracleRegistration.UniqueId { + return sdkerrors.Wrapf(types.ErrOracleRegistration, "is not match the currently active uniqueID") + }*/ + + if oracle, err := k.GetOracle(ctx, oracleRegistration.OracleAddress); !errors.Is(types.ErrOracleNotFound, err) { + if oracle != nil { + return sdkerrors.Wrapf(types.ErrOracleRegistration, "already registered oracle. address(%s)", oracleRegistration.OracleAddress) + } else { + return sdkerrors.Wrapf(types.ErrOracleRegistration, err.Error()) + } + } + + if err := k.SetOracleRegistration(ctx, oracleRegistration); err != nil { + return err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeRegistration, + sdk.NewAttribute(types.AttributeKeyUniqueID, oracleRegistration.UniqueId), + sdk.NewAttribute(types.AttributeKeyOracleAddress, oracleRegistration.OracleAddress), + ), + ) + return nil +} + +func (k Keeper) SetOracleRegistration(ctx sdk.Context, regOracle *types.OracleRegistration) error { + store := ctx.KVStore(k.storeKey) + + accAddr, err := sdk.AccAddressFromBech32(regOracle.OracleAddress) + if err != nil { + return err + } + key := types.GetOracleRegistrationKey(regOracle.UniqueId, accAddr) + bz, err := k.cdc.MarshalLengthPrefixed(regOracle) + if err != nil { + return err + } + + store.Set(key, bz) + + return nil +} + +func (k Keeper) GetOracleRegistration(ctx sdk.Context, uniqueID, address string) (*types.OracleRegistration, error) { + store := ctx.KVStore(k.storeKey) + accAddr, err := sdk.AccAddressFromBech32(address) + if err != nil { + return nil, err + } + key := types.GetOracleRegistrationKey(uniqueID, accAddr) + bz := store.Get(key) + if bz == nil { + return nil, sdkerrors.Wrapf(types.ErrGetOracleRegistration, "oracle registration not found") + } + + oracleRegistration := &types.OracleRegistration{} + + if err := k.cdc.UnmarshalLengthPrefixed(bz, oracleRegistration); err != nil { + return nil, err + } + + return oracleRegistration, nil +} + +func (k Keeper) SetOracle(ctx sdk.Context, oracle *types.Oracle) error { + store := ctx.KVStore(k.storeKey) + accAddr, err := sdk.AccAddressFromBech32(oracle.OracleAddress) + if err != nil { + return err + } + key := types.GetOracleKey(accAddr) + bz, err := k.cdc.MarshalLengthPrefixed(oracle) + if err != nil { + return err + } + + store.Set(key, bz) + + return nil +} + +func (k Keeper) GetOracle(ctx sdk.Context, address string) (*types.Oracle, error) { + store := ctx.KVStore(k.storeKey) + accAddr, err := sdk.AccAddressFromBech32(address) + if err != nil { + return nil, err + } + key := types.GetOracleKey(accAddr) + bz := store.Get(key) + if bz == nil { + return nil, sdkerrors.Wrapf(types.ErrOracleNotFound, "oracle '%s' does not exist", address) + } + + oracle := &types.Oracle{} + + err = k.cdc.UnmarshalLengthPrefixed(bz, oracle) + if err != nil { + return nil, sdkerrors.Wrapf(types.ErrGetOracle, err.Error()) + } + + return oracle, nil +} diff --git a/x/oracle/keeper/oracle_test.go b/x/oracle/keeper/oracle_test.go new file mode 100644 index 00000000..267dcefd --- /dev/null +++ b/x/oracle/keeper/oracle_test.go @@ -0,0 +1,197 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/medibloc/panacea-core/v2/types/testsuite" + "github.com/medibloc/panacea-core/v2/x/oracle/types" + "github.com/stretchr/testify/suite" +) + +type oracleTestSuite struct { + testsuite.TestSuite + + uniqueID string + + oracleAccPrivKey cryptotypes.PrivKey + oracleAccPubKey cryptotypes.PubKey + oracleAccAddr sdk.AccAddress + + newOracleAccPrivKey cryptotypes.PrivKey + newOracleAccPubKey cryptotypes.PubKey + newOracleAccAddr sdk.AccAddress + + oraclePrivKey *btcec.PrivateKey + oraclePubKey *btcec.PublicKey + + nodePrivKey *btcec.PrivateKey + nodePubKey *btcec.PublicKey + + nodePubKeyRemoteReport []byte + + trustedBlockHeight int64 + trustedBlockHash []byte + + endpoint string + oracleCommissionRate sdk.Dec +} + +func TestOracleTestSuite(t *testing.T) { + suite.Run(t, new(oracleTestSuite)) +} + +func (suite *oracleTestSuite) BeforeTest(_, _ string) { + suite.uniqueID = "uniqueID" + suite.oracleAccPrivKey = secp256k1.GenPrivKey() + suite.oracleAccPubKey = suite.oracleAccPrivKey.PubKey() + suite.oracleAccAddr = sdk.AccAddress(suite.oracleAccPubKey.Address()) + + suite.newOracleAccPrivKey = secp256k1.GenPrivKey() + suite.newOracleAccPubKey = suite.newOracleAccPrivKey.PubKey() + suite.newOracleAccAddr = sdk.AccAddress(suite.newOracleAccPubKey.Address()) + + suite.oraclePrivKey, _ = btcec.NewPrivateKey(btcec.S256()) + suite.oraclePubKey = suite.oraclePrivKey.PubKey() + + suite.nodePrivKey, _ = btcec.NewPrivateKey(btcec.S256()) + suite.nodePubKey = suite.nodePrivKey.PubKey() + + suite.nodePubKeyRemoteReport = []byte("nodePubKeyRemoteReport") + + suite.trustedBlockHeight = int64(1) + suite.trustedBlockHash = []byte("trustedBlockHash") + + suite.endpoint = "https://my-validator.org" + suite.oracleCommissionRate = sdk.NewDecWithPrec(1, 1) +} + +func (suite *oracleTestSuite) TestRegisterOracleSuccess() { + ctx := suite.Ctx + + msgRegisterOracle := &types.MsgRegisterOracle{ + UniqueId: suite.uniqueID, + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, + TrustedBlockHeight: suite.trustedBlockHeight, + TrustedBlockHash: suite.trustedBlockHash, + Endpoint: suite.endpoint, + OracleCommissionRate: suite.oracleCommissionRate, + } + + err := suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().NoError(err) + + events := suite.Ctx.EventManager().Events() + suite.Require().Equal(1, len(events)) + suite.Require().Equal(types.EventTypeRegistration, events[0].Type) + + eventVoteAttributes := events[0].Attributes + suite.Require().Equal(2, len(eventVoteAttributes)) + suite.Require().Equal(types.AttributeKeyUniqueID, string(eventVoteAttributes[0].Key)) + suite.Require().Equal(suite.uniqueID, string(eventVoteAttributes[0].Value)) + suite.Require().Equal(types.AttributeKeyOracleAddress, string(eventVoteAttributes[1].Key)) + suite.Require().Equal(suite.oracleAccAddr.String(), string(eventVoteAttributes[1].Value)) + + oracleFromKeeper, err := suite.OracleKeeper.GetOracleRegistration(ctx, suite.uniqueID, suite.oracleAccAddr.String()) + suite.Require().NoError(err) + suite.Require().Equal(suite.uniqueID, oracleFromKeeper.UniqueId) + suite.Require().Equal(suite.oracleAccAddr.String(), oracleFromKeeper.OracleAddress) + suite.Require().Equal(suite.nodePubKey.SerializeCompressed(), oracleFromKeeper.NodePubKey) + suite.Require().Equal(suite.nodePubKeyRemoteReport, oracleFromKeeper.NodePubKeyRemoteReport) + suite.Require().Equal(suite.trustedBlockHeight, oracleFromKeeper.TrustedBlockHeight) + suite.Require().Equal(suite.trustedBlockHash, oracleFromKeeper.TrustedBlockHash) + suite.Require().Equal(suite.endpoint, oracleFromKeeper.Endpoint) + suite.Require().Equal(suite.oracleCommissionRate, oracleFromKeeper.OracleCommissionRate) +} + +func (suite *oracleTestSuite) TestRegisterOracleFailedValidateToMsgOracleRegistration() { + ctx := suite.Ctx + + msgRegisterOracle := &types.MsgRegisterOracle{ + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, + TrustedBlockHeight: suite.trustedBlockHeight, + TrustedBlockHash: suite.trustedBlockHash, + Endpoint: suite.endpoint, + OracleCommissionRate: suite.oracleCommissionRate, + } + + err := suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "uniqueID is empty") + + msgRegisterOracle.UniqueId = suite.uniqueID + msgRegisterOracle.NodePubKey = nil + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "node public key is empty") + + msgRegisterOracle.NodePubKey = []byte("invalidNodePubKey") + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "invalid node public key") + + msgRegisterOracle.NodePubKey = suite.nodePubKey.SerializeCompressed() + msgRegisterOracle.NodePubKeyRemoteReport = nil + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "remote report of node public key is empty") + + msgRegisterOracle.NodePubKeyRemoteReport = suite.nodePubKeyRemoteReport + msgRegisterOracle.TrustedBlockHeight = 0 + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "trusted block height must be greater than zero") + + msgRegisterOracle.TrustedBlockHeight = suite.trustedBlockHeight + msgRegisterOracle.TrustedBlockHash = nil + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "trusted block hash should not be nil") + + msgRegisterOracle.TrustedBlockHash = suite.trustedBlockHash + msgRegisterOracle.Endpoint = "" + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "endpoint is empty") + + msgRegisterOracle.Endpoint = suite.endpoint + msgRegisterOracle.OracleCommissionRate = sdk.NewInt(-1).ToDec() + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, sdkerrors.ErrInvalidRequest) + suite.Require().ErrorContains(err, "oracle commission rate cannot be negative") + + events := suite.Ctx.EventManager().Events() + suite.Require().Equal(0, len(events)) +} + +func (suite *oracleTestSuite) TestRegisterOracleAlreadyExistOracle() { + ctx := suite.Ctx + + oracle := types.NewOracle(suite.oracleAccAddr.String(), suite.uniqueID, suite.endpoint, suite.oracleCommissionRate) + err := suite.OracleKeeper.SetOracle(ctx, oracle) + suite.Require().NoError(err) + + msgRegisterOracle := &types.MsgRegisterOracle{ + UniqueId: suite.uniqueID, + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, + TrustedBlockHeight: suite.trustedBlockHeight, + TrustedBlockHash: suite.trustedBlockHash, + Endpoint: suite.endpoint, + OracleCommissionRate: suite.oracleCommissionRate, + } + + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, types.ErrOracleRegistration) + suite.Require().ErrorContains(err, fmt.Sprintf("already registered oracle. address(%s)", msgRegisterOracle.OracleAddress)) +} diff --git a/x/oracle/types/errors.go b/x/oracle/types/errors.go new file mode 100644 index 00000000..b9e92631 --- /dev/null +++ b/x/oracle/types/errors.go @@ -0,0 +1,10 @@ +package types + +import sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + +var ( + ErrOracleRegistration = sdkerrors.Register(ModuleName, 1, "error while registering a oracle") + ErrGetOracleRegistration = sdkerrors.Register(ModuleName, 2, "error while getting a oracle registration") + ErrGetOracle = sdkerrors.Register(ModuleName, 3, "error while getting a oracle") + ErrOracleNotFound = sdkerrors.Register(ModuleName, 4, "oracle not found") +) diff --git a/x/oracle/types/events.go b/x/oracle/types/events.go new file mode 100644 index 00000000..e1950f2f --- /dev/null +++ b/x/oracle/types/events.go @@ -0,0 +1,8 @@ +package types + +const ( + EventTypeRegistration = "oracle_registration" + + AttributeKeyUniqueID = "unique_id" + AttributeKeyOracleAddress = "oracle_address" +) diff --git a/x/oracle/types/keys.go b/x/oracle/types/keys.go index 28e943d6..229e939c 100644 --- a/x/oracle/types/keys.go +++ b/x/oracle/types/keys.go @@ -1,5 +1,11 @@ package types +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( // ModuleName defines the module name ModuleName = "oracle" @@ -16,3 +22,23 @@ const ( // MemStoreKey defines the in-memory store key MemStoreKey = "mem_oracle" ) + +var ( + // OraclesKey defines key to store oracle + OraclesKey = []byte{0x01} + OracleRegistrationKey = []byte{0x02} + + IndexSeparator = []byte{0xFF} +) + +func GetOracleKey(address sdk.AccAddress) []byte { + return append(OraclesKey, address...) +} + +func GetOracleRegistrationKey(uniqueID string, address sdk.AccAddress) []byte { + return append(OracleRegistrationKey, CombineKeys([]byte(uniqueID), address)...) +} + +func CombineKeys(keys ...[]byte) []byte { + return bytes.Join(keys, IndexSeparator) +} diff --git a/x/oracle/types/message_oracle.go b/x/oracle/types/message_oracle.go index 4a1c1a40..a645a6c8 100644 --- a/x/oracle/types/message_oracle.go +++ b/x/oracle/types/message_oracle.go @@ -1,12 +1,14 @@ package types import ( + "github.com/btcsuite/btcd/btcec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var _ sdk.Msg = &MsgRegisterOracle{} -func NewMsgRegisterOracle(uniqueID, oracleAddress, endpoint string, nodePubKey, nodePubKeyRemoteReport []byte, trustedBlockHeight int64, trustedBlockHash []byte, oracleCommissionRate sdk.Dec) *MsgRegisterOracle { +func NewMsgRegisterOracle(uniqueID, oracleAddress string, nodePubKey, nodePubKeyRemoteReport []byte, trustedBlockHeight int64, trustedBlockHash []byte, endpoint string, oracleCommissionRate sdk.Dec) *MsgRegisterOracle { return &MsgRegisterOracle{ UniqueId: uniqueID, OracleAddress: oracleAddress, @@ -28,6 +30,23 @@ func (m *MsgRegisterOracle) Type() string { } func (m *MsgRegisterOracle) ValidateBasic() error { + if m.NodePubKey == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "node public key is empty") + } else if _, err := btcec.ParsePubKey(m.NodePubKey, btcec.S256()); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid node public key") + } + + if m.NodePubKeyRemoteReport == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "remote report of node public key is empty") + } + + if m.TrustedBlockHeight <= 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "trusted block height must be greater than zero") + } + + if m.TrustedBlockHash == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "trusted block hash should not be nil") + } return nil } @@ -44,3 +63,104 @@ func (m *MsgRegisterOracle) GetSigners() []sdk.AccAddress { } return []sdk.AccAddress{oracleAddress} } + +var _ sdk.Msg = &MsgApproveOracleRegistration{} + +func NewMsgApproveOracleRegistration(approve *ApproveOracleRegistration, signature []byte) *MsgApproveOracleRegistration { + return &MsgApproveOracleRegistration{ + ApproveOracleRegistration: approve, + Signature: signature, + } +} + +func (m *MsgApproveOracleRegistration) Route() string { + return RouterKey +} + +func (m *MsgApproveOracleRegistration) Type() string { + return "ApproveOracleRegistration" +} + +func (m *MsgApproveOracleRegistration) ValidateBasic() error { + if err := m.ApproveOracleRegistration.ValidateBasic(); err != nil { + return err + } + + if m.Signature == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "signature is empty") + } + + return nil +} + +func (m *MsgApproveOracleRegistration) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} + +func (m *MsgApproveOracleRegistration) GetSigners() []sdk.AccAddress { + oracleAddress, err := sdk.AccAddressFromBech32(m.ApproveOracleRegistration.ApprovedOracleAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{oracleAddress} +} + +func (m *ApproveOracleRegistration) ValidateBasic() error { + if len(m.UniqueId) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "uniqueId is empty") + } + + if len(m.ApprovedOracleAddress) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "approvedOracleAddress is empty") + } + + if len(m.TargetOracleAddress) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "targetOracleAddress is empty") + } + + if m.EncryptedOraclePrivKey == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "encryptedOraclePrivKey is empty") + } + + return nil +} + +var _ sdk.Msg = &MsgUpdateOracleInfo{} + +func NewMsgUpdateOracleInfo(address, endpoint string, commissionRate sdk.Dec) *MsgUpdateOracleInfo { + return &MsgUpdateOracleInfo{ + OracleAddress: address, + Endpoint: endpoint, + OracleCommissionRate: commissionRate, + } +} + +func (m *MsgUpdateOracleInfo) Route() string { + return RouterKey +} + +func (m *MsgUpdateOracleInfo) Type() string { + return "UpdateOracleInfo" +} + +func (m *MsgUpdateOracleInfo) ValidateBasic() error { + if len(m.OracleAddress) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "oracleAddress is empty") + } + + return nil +} + +func (m *MsgUpdateOracleInfo) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(m) + return sdk.MustSortJSON(bz) +} + +func (m *MsgUpdateOracleInfo) GetSigners() []sdk.AccAddress { + oracleAddress, err := sdk.AccAddressFromBech32(m.OracleAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{oracleAddress} +} diff --git a/x/oracle/types/oracle.go b/x/oracle/types/oracle.go index db3432b5..99ef64de 100644 --- a/x/oracle/types/oracle.go +++ b/x/oracle/types/oracle.go @@ -1,11 +1,86 @@ package types -func (m Oracle) ValidateBasic() error { - //TODO implement me - panic("implement me") +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +func NewOracle(oracleAddress, uniqueID, endpoint string, oracleCommissionRate sdk.Dec) *Oracle { + return &Oracle{ + OracleAddress: oracleAddress, + UniqueId: uniqueID, + Endpoint: endpoint, + OracleCommissionRate: oracleCommissionRate, + } +} + +func (m *Oracle) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.OracleAddress); err != nil { + return sdkerrors.Wrapf(err, "oracle address is invalid. address: %s", m.OracleAddress) + } + if len(m.UniqueId) == 0 { + return fmt.Errorf("uniqueID is empty") + } + if len(m.Endpoint) == 0 { + return fmt.Errorf("endpoint is empty") + } + if m.OracleCommissionRate.IsNegative() { + return fmt.Errorf("oracle commission rate cannot be negative") + } + if m.OracleCommissionRate.GT(sdk.OneDec()) { + return fmt.Errorf("oracle commission rate cannot be greater than 1") + } + return nil +} + +func NewOracleRegistration(msg *MsgRegisterOracle) *OracleRegistration { + return &OracleRegistration{ + UniqueId: msg.UniqueId, + OracleAddress: msg.OracleAddress, + NodePubKey: msg.NodePubKey, + NodePubKeyRemoteReport: msg.NodePubKeyRemoteReport, + TrustedBlockHeight: msg.TrustedBlockHeight, + TrustedBlockHash: msg.TrustedBlockHash, + Endpoint: msg.Endpoint, + OracleCommissionRate: msg.OracleCommissionRate, + } } -func (m OracleRegistration) ValidateBasic() error { - //TODO implement me - panic("implement me") +func (m *OracleRegistration) ValidateBasic() error { + if len(m.UniqueId) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "uniqueID is empty") + } + if _, err := sdk.AccAddressFromBech32(m.OracleAddress); err != nil { + return sdkerrors.Wrapf(err, "oracle address is invalid. address: %s", m.OracleAddress) + } + + if m.NodePubKey == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "node public key is empty") + } else if _, err := btcec.ParsePubKey(m.NodePubKey, btcec.S256()); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid node public key") + } + + if m.NodePubKeyRemoteReport == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "remote report of node public key is empty") + } + + if m.TrustedBlockHeight <= 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "trusted block height must be greater than zero") + } + + if m.TrustedBlockHash == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "trusted block hash should not be nil") + } + + if len(m.Endpoint) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "endpoint is empty") + } + + if m.OracleCommissionRate.IsNegative() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "oracle commission rate cannot be negative") + } + return nil } diff --git a/x/oracle/types/tx.pb.go b/x/oracle/types/tx.pb.go index 6a8fb127..c318dd36 100644 --- a/x/oracle/types/tx.pb.go +++ b/x/oracle/types/tx.pb.go @@ -219,8 +219,9 @@ func (m *MsgApproveOracleRegistration) GetSignature() []byte { // ApproveOracleRegistration defines for oracle registration approval type ApproveOracleRegistration struct { UniqueId string `protobuf:"bytes,1,opt,name=unique_id,json=uniqueId,proto3" json:"unique_id,omitempty"` - TargetOracleAddress string `protobuf:"bytes,2,opt,name=target_oracle_address,json=targetOracleAddress,proto3" json:"target_oracle_address,omitempty"` - EncryptedOraclePrivKey []byte `protobuf:"bytes,3,opt,name=encrypted_oracle_priv_key,json=encryptedOraclePrivKey,proto3" json:"encrypted_oracle_priv_key,omitempty"` + ApprovedOracleAddress string `protobuf:"bytes,2,opt,name=approved_oracle_address,json=approvedOracleAddress,proto3" json:"approved_oracle_address,omitempty"` + TargetOracleAddress string `protobuf:"bytes,3,opt,name=target_oracle_address,json=targetOracleAddress,proto3" json:"target_oracle_address,omitempty"` + EncryptedOraclePrivKey []byte `protobuf:"bytes,4,opt,name=encrypted_oracle_priv_key,json=encryptedOraclePrivKey,proto3" json:"encrypted_oracle_priv_key,omitempty"` } func (m *ApproveOracleRegistration) Reset() { *m = ApproveOracleRegistration{} } @@ -263,6 +264,13 @@ func (m *ApproveOracleRegistration) GetUniqueId() string { return "" } +func (m *ApproveOracleRegistration) GetApprovedOracleAddress() string { + if m != nil { + return m.ApprovedOracleAddress + } + return "" +} + func (m *ApproveOracleRegistration) GetTargetOracleAddress() string { if m != nil { return m.TargetOracleAddress @@ -418,52 +426,53 @@ func init() { func init() { proto.RegisterFile("panacea/oracle/v2/tx.proto", fileDescriptor_91b53ec53f5557f6) } var fileDescriptor_91b53ec53f5557f6 = []byte{ - // 716 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x4f, 0xdb, 0x48, - 0x14, 0x8e, 0xc9, 0x2e, 0x0b, 0xb3, 0x2c, 0x02, 0xc3, 0x22, 0xc7, 0x61, 0x43, 0x14, 0xb1, 0x08, - 0xad, 0xc0, 0xb3, 0x64, 0x0f, 0xab, 0xdd, 0x1b, 0xb4, 0x87, 0x22, 0x14, 0x81, 0x2c, 0xf5, 0xd2, - 0x8b, 0x35, 0xb6, 0x1f, 0xce, 0x94, 0xd8, 0x33, 0xcc, 0x8c, 0x23, 0xf2, 0x03, 0x7a, 0xef, 0xad, - 0x3f, 0xa1, 0x52, 0x7f, 0x44, 0xcf, 0x1c, 0x39, 0x56, 0x3d, 0xd0, 0x0a, 0xfe, 0x48, 0x95, 0xf1, - 0x24, 0x04, 0x92, 0x20, 0x2a, 0xf5, 0x94, 0xf8, 0x7d, 0xdf, 0xfb, 0xde, 0xf3, 0x7b, 0x9f, 0x1f, - 0x72, 0x39, 0xc9, 0x48, 0x04, 0x04, 0x33, 0x41, 0xa2, 0x0e, 0xe0, 0x6e, 0x13, 0xab, 0x0b, 0x8f, - 0x0b, 0xa6, 0x98, 0xbd, 0x6c, 0x30, 0xaf, 0xc0, 0xbc, 0x6e, 0xd3, 0x5d, 0x4d, 0x58, 0xc2, 0x34, - 0x8a, 0xfb, 0xff, 0x0a, 0xa2, 0xbb, 0x91, 0x30, 0x96, 0x74, 0x00, 0xeb, 0xa7, 0x30, 0x3f, 0xc5, - 0x8a, 0xa6, 0x20, 0x15, 0x49, 0xb9, 0x21, 0xfc, 0x15, 0x31, 0x99, 0x32, 0x89, 0x43, 0x22, 0x01, - 0x9f, 0xe7, 0x20, 0x7a, 0xb8, 0xbb, 0x17, 0x82, 0x22, 0x7b, 0x98, 0x93, 0x84, 0x66, 0x44, 0x51, - 0x96, 0x19, 0xee, 0xba, 0x11, 0x23, 0x9c, 0x62, 0x92, 0x65, 0x4c, 0x69, 0x50, 0x1a, 0xb4, 0x36, - 0xde, 0xaf, 0xe9, 0xce, 0xb4, 0x32, 0x8e, 0x27, 0x90, 0x81, 0xa4, 0x46, 0xa0, 0xf1, 0xae, 0x8c, - 0x96, 0x5b, 0x32, 0xf1, 0x21, 0xa1, 0x52, 0x81, 0x38, 0xd6, 0x34, 0xbb, 0x8a, 0xe6, 0xf3, 0x8c, - 0x9e, 0xe7, 0x10, 0xd0, 0xd8, 0xb1, 0xea, 0xd6, 0xf6, 0xbc, 0x3f, 0x57, 0x04, 0x0e, 0x63, 0xfb, - 0x4f, 0xb4, 0x58, 0xa8, 0x05, 0x24, 0x8e, 0x05, 0x48, 0xe9, 0xcc, 0x68, 0xc6, 0x6f, 0x45, 0x74, - 0xbf, 0x08, 0xda, 0x75, 0xb4, 0x90, 0xb1, 0x18, 0x02, 0x9e, 0x87, 0xc1, 0x19, 0xf4, 0x9c, 0x72, - 0xdd, 0xda, 0x5e, 0xf0, 0x51, 0x3f, 0x76, 0x92, 0x87, 0x47, 0xd0, 0xb3, 0xff, 0x47, 0xee, 0x28, - 0x23, 0x10, 0x90, 0x32, 0x05, 0x81, 0x00, 0xce, 0x84, 0x72, 0x7e, 0xd2, 0xfc, 0xb5, 0x3b, 0xbe, - 0xaf, 0x61, 0x5f, 0xa3, 0xf6, 0xdf, 0x68, 0x55, 0x89, 0x5c, 0x2a, 0x88, 0x83, 0xb0, 0xc3, 0xa2, - 0xb3, 0xa0, 0x0d, 0x34, 0x69, 0x2b, 0xe7, 0xe7, 0xba, 0xb5, 0x5d, 0xf6, 0x6d, 0x83, 0x1d, 0xf4, - 0xa1, 0x17, 0x1a, 0xb1, 0x77, 0x90, 0xfd, 0x20, 0x83, 0xc8, 0xb6, 0x33, 0xab, 0xab, 0x2c, 0xdd, - 0xe3, 0x13, 0xd9, 0xb6, 0x5d, 0x34, 0x07, 0x59, 0xcc, 0x19, 0xcd, 0x94, 0xf3, 0x4b, 0x31, 0x80, - 0xc1, 0xb3, 0x1d, 0xa3, 0x35, 0x33, 0x80, 0x88, 0xa5, 0x29, 0x95, 0x92, 0xb2, 0x2c, 0x10, 0x44, - 0x81, 0x33, 0xd7, 0x67, 0x1e, 0x78, 0x97, 0xd7, 0x1b, 0xa5, 0xcf, 0xd7, 0x1b, 0x5b, 0x09, 0x55, - 0xed, 0x3c, 0xf4, 0x22, 0x96, 0x62, 0xb3, 0xf1, 0xe2, 0x67, 0x57, 0xc6, 0x67, 0x58, 0xf5, 0x38, - 0x48, 0xef, 0x39, 0x44, 0xfe, 0x6a, 0xa1, 0xf6, 0x6c, 0x28, 0xe6, 0x13, 0x05, 0x8d, 0x2a, 0xaa, - 0x8c, 0x2d, 0xc6, 0x07, 0xc9, 0x59, 0x26, 0xa1, 0xf1, 0xc1, 0x42, 0xeb, 0x2d, 0x99, 0xec, 0x73, - 0x2e, 0x58, 0x17, 0x06, 0x60, 0x9f, 0x2a, 0xb4, 0x3f, 0xec, 0x0e, 0xaa, 0x92, 0x02, 0x0c, 0x4c, - 0xaf, 0x62, 0x04, 0xd6, 0x3b, 0xfd, 0xb5, 0xb9, 0xe3, 0x8d, 0x59, 0xda, 0x9b, 0x2a, 0xe9, 0x57, - 0xc8, 0xd4, 0x6a, 0xeb, 0x68, 0x5e, 0xd2, 0x24, 0x23, 0x2a, 0x17, 0xa0, 0xdd, 0xb0, 0xe0, 0xdf, - 0x05, 0x1a, 0xef, 0x2d, 0x54, 0x99, 0xde, 0xe9, 0xa3, 0x5e, 0x6b, 0xa2, 0xdf, 0x15, 0x11, 0x09, - 0xa8, 0x60, 0xa2, 0xe5, 0x56, 0x0a, 0xf0, 0xf8, 0x9e, 0xf1, 0xfe, 0x43, 0x15, 0xc8, 0x22, 0xd1, - 0xe3, 0xfd, 0x55, 0x9b, 0x34, 0x2e, 0x68, 0x77, 0xc4, 0x85, 0x6b, 0x43, 0x42, 0x91, 0x7a, 0x22, - 0x68, 0xf7, 0x08, 0x7a, 0x8d, 0x2d, 0xb4, 0xf9, 0xd8, 0x54, 0x87, 0xe3, 0xff, 0x68, 0xa1, 0x95, - 0x96, 0x4c, 0x5e, 0xf2, 0x98, 0x28, 0xc3, 0x3b, 0xcc, 0x4e, 0xd9, 0x84, 0x4f, 0xc3, 0x9a, 0xf4, - 0x69, 0x8c, 0x9a, 0x6b, 0xe6, 0xc9, 0xe6, 0x2a, 0xff, 0x40, 0x73, 0xfd, 0x81, 0xaa, 0x13, 0xfa, - 0x1f, 0xbc, 0x5f, 0xf3, 0xcb, 0x0c, 0x2a, 0xb7, 0x64, 0x62, 0xc7, 0x68, 0xf1, 0xc1, 0x65, 0xd8, - 0x9c, 0x60, 0x99, 0x31, 0x9b, 0xba, 0x3b, 0x4f, 0x61, 0x0d, 0xaa, 0xd9, 0x6f, 0x1e, 0xf5, 0x07, - 0x9e, 0xac, 0x35, 0x35, 0xc1, 0xfd, 0xf7, 0x3b, 0x13, 0x86, 0x7d, 0xbc, 0x46, 0x4b, 0x63, 0x1b, - 0xdd, 0x9a, 0x2c, 0xf6, 0x90, 0xe7, 0x7a, 0x4f, 0xe3, 0x0d, 0x6a, 0x1d, 0x1c, 0x5e, 0xde, 0xd4, - 0xac, 0xab, 0x9b, 0x9a, 0xf5, 0xf5, 0xa6, 0x66, 0xbd, 0xbd, 0xad, 0x95, 0xae, 0x6e, 0x6b, 0xa5, - 0x4f, 0xb7, 0xb5, 0xd2, 0x2b, 0x3c, 0xb2, 0xd8, 0x14, 0x62, 0xda, 0x3f, 0x56, 0xd8, 0x88, 0xef, - 0x46, 0x4c, 0x00, 0xbe, 0x18, 0x5c, 0x73, 0xbd, 0xe5, 0x70, 0x56, 0x5f, 0xf2, 0x7f, 0xbe, 0x05, - 0x00, 0x00, 0xff, 0xff, 0xe1, 0x73, 0xae, 0x44, 0xbc, 0x06, 0x00, 0x00, + // 730 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x4f, 0xdb, 0x4a, + 0x14, 0x8d, 0xc9, 0x7b, 0x3c, 0x98, 0xc7, 0x43, 0x60, 0x3e, 0x9e, 0xe3, 0xf0, 0x42, 0x14, 0xf1, + 0x10, 0xaa, 0xc0, 0x53, 0x52, 0xa9, 0x55, 0xbb, 0x83, 0x76, 0x51, 0x84, 0x22, 0x90, 0xa5, 0x6e, + 0xba, 0xb1, 0xc6, 0xf6, 0xc5, 0x99, 0x12, 0x7b, 0xcc, 0xcc, 0x38, 0x22, 0x3f, 0xa0, 0xfb, 0xee, + 0xfa, 0x1f, 0xfa, 0x23, 0xba, 0x66, 0xc9, 0xb2, 0xea, 0x02, 0x2a, 0xf8, 0x23, 0x55, 0xc6, 0x93, + 0x10, 0xf2, 0x81, 0xa8, 0xd4, 0x55, 0xe2, 0x7b, 0xce, 0x3d, 0xf7, 0xda, 0xe7, 0xd8, 0x83, 0xec, + 0x94, 0x24, 0x24, 0x00, 0x82, 0x19, 0x27, 0x41, 0x0b, 0x70, 0xbb, 0x8e, 0xe5, 0xb9, 0x93, 0x72, + 0x26, 0x99, 0xb9, 0xa8, 0x31, 0x27, 0xc7, 0x9c, 0x76, 0xdd, 0x5e, 0x8e, 0x58, 0xc4, 0x14, 0x8a, + 0xbb, 0xff, 0x72, 0xa2, 0xbd, 0x1e, 0x31, 0x16, 0xb5, 0x00, 0xab, 0x2b, 0x3f, 0x3b, 0xc1, 0x92, + 0xc6, 0x20, 0x24, 0x89, 0x53, 0x4d, 0x78, 0x12, 0x30, 0x11, 0x33, 0x81, 0x7d, 0x22, 0x00, 0x9f, + 0x65, 0xc0, 0x3b, 0xb8, 0xbd, 0xeb, 0x83, 0x24, 0xbb, 0x38, 0x25, 0x11, 0x4d, 0x88, 0xa4, 0x2c, + 0xd1, 0xdc, 0x35, 0x2d, 0x46, 0x52, 0x8a, 0x49, 0x92, 0x30, 0xa9, 0x40, 0xa1, 0xd1, 0xca, 0xe8, + 0xbe, 0x7a, 0x3b, 0xbd, 0xca, 0x28, 0x1e, 0x41, 0x02, 0x82, 0x6a, 0x81, 0xda, 0xe7, 0x22, 0x5a, + 0x6c, 0x88, 0xc8, 0x85, 0x88, 0x0a, 0x09, 0xfc, 0x48, 0xd1, 0xcc, 0x32, 0x9a, 0xcd, 0x12, 0x7a, + 0x96, 0x81, 0x47, 0x43, 0xcb, 0xa8, 0x1a, 0x5b, 0xb3, 0xee, 0x4c, 0x5e, 0x38, 0x08, 0xcd, 0xff, + 0xd1, 0x7c, 0xae, 0xe6, 0x91, 0x30, 0xe4, 0x20, 0x84, 0x35, 0xa5, 0x18, 0xff, 0xe4, 0xd5, 0xbd, + 0xbc, 0x68, 0x56, 0xd1, 0x5c, 0xc2, 0x42, 0xf0, 0xd2, 0xcc, 0xf7, 0x4e, 0xa1, 0x63, 0x15, 0xab, + 0xc6, 0xd6, 0x9c, 0x8b, 0xba, 0xb5, 0xe3, 0xcc, 0x3f, 0x84, 0x8e, 0xf9, 0x0a, 0xd9, 0x83, 0x0c, + 0x8f, 0x43, 0xcc, 0x24, 0x78, 0x1c, 0x52, 0xc6, 0xa5, 0xf5, 0x87, 0xe2, 0xaf, 0xde, 0xf1, 0x5d, + 0x05, 0xbb, 0x0a, 0x35, 0x9f, 0xa2, 0x65, 0xc9, 0x33, 0x21, 0x21, 0xf4, 0xfc, 0x16, 0x0b, 0x4e, + 0xbd, 0x26, 0xd0, 0xa8, 0x29, 0xad, 0x3f, 0xab, 0xc6, 0x56, 0xd1, 0x35, 0x35, 0xb6, 0xdf, 0x85, + 0xde, 0x2a, 0xc4, 0xdc, 0x46, 0xe6, 0x50, 0x07, 0x11, 0x4d, 0x6b, 0x5a, 0x4d, 0x59, 0xb8, 0xc7, + 0x27, 0xa2, 0x69, 0xda, 0x68, 0x06, 0x92, 0x30, 0x65, 0x34, 0x91, 0xd6, 0x5f, 0xf9, 0x03, 0xe8, + 0x5d, 0x9b, 0x21, 0x5a, 0xd5, 0x0f, 0x20, 0x60, 0x71, 0x4c, 0x85, 0xa0, 0x2c, 0xf1, 0x38, 0x91, + 0x60, 0xcd, 0x74, 0x99, 0xfb, 0xce, 0xc5, 0xd5, 0x7a, 0xe1, 0xfb, 0xd5, 0xfa, 0x66, 0x44, 0x65, + 0x33, 0xf3, 0x9d, 0x80, 0xc5, 0x58, 0x3b, 0x9e, 0xff, 0xec, 0x88, 0xf0, 0x14, 0xcb, 0x4e, 0x0a, + 0xc2, 0x79, 0x03, 0x81, 0xbb, 0x9c, 0xab, 0xbd, 0xee, 0x8b, 0xb9, 0x44, 0x42, 0xad, 0x8c, 0x4a, + 0x23, 0xc6, 0xb8, 0x20, 0x52, 0x96, 0x08, 0xa8, 0x7d, 0x31, 0xd0, 0x5a, 0x43, 0x44, 0x7b, 0x69, + 0xca, 0x59, 0x1b, 0x7a, 0x60, 0x97, 0xca, 0x55, 0x3e, 0xcc, 0x16, 0x2a, 0x93, 0x1c, 0xf4, 0xf4, + 0xae, 0x7c, 0x00, 0x56, 0x9e, 0xfe, 0x5d, 0xdf, 0x76, 0x46, 0x22, 0xed, 0x4c, 0x94, 0x74, 0x4b, + 0x64, 0xe2, 0xb4, 0x35, 0x34, 0x2b, 0x68, 0x94, 0x10, 0x99, 0x71, 0x50, 0x69, 0x98, 0x73, 0xef, + 0x0a, 0xb5, 0x6b, 0x03, 0x95, 0x26, 0x6f, 0xfa, 0x60, 0xd6, 0x9e, 0xa3, 0x7f, 0xf5, 0xd4, 0xd0, + 0x1b, 0x1b, 0xba, 0x95, 0x1e, 0x7c, 0x74, 0x2f, 0x7c, 0x75, 0xb4, 0x22, 0x09, 0x8f, 0x40, 0x0e, + 0x77, 0x15, 0x55, 0xd7, 0x52, 0x0e, 0xde, 0xef, 0x79, 0x89, 0x4a, 0x90, 0x04, 0xbc, 0x93, 0xca, + 0xbb, 0x61, 0x29, 0xa7, 0x6d, 0x95, 0x5e, 0x9d, 0xc6, 0x3e, 0x21, 0x6f, 0x3d, 0xe6, 0xb4, 0x7d, + 0x08, 0x9d, 0xda, 0x26, 0xda, 0x78, 0xc8, 0x8d, 0xbe, 0x6d, 0x5f, 0x0d, 0xb4, 0xd4, 0x10, 0xd1, + 0xbb, 0x34, 0x24, 0x52, 0xf3, 0x0e, 0x92, 0x13, 0x36, 0xe6, 0x95, 0x32, 0xc6, 0xbd, 0x52, 0x83, + 0xa1, 0x9c, 0x7a, 0x74, 0x28, 0x8b, 0xbf, 0x31, 0x94, 0xff, 0xa1, 0xf2, 0x98, 0xfd, 0x7b, 0xf7, + 0x57, 0xbf, 0x9e, 0x42, 0xc5, 0x86, 0x88, 0xcc, 0x10, 0xcd, 0x0f, 0x7d, 0x51, 0x36, 0xc6, 0x44, + 0x6d, 0x24, 0xde, 0xf6, 0xf6, 0x63, 0x58, 0xbd, 0x69, 0xe6, 0xc7, 0x07, 0x73, 0x85, 0xc7, 0x6b, + 0x4d, 0x6c, 0xb0, 0x5f, 0xfc, 0x62, 0x43, 0x7f, 0x8f, 0x0f, 0x68, 0x61, 0xc4, 0xd1, 0xcd, 0xf1, + 0x62, 0xc3, 0x3c, 0xdb, 0x79, 0x1c, 0xaf, 0x37, 0x6b, 0xff, 0xe0, 0xe2, 0xa6, 0x62, 0x5c, 0xde, + 0x54, 0x8c, 0x1f, 0x37, 0x15, 0xe3, 0xd3, 0x6d, 0xa5, 0x70, 0x79, 0x5b, 0x29, 0x7c, 0xbb, 0xad, + 0x14, 0xde, 0xe3, 0x01, 0x63, 0x63, 0x08, 0x69, 0xf7, 0x23, 0x87, 0xb5, 0xf8, 0x4e, 0xc0, 0x38, + 0xe0, 0xf3, 0xde, 0x29, 0xa0, 0x5c, 0xf6, 0xa7, 0xd5, 0x09, 0xf0, 0xec, 0x67, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xae, 0xec, 0xee, 0x93, 0xf4, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -794,13 +803,20 @@ func (m *ApproveOracleRegistration) MarshalToSizedBuffer(dAtA []byte) (int, erro copy(dAtA[i:], m.EncryptedOraclePrivKey) i = encodeVarintTx(dAtA, i, uint64(len(m.EncryptedOraclePrivKey))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 } if len(m.TargetOracleAddress) > 0 { i -= len(m.TargetOracleAddress) copy(dAtA[i:], m.TargetOracleAddress) i = encodeVarintTx(dAtA, i, uint64(len(m.TargetOracleAddress))) i-- + dAtA[i] = 0x1a + } + if len(m.ApprovedOracleAddress) > 0 { + i -= len(m.ApprovedOracleAddress) + copy(dAtA[i:], m.ApprovedOracleAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ApprovedOracleAddress))) + i-- dAtA[i] = 0x12 } if len(m.UniqueId) > 0 { @@ -991,6 +1007,10 @@ func (m *ApproveOracleRegistration) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + l = len(m.ApprovedOracleAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } l = len(m.TargetOracleAddress) if l > 0 { n += 1 + l + sovTx(uint64(l)) @@ -1578,6 +1598,38 @@ func (m *ApproveOracleRegistration) Unmarshal(dAtA []byte) error { m.UniqueId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ApprovedOracleAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ApprovedOracleAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TargetOracleAddress", wireType) } @@ -1609,7 +1661,7 @@ func (m *ApproveOracleRegistration) Unmarshal(dAtA []byte) error { } m.TargetOracleAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptedOraclePrivKey", wireType) } From 01bfc2f63e99512d2677bd02e8fbcc4010ba2fc0 Mon Sep 17 00:00:00 2001 From: gyuguen Date: Tue, 6 Dec 2022 15:06:28 +0900 Subject: [PATCH 2/7] fix --- x/oracle/client/cli/txRegisterOracle.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/x/oracle/client/cli/txRegisterOracle.go b/x/oracle/client/cli/txRegisterOracle.go index c7b023da..ecc52da2 100644 --- a/x/oracle/client/cli/txRegisterOracle.go +++ b/x/oracle/client/cli/txRegisterOracle.go @@ -78,16 +78,3 @@ func CmdRegisterOracle() *cobra.Command { return cmd } - -func getOracleCommissionRate(oracleCommissionRateStr string) (*sdk.Dec, error) { - if len(oracleCommissionRateStr) == 1 { - return nil, fmt.Errorf("oracleCommissionRate is empty") - } - - if len(oracleCommissionRateStr) != 0 { - oracleCommissionRate, err := sdk.NewDecFromStr(oracleCommissionRateStr) - return &oracleCommissionRate, err - } - - return nil, nil -} From d65080d864952c2cec1f7278f65bebb9fdffd9a6 Mon Sep 17 00:00:00 2001 From: gyuguen Date: Tue, 6 Dec 2022 15:13:57 +0900 Subject: [PATCH 3/7] add check uniqueID in params --- x/oracle/keeper/oracle.go | 5 ++--- x/oracle/keeper/oracle_test.go | 26 ++++++++++++++++++++++++++ x/oracle/types/params.go | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/x/oracle/keeper/oracle.go b/x/oracle/keeper/oracle.go index 0e2b0281..b19f6e53 100644 --- a/x/oracle/keeper/oracle.go +++ b/x/oracle/keeper/oracle.go @@ -15,11 +15,10 @@ func (k Keeper) RegisterOracle(ctx sdk.Context, msg *types.MsgRegisterOracle) er return err } - // TODO implement required to params - /*params := k.GetParams(ctx) + params := k.GetParams(ctx) if params.UniqueId != oracleRegistration.UniqueId { return sdkerrors.Wrapf(types.ErrOracleRegistration, "is not match the currently active uniqueID") - }*/ + } if oracle, err := k.GetOracle(ctx, oracleRegistration.OracleAddress); !errors.Is(types.ErrOracleNotFound, err) { if oracle != nil { diff --git a/x/oracle/keeper/oracle_test.go b/x/oracle/keeper/oracle_test.go index 267dcefd..3336c26b 100644 --- a/x/oracle/keeper/oracle_test.go +++ b/x/oracle/keeper/oracle_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "encoding/base64" "fmt" "testing" @@ -69,6 +70,12 @@ func (suite *oracleTestSuite) BeforeTest(_, _ string) { suite.endpoint = "https://my-validator.org" suite.oracleCommissionRate = sdk.NewDecWithPrec(1, 1) + + suite.OracleKeeper.SetParams(suite.Ctx, types.Params{ + OraclePublicKey: base64.StdEncoding.EncodeToString(suite.oraclePubKey.SerializeCompressed()), + OraclePubKeyRemoteReport: "", + UniqueId: suite.uniqueID, + }) } func (suite *oracleTestSuite) TestRegisterOracleSuccess() { @@ -195,3 +202,22 @@ func (suite *oracleTestSuite) TestRegisterOracleAlreadyExistOracle() { suite.Require().Error(err, types.ErrOracleRegistration) suite.Require().ErrorContains(err, fmt.Sprintf("already registered oracle. address(%s)", msgRegisterOracle.OracleAddress)) } + +func (suite *oracleTestSuite) TestRegisterOracleNotSameUniqueID() { + ctx := suite.Ctx + + msgRegisterOracle := &types.MsgRegisterOracle{ + UniqueId: "wrongUniqueID", + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, + TrustedBlockHeight: suite.trustedBlockHeight, + TrustedBlockHash: suite.trustedBlockHash, + Endpoint: suite.endpoint, + OracleCommissionRate: suite.oracleCommissionRate, + } + + err := suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + suite.Require().Error(err, types.ErrOracleRegistration) + suite.Require().ErrorContains(err, "is not match the currently active uniqueID") +} diff --git a/x/oracle/types/params.go b/x/oracle/types/params.go index e5fdb39b..c36c9548 100644 --- a/x/oracle/types/params.go +++ b/x/oracle/types/params.go @@ -36,7 +36,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { } } -func (p Params) Validate() error { +func (p *Params) Validate() error { if err := validateOraclePublicKey(p.OraclePublicKey); err != nil { return err } From c1f5e78eae1c39b9c3ae663a3ea986138ef8915f Mon Sep 17 00:00:00 2001 From: gyuguen Date: Tue, 6 Dec 2022 17:19:41 +0900 Subject: [PATCH 4/7] feat: add oracle modules query client --- proto/panacea/oracle/v2/query.proto | 3 +- x/oracle/client/cli/query.go | 6 + x/oracle/client/cli/queryOracle.go | 72 +++++++ .../client/cli/queryOracleRegistration.go | 75 +++++++ x/oracle/keeper/grpc_query_oracle.go | 91 +++++++-- x/oracle/keeper/grpc_query_oracle_test.go | 193 ++++++++++++++++++ x/oracle/types/query.pb.go | 140 +++++++++---- 7 files changed, 523 insertions(+), 57 deletions(-) create mode 100644 x/oracle/client/cli/queryOracle.go create mode 100644 x/oracle/client/cli/queryOracleRegistration.go create mode 100644 x/oracle/keeper/grpc_query_oracle_test.go diff --git a/proto/panacea/oracle/v2/query.proto b/proto/panacea/oracle/v2/query.proto index 278af92a..d6756ede 100644 --- a/proto/panacea/oracle/v2/query.proto +++ b/proto/panacea/oracle/v2/query.proto @@ -61,7 +61,8 @@ message QueryOracleResponse { // QueryOracleRegistrationsRequest is the request type for the Query/OracleRegistrations RPC method. message QueryOracleRegistrationsRequest { - cosmos.base.query.v1beta1.PageRequest pagination = 1; + string unique_id = 1; + cosmos.base.query.v1beta1.PageRequest pagination = 2; } // QueryOracleRegistrationsResponse is the response type for the Query/OracleRegistrations RPC method. diff --git a/x/oracle/client/cli/query.go b/x/oracle/client/cli/query.go index 043255c8..b813f2a6 100644 --- a/x/oracle/client/cli/query.go +++ b/x/oracle/client/cli/query.go @@ -18,5 +18,11 @@ func GetQueryCmd(queryRoute string) *cobra.Command { SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } + + cmd.AddCommand(CmdGetOracles()) + cmd.AddCommand(CmdGetOracle()) + cmd.AddCommand(CmdGetOracleRegistrations()) + cmd.AddCommand(CmdGetOracleRegistration()) + return cmd } diff --git a/x/oracle/client/cli/queryOracle.go b/x/oracle/client/cli/queryOracle.go new file mode 100644 index 00000000..ae792ace --- /dev/null +++ b/x/oracle/client/cli/queryOracle.go @@ -0,0 +1,72 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/medibloc/panacea-core/v2/x/oracle/types" + "github.com/spf13/cobra" +) + +func CmdGetOracles() *cobra.Command { + cmd := &cobra.Command{ + Use: "oracles", + Short: "Query oracles info", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryOraclesRequest{ + Pagination: pageReq, + } + res, err := queryClient.Oracles(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "oracles") + return cmd +} + +func CmdGetOracle() *cobra.Command { + cmd := &cobra.Command{ + Use: "oracle [oracle-address]", + Short: "Query a oracle info", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryOracleRequest{ + OracleAddress: args[0], + } + res, err := queryClient.Oracle(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/oracle/client/cli/queryOracleRegistration.go b/x/oracle/client/cli/queryOracleRegistration.go new file mode 100644 index 00000000..5f2182be --- /dev/null +++ b/x/oracle/client/cli/queryOracleRegistration.go @@ -0,0 +1,75 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/medibloc/panacea-core/v2/x/oracle/types" + "github.com/spf13/cobra" +) + +func CmdGetOracleRegistrations() *cobra.Command { + cmd := &cobra.Command{ + Use: "oracle-registrations [unique-id]", + Short: "Query oracle-registrations info", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryOracleRegistrationsRequest{ + UniqueId: args[0], + Pagination: pageReq, + } + res, err := queryClient.OracleRegistrations(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "oracle-registrations") + + return cmd +} + +func CmdGetOracleRegistration() *cobra.Command { + cmd := &cobra.Command{ + Use: "oracle-registration [unique-id] [oracle-address]", + Short: "Query a oracle registration info", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.OracleRegistration(cmd.Context(), &types.QueryOracleRegistrationRequest{ + UniqueId: args[0], + OracleAddress: args[1], + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/oracle/keeper/grpc_query_oracle.go b/x/oracle/keeper/grpc_query_oracle.go index 34b75319..f6951fd9 100644 --- a/x/oracle/keeper/grpc_query_oracle.go +++ b/x/oracle/keeper/grpc_query_oracle.go @@ -3,27 +3,94 @@ package keeper import ( "context" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" "github.com/medibloc/panacea-core/v2/x/oracle/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) -func (k Keeper) Oracles(ctx context.Context, request *types.QueryOraclesRequest) (*types.QueryOraclesResponse, error) { - //TODO implement me - panic("implement me") +func (k Keeper) Oracles(goCtx context.Context, request *types.QueryOraclesRequest) (*types.QueryOraclesResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + store := ctx.KVStore(k.storeKey) + oracleStore := prefix.NewStore(store, types.OraclesKey) + + var oracles []*types.Oracle + pageRes, err := query.Paginate(oracleStore, request.Pagination, func(_, value []byte) error { + var oracle types.Oracle + if err := k.cdc.UnmarshalLengthPrefixed(value, &oracle); err != nil { + return err + } + + oracles = append(oracles, &oracle) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryOraclesResponse{ + Oracles: oracles, + Pagination: pageRes, + }, nil } -func (k Keeper) Oracle(ctx context.Context, request *types.QueryOracleRequest) (*types.QueryOracleResponse, error) { - //TODO implement me - panic("implement me") +func (k Keeper) Oracle(goCtx context.Context, request *types.QueryOracleRequest) (*types.QueryOracleResponse, error) { + oracle, err := k.GetOracle(sdk.UnwrapSDKContext(goCtx), request.OracleAddress) + if err != nil { + return nil, err + } + + return &types.QueryOracleResponse{ + Oracle: oracle, + }, nil } -func (k Keeper) OracleRegistrations(ctx context.Context, request *types.QueryOracleRegistrationsRequest) (*types.QueryOracleRegistrationsResponse, error) { - //TODO implement me - panic("implement me") +func (k Keeper) OracleRegistrations(goCtx context.Context, request *types.QueryOracleRegistrationsRequest) (*types.QueryOracleRegistrationsResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + store := ctx.KVStore(k.storeKey) + oracleRegistrationStore := prefix.NewStore(store, types.OracleRegistrationKey) + + var oracleRegistrations []*types.OracleRegistration + pageRes, err := query.Paginate(oracleRegistrationStore, request.Pagination, func(_, value []byte) error { + var oracleRegistration types.OracleRegistration + if err := k.cdc.UnmarshalLengthPrefixed(value, &oracleRegistration); err != nil { + return err + } + + oracleRegistrations = append(oracleRegistrations, &oracleRegistration) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryOracleRegistrationsResponse{ + OracleRegistrations: oracleRegistrations, + Pagination: pageRes, + }, nil } -func (k Keeper) OracleRegistration(ctx context.Context, request *types.QueryOracleRegistrationRequest) (*types.QueryOracleRegistrationResponse, error) { - //TODO implement me - panic("implement me") +func (k Keeper) OracleRegistration(goCtx context.Context, request *types.QueryOracleRegistrationRequest) (*types.QueryOracleRegistrationResponse, error) { + oracleRegistration, err := k.GetOracleRegistration(sdk.UnwrapSDKContext(goCtx), request.UniqueId, request.OracleAddress) + if err != nil { + return nil, err + } + + return &types.QueryOracleRegistrationResponse{OracleRegistration: oracleRegistration}, nil } func (k Keeper) Params(ctx context.Context, request *types.QueryOracleParamsRequest) (*types.QueryParamsResponse, error) { diff --git a/x/oracle/keeper/grpc_query_oracle_test.go b/x/oracle/keeper/grpc_query_oracle_test.go new file mode 100644 index 00000000..64bf48bf --- /dev/null +++ b/x/oracle/keeper/grpc_query_oracle_test.go @@ -0,0 +1,193 @@ +package keeper_test + +import ( + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/medibloc/panacea-core/v2/types/testsuite" + "github.com/medibloc/panacea-core/v2/x/oracle/types" + "github.com/stretchr/testify/suite" +) + +type queryOracleTestSuite struct { + testsuite.TestSuite + + uniqueID string + + oracleAccPrivKey cryptotypes.PrivKey + oracleAccPubKey cryptotypes.PubKey + oracleAccAddr sdk.AccAddress + oracleEndpoint string + oracleCommissionRate sdk.Dec + + oracle2AccPrivKey cryptotypes.PrivKey + oracle2AccPubKey cryptotypes.PubKey + oracle2AccAddr sdk.AccAddress + oracle2Endpoint string + oracle2CommissionRate sdk.Dec + + nodePrivKey *btcec.PrivateKey + nodePubKey *btcec.PublicKey +} + +func TestQueryOracleTestSuite(t *testing.T) { + suite.Run(t, new(queryOracleTestSuite)) +} + +func (suite *queryOracleTestSuite) BeforeTest(_, _ string) { + suite.uniqueID = "correctUniqueID" + + suite.oracleAccPrivKey = secp256k1.GenPrivKey() + suite.oracleAccPubKey = suite.oracleAccPrivKey.PubKey() + suite.oracleAccAddr = sdk.AccAddress(suite.oracleAccPubKey.Address()) + suite.oracleEndpoint = "https://my-validator.org" + suite.oracleCommissionRate = sdk.NewDecWithPrec(1, 1) + + suite.oracle2AccPrivKey = secp256k1.GenPrivKey() + suite.oracle2AccPubKey = suite.oracle2AccPrivKey.PubKey() + suite.oracle2AccAddr = sdk.AccAddress(suite.oracle2AccPubKey.Address()) + suite.oracle2Endpoint = "https://my-validator2.org" + suite.oracle2CommissionRate = sdk.NewDecWithPrec(1, 2) + + suite.nodePrivKey, _ = btcec.NewPrivateKey(btcec.S256()) + suite.nodePubKey = suite.nodePrivKey.PubKey() +} + +func (suite *queryOracleTestSuite) TestOracles() { + ctx := suite.Ctx + oracleKeeper := suite.OracleKeeper + + oracle := types.NewOracle(suite.oracleAccAddr.String(), suite.uniqueID, suite.oracleEndpoint, suite.oracleCommissionRate) + err := oracleKeeper.SetOracle(ctx, oracle) + suite.Require().NoError(err) + + oracle2 := types.NewOracle(suite.oracle2AccAddr.String(), suite.uniqueID, suite.oracle2Endpoint, suite.oracle2CommissionRate) + err = oracleKeeper.SetOracle(ctx, oracle2) + suite.Require().NoError(err) + + req := types.QueryOraclesRequest{ + Pagination: &query.PageRequest{}, + } + + res, err := oracleKeeper.Oracles(sdk.WrapSDKContext(ctx), &req) + suite.Require().NoError(err) + suite.Require().Equal(2, len(res.Oracles)) + for _, oracle := range res.Oracles { + switch oracle.OracleAddress { + case suite.oracleAccAddr.String(): + suite.Require().Equal(suite.uniqueID, oracle.UniqueId) + suite.Require().Equal(suite.oracleEndpoint, oracle.Endpoint) + suite.Require().Equal(suite.oracleCommissionRate, oracle.OracleCommissionRate) + case suite.oracle2AccAddr.String(): + suite.Require().Equal(suite.uniqueID, oracle.UniqueId) + suite.Require().Equal(suite.oracle2Endpoint, oracle.Endpoint) + suite.Require().Equal(suite.oracle2CommissionRate, oracle.OracleCommissionRate) + default: + panic("not found oracle address. address: " + oracle.OracleAddress) + } + } +} + +func (suite *queryOracleTestSuite) TestOracle() { + ctx := suite.Ctx + oracleKeeper := suite.OracleKeeper + + oracle := types.NewOracle(suite.oracleAccAddr.String(), suite.uniqueID, suite.oracleEndpoint, suite.oracleCommissionRate) + err := oracleKeeper.SetOracle(ctx, oracle) + suite.Require().NoError(err) + + req := types.QueryOracleRequest{ + OracleAddress: suite.oracleAccAddr.String(), + } + res, err := oracleKeeper.Oracle(sdk.WrapSDKContext(ctx), &req) + suite.Require().NoError(err) + suite.Require().Equal(oracle, res.Oracle) +} + +func (suite *queryOracleTestSuite) TestOracleRegistrations() { + ctx := suite.Ctx + oracleKeeper := suite.OracleKeeper + + remoteReport := []byte("nodePubKeyRemoteReport") + trustedBlockHash := []byte("hash") + + oracleRegistration := &types.OracleRegistration{ + UniqueId: suite.uniqueID, + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: remoteReport, + TrustedBlockHeight: 10, + TrustedBlockHash: trustedBlockHash, + Endpoint: suite.oracleEndpoint, + OracleCommissionRate: suite.oracleCommissionRate, + } + + oracleRegistration2 := &types.OracleRegistration{ + UniqueId: suite.uniqueID, + OracleAddress: suite.oracle2AccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: remoteReport, + TrustedBlockHeight: 10, + TrustedBlockHash: trustedBlockHash, + Endpoint: suite.oracle2Endpoint, + OracleCommissionRate: suite.oracle2CommissionRate, + } + + err := oracleKeeper.SetOracleRegistration(ctx, oracleRegistration) + suite.Require().NoError(err) + err = oracleKeeper.SetOracleRegistration(ctx, oracleRegistration2) + suite.Require().NoError(err) + + req := types.QueryOracleRegistrationsRequest{ + UniqueId: suite.uniqueID, + Pagination: &query.PageRequest{}, + } + res, err := oracleKeeper.OracleRegistrations(sdk.WrapSDKContext(ctx), &req) + suite.Require().NoError(err) + suite.Require().Equal(2, len(res.OracleRegistrations)) + for _, oracleRegistration := range res.OracleRegistrations { + switch oracleRegistration.OracleAddress { + case suite.oracleAccAddr.String(): + suite.Require().Equal(suite.uniqueID, oracleRegistration.UniqueId) + suite.Require().Equal(suite.nodePubKey.SerializeCompressed(), oracleRegistration.NodePubKey) + suite.Require().Equal(remoteReport, oracleRegistration.NodePubKeyRemoteReport) + suite.Require().Equal(int64(10), oracleRegistration.TrustedBlockHeight) + suite.Require().Equal(trustedBlockHash, oracleRegistration.TrustedBlockHash) + suite.Require().Equal(suite.oracleEndpoint, oracleRegistration.Endpoint) + suite.Require().Equal(suite.oracleCommissionRate, oracleRegistration.OracleCommissionRate) + case suite.oracle2AccAddr.String(): + suite.Require().Equal(suite.uniqueID, oracleRegistration.UniqueId) + suite.Require().Equal(suite.nodePubKey.SerializeCompressed(), oracleRegistration.NodePubKey) + suite.Require().Equal(remoteReport, oracleRegistration.NodePubKeyRemoteReport) + suite.Require().Equal(int64(10), oracleRegistration.TrustedBlockHeight) + suite.Require().Equal(trustedBlockHash, oracleRegistration.TrustedBlockHash) + suite.Require().Equal(suite.oracle2Endpoint, oracleRegistration.Endpoint) + suite.Require().Equal(suite.oracle2CommissionRate, oracleRegistration.OracleCommissionRate) + default: + panic("not found oracle address. address: " + oracleRegistration.OracleAddress) + } + } +} + +func (suite *queryOracleTestSuite) TestOracleRegistration() { + ctx := suite.Ctx + oracleKeeper := suite.OracleKeeper + + oracleRegistration := &types.OracleRegistration{ + UniqueId: suite.uniqueID, + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: []byte("nodePubKeyRemoteReport"), + TrustedBlockHeight: 10, + TrustedBlockHash: []byte("hash"), + Endpoint: suite.oracleEndpoint, + OracleCommissionRate: suite.oracleCommissionRate, + } + err := oracleKeeper.SetOracleRegistration(ctx, oracleRegistration) + suite.Require().NoError(err) + +} diff --git a/x/oracle/types/query.pb.go b/x/oracle/types/query.pb.go index 7b66881a..91538028 100644 --- a/x/oracle/types/query.pb.go +++ b/x/oracle/types/query.pb.go @@ -221,7 +221,8 @@ func (m *QueryOracleResponse) GetOracle() *Oracle { // QueryOracleRegistrationsRequest is the request type for the Query/OracleRegistrations RPC method. type QueryOracleRegistrationsRequest struct { - Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` + UniqueId string `protobuf:"bytes,1,opt,name=unique_id,json=uniqueId,proto3" json:"unique_id,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryOracleRegistrationsRequest) Reset() { *m = QueryOracleRegistrationsRequest{} } @@ -257,6 +258,13 @@ func (m *QueryOracleRegistrationsRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryOracleRegistrationsRequest proto.InternalMessageInfo +func (m *QueryOracleRegistrationsRequest) GetUniqueId() string { + if m != nil { + return m.UniqueId + } + return "" +} + func (m *QueryOracleRegistrationsRequest) GetPagination() *query.PageRequest { if m != nil { return m.Pagination @@ -513,49 +521,50 @@ func init() { func init() { proto.RegisterFile("panacea/oracle/v2/query.proto", fileDescriptor_79c168e4f9a93eac) } var fileDescriptor_79c168e4f9a93eac = []byte{ - // 672 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0x4d, 0x6b, 0xd4, 0x4e, - 0x1c, 0xc7, 0x3b, 0xfd, 0xf3, 0xdf, 0xda, 0x29, 0x0a, 0xce, 0xf6, 0xd0, 0xa6, 0x35, 0xad, 0x81, - 0x76, 0xab, 0xa5, 0x19, 0x36, 0x7b, 0xf4, 0xa4, 0x48, 0xb5, 0x20, 0x58, 0xf7, 0x20, 0x22, 0x48, - 0x99, 0x24, 0x63, 0x0c, 0x6c, 0x32, 0xd9, 0x4c, 0xb2, 0xb8, 0x94, 0x8a, 0x78, 0xf6, 0x20, 0x28, - 0xf8, 0x32, 0x7c, 0x15, 0x82, 0xde, 0x0a, 0x5e, 0x3c, 0xca, 0xae, 0x2f, 0x44, 0x76, 0x66, 0xb2, - 0xdd, 0x90, 0x64, 0x77, 0x0b, 0xbd, 0xb5, 0xf3, 0x7b, 0xf8, 0x7e, 0x7e, 0x4f, 0x59, 0x78, 0x2b, - 0x22, 0x21, 0x71, 0x28, 0xc1, 0x2c, 0x26, 0x4e, 0x87, 0xe2, 0x9e, 0x85, 0xbb, 0x29, 0x8d, 0xfb, - 0x66, 0x14, 0xb3, 0x84, 0xa1, 0x9b, 0xca, 0x6c, 0x4a, 0xb3, 0xd9, 0xb3, 0xb4, 0x55, 0x8f, 0x79, - 0x4c, 0x58, 0xf1, 0xe8, 0x2f, 0xe9, 0xa8, 0x6d, 0x79, 0x8c, 0x79, 0x1d, 0x8a, 0xc5, 0x7f, 0x76, - 0xfa, 0x1a, 0x27, 0x7e, 0x40, 0x79, 0x42, 0x82, 0x48, 0x39, 0xdc, 0x75, 0x18, 0x0f, 0x18, 0xc7, - 0x36, 0xe1, 0x54, 0x4a, 0xe0, 0x5e, 0xd3, 0xa6, 0x09, 0x69, 0xe2, 0x88, 0x78, 0x7e, 0x48, 0x12, - 0x9f, 0x85, 0xca, 0x77, 0x53, 0x25, 0x23, 0x91, 0x8f, 0x49, 0x18, 0xb2, 0x44, 0x18, 0xb9, 0xb2, - 0xea, 0x45, 0x64, 0x45, 0xa7, 0x50, 0x8a, 0x76, 0x8f, 0x86, 0x94, 0xfb, 0x2a, 0x81, 0xf1, 0x0a, - 0xd6, 0x9f, 0x8d, 0x00, 0x9e, 0x0a, 0x3b, 0x6f, 0xd3, 0x6e, 0x4a, 0x79, 0x82, 0x0e, 0x21, 0xbc, - 0x20, 0x59, 0x03, 0xdb, 0x60, 0x6f, 0xc5, 0xda, 0x35, 0x25, 0xb6, 0x39, 0xc2, 0x36, 0x65, 0x67, - 0x14, 0xb6, 0x79, 0x4c, 0x3c, 0xaa, 0x62, 0xdb, 0x13, 0x91, 0xc6, 0x17, 0x00, 0x57, 0xf3, 0xf9, - 0x79, 0xc4, 0x42, 0x4e, 0x51, 0x0b, 0x2e, 0x49, 0x24, 0xbe, 0x06, 0xb6, 0xff, 0xdb, 0x5b, 0xb1, - 0xd6, 0xcd, 0x42, 0x7b, 0x4d, 0x19, 0xd4, 0xce, 0x3c, 0xd1, 0xa3, 0x1c, 0xd5, 0xa2, 0xa0, 0x6a, - 0xcc, 0xa4, 0x92, 0x8a, 0x39, 0xac, 0x7b, 0x10, 0x4d, 0x50, 0x65, 0x45, 0xef, 0xc0, 0x1b, 0x52, - 0xe9, 0x84, 0xb8, 0x6e, 0x4c, 0x39, 0x17, 0x85, 0x2f, 0xb7, 0xaf, 0xcb, 0xd7, 0xfb, 0xf2, 0xd1, - 0x78, 0x9c, 0x6b, 0xd9, 0xb8, 0xa2, 0x26, 0xac, 0x49, 0x3f, 0xd5, 0xae, 0x29, 0x05, 0x29, 0x47, - 0xc3, 0x87, 0x5b, 0xb9, 0x4c, 0x9e, 0xcf, 0x93, 0x58, 0xce, 0xf7, 0xaa, 0x07, 0xf1, 0x1d, 0xc0, - 0xed, 0x6a, 0x2d, 0x55, 0xc2, 0x0b, 0xb8, 0xaa, 0x1a, 0x10, 0x4f, 0xda, 0xd5, 0x84, 0x76, 0xaa, - 0x0b, 0x9a, 0xf0, 0x6e, 0xd7, 0x59, 0x51, 0xe1, 0xea, 0x26, 0xe7, 0x42, 0xbd, 0xa2, 0x8c, 0xac, - 0x63, 0x1b, 0x70, 0x39, 0x0d, 0xfd, 0x6e, 0x4a, 0x4f, 0x7c, 0x57, 0x0d, 0xf0, 0x9a, 0x7c, 0x38, - 0x72, 0x4b, 0x46, 0xbc, 0x58, 0x36, 0xe2, 0x7e, 0xe5, 0x60, 0xc6, 0xbd, 0x7a, 0x0e, 0xeb, 0x25, - 0xbd, 0x52, 0x13, 0x9a, 0xb3, 0x55, 0xa8, 0xd8, 0x2a, 0x43, 0x83, 0x6b, 0x13, 0xd2, 0xc7, 0x24, - 0x26, 0x41, 0xb6, 0x0c, 0xe3, 0xcd, 0xcb, 0x5e, 0x2f, 0x36, 0x2f, 0x12, 0x2f, 0x53, 0x36, 0x4f, - 0x85, 0x28, 0x47, 0xeb, 0x6b, 0x0d, 0xfe, 0x2f, 0x52, 0xa1, 0x77, 0x70, 0x49, 0xdd, 0x26, 0xda, - 0x2d, 0x89, 0x2b, 0xf9, 0x38, 0x68, 0x8d, 0x99, 0x7e, 0x12, 0xcc, 0x30, 0x3e, 0xfc, 0xfa, 0xfb, - 0x79, 0x71, 0x13, 0x69, 0xb8, 0xea, 0x33, 0xc5, 0xd1, 0x47, 0x00, 0x6b, 0x32, 0x0e, 0xed, 0x4c, - 0xcf, 0x9b, 0xc9, 0xef, 0xce, 0x72, 0x53, 0xea, 0x2d, 0xa1, 0x7e, 0x80, 0xf6, 0xab, 0xd5, 0xf1, - 0x69, 0x7e, 0x1b, 0xce, 0xd0, 0x37, 0x00, 0xeb, 0x25, 0x27, 0x82, 0xac, 0x59, 0xa2, 0xc5, 0xdb, - 0xd5, 0x5a, 0x97, 0x8a, 0x51, 0xd4, 0x58, 0x50, 0xdf, 0x41, 0x8d, 0x4a, 0xea, 0xfc, 0x71, 0xa2, - 0x9f, 0x00, 0xa2, 0x62, 0x42, 0xd4, 0x9c, 0x5f, 0x3c, 0xe3, 0xb5, 0x2e, 0x13, 0xa2, 0x70, 0x9f, - 0x08, 0xdc, 0x43, 0xf4, 0x70, 0x4e, 0x5c, 0x7c, 0x3a, 0x3e, 0xce, 0xb3, 0x62, 0xf7, 0xdf, 0x03, - 0x58, 0x93, 0x9b, 0x8a, 0xf6, 0xa7, 0xc3, 0xe4, 0x0e, 0xa3, 0x7a, 0x25, 0xf2, 0x97, 0x62, 0xdc, - 0x16, 0xb4, 0x1b, 0x68, 0xbd, 0x84, 0x56, 0x5e, 0xc6, 0x83, 0xa3, 0x1f, 0x03, 0x1d, 0x9c, 0x0f, - 0x74, 0xf0, 0x67, 0xa0, 0x83, 0x4f, 0x43, 0x7d, 0xe1, 0x7c, 0xa8, 0x2f, 0xfc, 0x1e, 0xea, 0x0b, - 0x2f, 0xb1, 0xe7, 0x27, 0x6f, 0x52, 0xdb, 0x74, 0x58, 0x80, 0x03, 0xea, 0xfa, 0x76, 0x87, 0x39, - 0x59, 0x9e, 0x03, 0x87, 0xc5, 0x14, 0xbf, 0xcd, 0xd2, 0x25, 0xfd, 0x88, 0x72, 0xbb, 0x26, 0x7e, - 0x62, 0x5b, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x85, 0xc2, 0xec, 0x28, 0x58, 0x08, 0x00, 0x00, + // 681 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0x4f, 0x6b, 0xd4, 0x4e, + 0x18, 0xc7, 0x3b, 0xfb, 0xe3, 0xb7, 0xb5, 0x53, 0x14, 0x9c, 0xed, 0xa1, 0x4d, 0x6b, 0x5a, 0x03, + 0xfd, 0xa3, 0xa5, 0x19, 0x36, 0x7b, 0xf4, 0xa4, 0x48, 0xb5, 0x20, 0x58, 0xf7, 0x20, 0x22, 0x48, + 0x99, 0x24, 0x63, 0x0c, 0x34, 0x99, 0x6c, 0x26, 0x59, 0x5c, 0x4a, 0x45, 0x3c, 0x78, 0xf2, 0x20, + 0x28, 0xf8, 0x32, 0x7c, 0x15, 0x82, 0xde, 0x0a, 0x5e, 0x3c, 0xca, 0xae, 0x2f, 0x44, 0x76, 0x66, + 0xb2, 0xdd, 0x98, 0x64, 0x77, 0x2b, 0xde, 0xda, 0x79, 0xfe, 0x7d, 0x9e, 0xef, 0xf3, 0x3c, 0x59, + 0x78, 0x2d, 0x22, 0x21, 0x71, 0x28, 0xc1, 0x2c, 0x26, 0xce, 0x31, 0xc5, 0x5d, 0x0b, 0x77, 0x52, + 0x1a, 0xf7, 0xcc, 0x28, 0x66, 0x09, 0x43, 0x57, 0x95, 0xd9, 0x94, 0x66, 0xb3, 0x6b, 0x69, 0x4b, + 0x1e, 0xf3, 0x98, 0xb0, 0xe2, 0xe1, 0x5f, 0xd2, 0x51, 0x5b, 0xf7, 0x18, 0xf3, 0x8e, 0x29, 0x16, + 0xff, 0xd9, 0xe9, 0x73, 0x9c, 0xf8, 0x01, 0xe5, 0x09, 0x09, 0x22, 0xe5, 0x70, 0xd3, 0x61, 0x3c, + 0x60, 0x1c, 0xdb, 0x84, 0x53, 0x59, 0x02, 0x77, 0x9b, 0x36, 0x4d, 0x48, 0x13, 0x47, 0xc4, 0xf3, + 0x43, 0x92, 0xf8, 0x2c, 0x54, 0xbe, 0x6b, 0x2a, 0x19, 0x89, 0x7c, 0x4c, 0xc2, 0x90, 0x25, 0xc2, + 0xc8, 0x95, 0x55, 0x2f, 0x22, 0x2b, 0x3a, 0x85, 0x52, 0xb4, 0x7b, 0x34, 0xa4, 0xdc, 0x57, 0x09, + 0x8c, 0x67, 0xb0, 0xf1, 0x68, 0x08, 0xf0, 0x50, 0xd8, 0x79, 0x9b, 0x76, 0x52, 0xca, 0x13, 0xb4, + 0x0f, 0xe1, 0x39, 0xc9, 0x32, 0xd8, 0x00, 0x3b, 0x8b, 0xd6, 0x96, 0x29, 0xb1, 0xcd, 0x21, 0xb6, + 0x29, 0x95, 0x51, 0xd8, 0xe6, 0x21, 0xf1, 0xa8, 0x8a, 0x6d, 0x8f, 0x45, 0x1a, 0x1f, 0x01, 0x5c, + 0xca, 0xe7, 0xe7, 0x11, 0x0b, 0x39, 0x45, 0x2d, 0x38, 0x2f, 0x91, 0xf8, 0x32, 0xd8, 0xf8, 0x6f, + 0x67, 0xd1, 0x5a, 0x31, 0x0b, 0xf2, 0x9a, 0x32, 0xa8, 0x9d, 0x79, 0xa2, 0x7b, 0x39, 0xaa, 0x9a, + 0xa0, 0xda, 0x9e, 0x4a, 0x25, 0x2b, 0xe6, 0xb0, 0x6e, 0x41, 0x34, 0x46, 0x95, 0x35, 0xbd, 0x09, + 0xaf, 0xc8, 0x4a, 0x47, 0xc4, 0x75, 0x63, 0xca, 0xb9, 0x68, 0x7c, 0xa1, 0x7d, 0x59, 0xbe, 0xde, + 0x96, 0x8f, 0xc6, 0xfd, 0x9c, 0x64, 0xa3, 0x8e, 0x9a, 0xb0, 0x2e, 0xfd, 0x94, 0x5c, 0x13, 0x1a, + 0x52, 0x8e, 0xc6, 0x5b, 0x00, 0xd7, 0x73, 0xa9, 0x3c, 0x9f, 0x27, 0xb1, 0x1c, 0x70, 0x06, 0xb5, + 0x0a, 0x17, 0xd2, 0xd0, 0xef, 0xa4, 0xf4, 0xc8, 0x77, 0x15, 0xcf, 0x25, 0xf9, 0x70, 0xe0, 0xfe, + 0x31, 0xa6, 0xda, 0x5f, 0x8f, 0xe9, 0x0b, 0x80, 0x1b, 0xd5, 0x20, 0xaa, 0xc1, 0x27, 0x70, 0x49, + 0xc9, 0x13, 0x8f, 0xdb, 0xd5, 0xfc, 0x36, 0xab, 0xdb, 0x1d, 0xf3, 0x6e, 0x37, 0x58, 0xb1, 0xc2, + 0xbf, 0x9b, 0xab, 0x0b, 0xf5, 0x8a, 0x36, 0x66, 0x92, 0xb3, 0xb8, 0x00, 0xb5, 0xb2, 0x05, 0xe8, + 0x55, 0x4e, 0x6d, 0xa4, 0xd5, 0x63, 0xd8, 0x28, 0xd1, 0x4a, 0x6d, 0xc6, 0x8c, 0x52, 0xa1, 0xa2, + 0x54, 0x86, 0x06, 0x97, 0xc7, 0x4a, 0x1f, 0x92, 0x98, 0x04, 0xd9, 0xa6, 0x8c, 0xf6, 0x32, 0x7b, + 0x3d, 0xdf, 0xcb, 0x48, 0xbc, 0x4c, 0xd8, 0x4b, 0x15, 0xa2, 0x1c, 0xad, 0x4f, 0x75, 0xf8, 0xbf, + 0x48, 0x85, 0x5e, 0xc1, 0x79, 0x75, 0xb9, 0x68, 0xab, 0x24, 0xae, 0xe4, 0xd3, 0xa1, 0x6d, 0x4f, + 0xf5, 0x93, 0x60, 0x86, 0xf1, 0xe6, 0xfb, 0xaf, 0x0f, 0xb5, 0x35, 0xa4, 0xe1, 0xaa, 0x8f, 0x18, + 0x47, 0xef, 0x00, 0xac, 0xcb, 0x38, 0xb4, 0x39, 0x39, 0x6f, 0x56, 0x7e, 0x6b, 0x9a, 0x9b, 0xaa, + 0xde, 0x12, 0xd5, 0xf7, 0xd0, 0x6e, 0x75, 0x75, 0x7c, 0x92, 0xdf, 0x86, 0x53, 0xf4, 0x19, 0xc0, + 0x46, 0xc9, 0x89, 0x20, 0x6b, 0x5a, 0xd1, 0xe2, 0x61, 0x6b, 0xad, 0x0b, 0xc5, 0x28, 0x6a, 0x2c, + 0xa8, 0x6f, 0xa0, 0xed, 0x4a, 0xea, 0xfc, 0x71, 0xa2, 0x6f, 0x00, 0xa2, 0x62, 0x42, 0xd4, 0x9c, + 0xbd, 0x78, 0xc6, 0x6b, 0x5d, 0x24, 0x44, 0xe1, 0x3e, 0x10, 0xb8, 0xfb, 0xe8, 0xee, 0x8c, 0xb8, + 0xf8, 0x64, 0x74, 0x9c, 0xa7, 0x45, 0xf5, 0x5f, 0x03, 0x58, 0x97, 0x9b, 0x8a, 0x76, 0x27, 0xc3, + 0xe4, 0x0e, 0xa3, 0x7a, 0x25, 0xf2, 0x97, 0x62, 0x5c, 0x17, 0xb4, 0xab, 0x68, 0xa5, 0x84, 0x56, + 0x5e, 0xc6, 0x9d, 0x83, 0xaf, 0x7d, 0x1d, 0x9c, 0xf5, 0x75, 0xf0, 0xb3, 0xaf, 0x83, 0xf7, 0x03, + 0x7d, 0xee, 0x6c, 0xa0, 0xcf, 0xfd, 0x18, 0xe8, 0x73, 0x4f, 0xb1, 0xe7, 0x27, 0x2f, 0x52, 0xdb, + 0x74, 0x58, 0x80, 0x03, 0xea, 0xfa, 0xf6, 0x31, 0x73, 0xb2, 0x3c, 0x7b, 0x0e, 0x8b, 0x29, 0x7e, + 0x99, 0xa5, 0x4b, 0x7a, 0x11, 0xe5, 0x76, 0x5d, 0xfc, 0x00, 0xb7, 0x7e, 0x07, 0x00, 0x00, 0xff, + 0xff, 0x90, 0xa8, 0xd7, 0x6a, 0x76, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -971,6 +980,13 @@ func (m *QueryOracleRegistrationsRequest) MarshalToSizedBuffer(dAtA []byte) (int i = encodeVarintQuery(dAtA, i, uint64(size)) } i-- + dAtA[i] = 0x12 + } + if len(m.UniqueId) > 0 { + i -= len(m.UniqueId) + copy(dAtA[i:], m.UniqueId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.UniqueId))) + i-- dAtA[i] = 0xa } return len(dAtA) - i, nil @@ -1230,6 +1246,10 @@ func (m *QueryOracleRegistrationsRequest) Size() (n int) { } var l int _ = l + l = len(m.UniqueId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } if m.Pagination != nil { l = m.Pagination.Size() n += 1 + l + sovQuery(uint64(l)) @@ -1718,6 +1738,38 @@ func (m *QueryOracleRegistrationsRequest) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UniqueId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UniqueId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) } From 3c9e1defd997ff3a8b36fa253b05137e71b1184c Mon Sep 17 00:00:00 2001 From: H4NLee Date: Wed, 7 Dec 2022 14:58:12 +0900 Subject: [PATCH 5/7] feat: Add `encrypt-data` CLI --- cmd/panacead/cmd/encrypt_data.go | 106 +++++++++++++++++++++++++++++++ cmd/panacead/cmd/root.go | 1 + crypto/aes256.go | 83 ++++++++++++++++++++++++ crypto/aes256_test.go | 34 ++++++++++ 4 files changed, 224 insertions(+) create mode 100644 cmd/panacead/cmd/encrypt_data.go create mode 100644 crypto/aes256.go create mode 100644 crypto/aes256_test.go diff --git a/cmd/panacead/cmd/encrypt_data.go b/cmd/panacead/cmd/encrypt_data.go new file mode 100644 index 00000000..24ce1680 --- /dev/null +++ b/cmd/panacead/cmd/encrypt_data.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "encoding/hex" + "github.com/btcsuite/btcd/btcec" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/medibloc/panacea-core/v2/crypto" + oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types" + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/libs/cli" + "os" +) + +func EncryptDataCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "encrypt-data [input-file-path] [output-file-path] [key-name]", + Short: "Encrypt data with shared key which consists of oracle public key and provider's private key", + Long: ` + This command can encrypt data with shared key which consists of oracle public key and provider's private key. + The key to be used for encryption should be stored in the localStore. + If not stored, please add the key first via the following command. + panacead keys add ... + `, + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := oracletypes.NewQueryClient(clientCtx) + + origData, err := os.ReadFile(args[0]) + if err != nil { + return err + } + + params, err := queryClient.Params(cmd.Context(), &oracletypes.QueryOracleParamsRequest{}) + if err != nil { + return err + } + + oraclePubKey := params.GetParams().GetOraclePublicKey() + + encryptedData, err := encrypt(clientCtx, args[2], origData, oraclePubKey) + if err != nil { + return err + } + + if err := os.WriteFile(args[1], encryptedData, 0644); err != nil { + return err + } + + return nil + }, + } + + cmd.PersistentFlags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.PersistentFlags().String(flags.FlagKeyringDir, "", "The client Keyring directory; if omitted, the default 'home' directory will be used") + cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + cmd.PersistentFlags().String(cli.OutputFlag, "text", "Output format (text|json)") + cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func encrypt(clientCtx client.Context, keyName string, origData []byte, oraclePubKeyStr string) ([]byte, error) { + // get unsafe export private key from keystore + privKeyHex, err := keyring.NewUnsafe(clientCtx.Keyring).UnsafeExportPrivKeyHex(keyName) + if err != nil { + return nil, err + } + + privKeyBz, err := hex.DecodeString(privKeyHex) + if err != nil { + return nil, err + } + + privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBz) + + // oracle public key + oraclePubKeyBz, err := hex.DecodeString(oraclePubKeyStr) + if err != nil { + return nil, err + } + + oraclePubKey, err := btcec.ParsePubKey(oraclePubKeyBz, btcec.S256()) + if err != nil { + return nil, err + } + + // shared key + sharedKey := crypto.DeriveSharedKey(privKey, oraclePubKey, crypto.KDFSHA256) + + // encrypt data + encryptedData, err := crypto.Encrypt(sharedKey, nil, origData) + if err != nil { + return nil, err + } + + return encryptedData, nil +} diff --git a/cmd/panacead/cmd/root.go b/cmd/panacead/cmd/root.go index 3c1ce484..8a5fd4d0 100644 --- a/cmd/panacead/cmd/root.go +++ b/cmd/panacead/cmd/root.go @@ -113,6 +113,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { debug.Cmd(), // this line is used by starport scaffolding # stargate/root/commands AddGenesisWasmMsgCmd(app.DefaultNodeHome), + EncryptDataCmd(app.DefaultNodeHome), ) a := appCreator{encodingConfig} diff --git a/crypto/aes256.go b/crypto/aes256.go new file mode 100644 index 00000000..76d446c4 --- /dev/null +++ b/crypto/aes256.go @@ -0,0 +1,83 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "fmt" + "io" + + "github.com/btcsuite/btcd/btcec" +) + +// DeriveSharedKey derives a shared key (which can be used for asymmetric encryption) +// using a specified KDF (Key Derivation Function) +// from a shared secret generated by Diffie-Hellman key exchange (ECDH). +func DeriveSharedKey(priv *btcec.PrivateKey, pub *btcec.PublicKey, kdf func([]byte) []byte) []byte { + sharedSecret := btcec.GenerateSharedSecret(priv, pub) + return kdf(sharedSecret) +} + +// KDFSHA256 is a key derivation function which uses SHA256. +func KDFSHA256(in []byte) []byte { + out := sha256.Sum256(in) + return out[:] +} + +// Encrypt combines secretKey and secondKey to encrypt with AES256-GCM method. +func Encrypt(secretKey, additional, data []byte) ([]byte, error) { + if len(secretKey) != 32 { + return nil, fmt.Errorf("secret key is not for AES-256: total %d bits", 8*len(secretKey)) + } + + // prepare AES-256-GSM cipher + block, err := aes.NewCipher(secretKey) + if err != nil { + return nil, err + } + + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + // make random nonce + nonce := make([]byte, aesGCM.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + // encrypt data with second key + ciphertext := aesGCM.Seal(nonce, nonce, data, additional) + return ciphertext, nil +} + +// Decrypt combines secretKey and secondKey to decrypt AES256-GCM. +func Decrypt(secretKey []byte, additional []byte, ciphertext []byte) ([]byte, error) { + if len(secretKey) != 32 { + return nil, fmt.Errorf("secret key is not for AES-256: total %d bits", 8*len(secretKey)) + } + + // prepare AES-256-GCM cipher + block, err := aes.NewCipher(secretKey) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonceSize := aesgcm.NonceSize() + nonce, pureCiphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] + + // decrypt ciphertext with second key + plaintext, err := aesgcm.Open(nil, nonce, pureCiphertext, additional) + if err != nil { + return nil, err + } + + return plaintext, nil +} diff --git a/crypto/aes256_test.go b/crypto/aes256_test.go new file mode 100644 index 00000000..e664070c --- /dev/null +++ b/crypto/aes256_test.go @@ -0,0 +1,34 @@ +package crypto + +import ( + "crypto/rand" + "io" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/stretchr/testify/require" +) + +func TestDecryptWithAES256(t *testing.T) { + privKey1, err := btcec.NewPrivateKey(btcec.S256()) + require.NoError(t, err) + privKey2, err := btcec.NewPrivateKey(btcec.S256()) + require.NoError(t, err) + + data := []byte("hello, Panacea") + + shareKey1 := DeriveSharedKey(privKey1, privKey2.PubKey(), KDFSHA256) + shareKey2 := DeriveSharedKey(privKey2, privKey1.PubKey(), KDFSHA256) + + nonce := make([]byte, 12) + _, err = io.ReadFull(rand.Reader, nonce) + require.NoError(t, err) + + encryptedData, err := Encrypt(shareKey1, nonce, data) + require.NoError(t, err) + + decryptedData, err := Decrypt(shareKey2, nonce, encryptedData) + require.NoError(t, err) + + require.Equal(t, decryptedData, data) +} From 81c850c72a58e1a4e7dca0860976e83a7659a73a Mon Sep 17 00:00:00 2001 From: H4NLee Date: Thu, 8 Dec 2022 13:53:04 +0900 Subject: [PATCH 6/7] fix --- app/app.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/app.go b/app/app.go index b53908d5..6d44d1e8 100644 --- a/app/app.go +++ b/app/app.go @@ -315,7 +315,6 @@ func New( oracletypes.StoreKey, wasm.StoreKey, feegrant.StoreKey, - oracletypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -462,13 +461,6 @@ func New( wasmOpts..., ) - app.oracleKeeper = *oraclekeeper.NewKeeper( - appCodec, - keys[oracletypes.StoreKey], - keys[oracletypes.MemStoreKey], - app.GetSubspace(oracletypes.ModuleName), - ) - // The gov proposal types can be individually enabled if len(enabledWasmProposals) != 0 { govRouter.AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.wasmKeeper, enabledWasmProposals)) @@ -522,7 +514,6 @@ func New( burn.NewAppModule(appCodec, app.burnKeeper), oracle.NewAppModule(appCodec, app.oracleKeeper), wasm.NewAppModule(appCodec, &app.wasmKeeper, app.StakingKeeper), - oracle.NewAppModule(appCodec, app.oracleKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -835,7 +826,6 @@ func initParamsKeeper(appCodec codec.Codec, legacyAmino *codec.LegacyAmino, key, paramsKeeper.Subspace(burntypes.ModuleName) paramsKeeper.Subspace(oracletypes.ModuleName) paramsKeeper.Subspace(wasm.ModuleName) - paramsKeeper.Subspace(oracletypes.ModuleName) return paramsKeeper } From e7034f6d80a9bd312c1ca93aaa848243c8309d5c Mon Sep 17 00:00:00 2001 From: H4NLee Date: Thu, 8 Dec 2022 13:53:55 +0900 Subject: [PATCH 7/7] change to base64 --- cmd/panacead/cmd/encrypt_data.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/panacead/cmd/encrypt_data.go b/cmd/panacead/cmd/encrypt_data.go index 24ce1680..d61f9e2c 100644 --- a/cmd/panacead/cmd/encrypt_data.go +++ b/cmd/panacead/cmd/encrypt_data.go @@ -1,7 +1,10 @@ package cmd import ( + "encoding/base64" "encoding/hex" + "os" + "github.com/btcsuite/btcd/btcec" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -10,7 +13,6 @@ import ( oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types" "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" - "os" ) func EncryptDataCmd(defaultNodeHome string) *cobra.Command { @@ -83,7 +85,7 @@ func encrypt(clientCtx client.Context, keyName string, origData []byte, oraclePu privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBz) // oracle public key - oraclePubKeyBz, err := hex.DecodeString(oraclePubKeyStr) + oraclePubKeyBz, err := base64.StdEncoding.DecodeString(oraclePubKeyStr) if err != nil { return nil, err }