Skip to content

Commit

Permalink
feat: add ApproveOracleRegistration tx (#527)
Browse files Browse the repository at this point in the history
Co-authored-by: gyuguen <gyuguen.jang@medibloc.org>
Co-authored-by: Youngjoon Lee <yjlee@medibloc.org>
  • Loading branch information
3 people committed Dec 9, 2022
1 parent f30a46f commit eb408fc
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 11 deletions.
3 changes: 3 additions & 0 deletions types/testsuite/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type TestSuite struct {
suite.Suite

Ctx sdk.Context
Cdc params.EncodingConfig

AccountKeeper authkeeper.AccountKeeper
StakingKeeper stakingkeeper.Keeper
Expand Down Expand Up @@ -137,6 +138,7 @@ func (suite *TestSuite) SetupTest() {
scopedIBCKeeper := suite.CapabilityKeeper.ScopeToModule(ibchost.ModuleName)

suite.Ctx = ctx
suite.Cdc = cdc
suite.AccountKeeper = authkeeper.NewAccountKeeper(
cdc.Marshaler,
keyParams[authtypes.StoreKey],
Expand Down Expand Up @@ -246,6 +248,7 @@ func newTestCodec() params.EncodingConfig {
authtypes.RegisterInterfaces(interfaceRegistry)
banktypes.RegisterInterfaces(interfaceRegistry)
didtypes.RegisterInterfaces(interfaceRegistry)
oracletypes.RegisterInterfaces(interfaceRegistry)
//TODO: When other Msgs are implemented, I'll remove this comment.
//datadealtypes.RegisterInterfaces(interfaceRegistry)
marshaler := codec.NewProtoCodec(interfaceRegistry)
Expand Down
6 changes: 3 additions & 3 deletions x/oracle/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgRegisterOracle:
res, err := msgServer.RegisterOracle(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
//case *types.MsgApproveOracleRegistration:
// res, err := msgServer.ApproveOracleRegistration(sdk.WrapSDKContext(ctx), msg)
// return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgApproveOracleRegistration:
res, err := msgServer.ApproveOracleRegistration(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
//case *types.MsgUpdateOracleInfo:
// res, err := msgServer.UpdateOracleInfo(sdk.WrapSDKContext(ctx), msg)
// return sdk.WrapServiceResult(ctx, res, err)
Expand Down
27 changes: 27 additions & 0 deletions x/oracle/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package keeper

import (
"fmt"

"github.com/btcsuite/btcd/btcec"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/medibloc/panacea-core/v2/x/oracle/types"
Expand Down Expand Up @@ -35,3 +38,27 @@ func NewKeeper(
paramSpace: paramSpace,
}
}

func (k Keeper) VerifySignature(ctx sdk.Context, msg codec.ProtoMarshaler, sigBz []byte) error {
bz, err := k.cdc.Marshal(msg)
if err != nil {
return err
}

oraclePubKeyBz := k.GetParams(ctx).MustDecodeOraclePublicKey()
oraclePubKey, err := btcec.ParsePubKey(oraclePubKeyBz, btcec.S256())
if err != nil {
return err
}

signature, err := btcec.ParseSignature(sigBz, btcec.S256())
if err != nil {
return err
}

if !signature.Verify(bz, oraclePubKey) {
return fmt.Errorf("failed to signature validation")
}

return nil
}
18 changes: 15 additions & 3 deletions x/oracle/keeper/msg_server_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,21 @@ func (m msgServer) RegisterOracle(goCtx context.Context, msg *types.MsgRegisterO
return &types.MsgRegisterOracleResponse{}, nil
}

func (m msgServer) ApproveOracleRegistration(ctx context.Context, registration *types.MsgApproveOracleRegistration) (*types.MsgApproveOracleRegistrationResponse, error) {
//TODO implement me
panic("implement me")
func (m msgServer) ApproveOracleRegistration(goCtx context.Context, msg *types.MsgApproveOracleRegistration) (*types.MsgApproveOracleRegistrationResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

if err := m.Keeper.ApproveOracleRegistration(ctx, msg); err != nil {
return nil, err
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
),
)

return &types.MsgApproveOracleRegistrationResponse{}, nil
}

func (m msgServer) UpdateOracleInfo(ctx context.Context, info *types.MsgUpdateOracleInfo) (*types.MsgUpdateOracleInfoResponse, error) {
Expand Down
73 changes: 73 additions & 0 deletions x/oracle/keeper/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"errors"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
Expand Down Expand Up @@ -42,6 +43,68 @@ func (k Keeper) RegisterOracle(ctx sdk.Context, msg *types.MsgRegisterOracle) er
return nil
}

func (k Keeper) ApproveOracleRegistration(ctx sdk.Context, msg *types.MsgApproveOracleRegistration) error {

if err := k.validateApproveOracleRegistration(ctx, msg); err != nil {
return sdkerrors.Wrapf(types.ErrApproveOracleRegistration, err.Error())
}

oracleRegistration, err := k.GetOracleRegistration(ctx, msg.ApproveOracleRegistration.UniqueId, msg.ApproveOracleRegistration.TargetOracleAddress)
if err != nil {
return sdkerrors.Wrapf(types.ErrApproveOracleRegistration, err.Error())
}

newOracle := types.NewOracle(
msg.ApproveOracleRegistration.TargetOracleAddress,
msg.ApproveOracleRegistration.UniqueId,
oracleRegistration.Endpoint,
oracleRegistration.OracleCommissionRate,
)

// append new oracle info
if err := k.SetOracle(ctx, newOracle); err != nil {
return sdkerrors.Wrapf(types.ErrApproveOracleRegistration, err.Error())
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeApproveOracleRegistration,
sdk.NewAttribute(types.AttributeKeyOracleAddress, msg.ApproveOracleRegistration.TargetOracleAddress),
),
)

return nil

}

// validateApproveOracleRegistration checks signature
func (k Keeper) validateApproveOracleRegistration(ctx sdk.Context, msg *types.MsgApproveOracleRegistration) error {

params := k.GetParams(ctx)
targetOracleAddress := msg.ApproveOracleRegistration.TargetOracleAddress

// check unique id
if msg.ApproveOracleRegistration.UniqueId != params.UniqueId {
return types.ErrInvalidUniqueID
}

// verify signature
if err := k.VerifySignature(ctx, msg.ApproveOracleRegistration, msg.Signature); err != nil {
return err
}

// check if the oracle has been already registered
hasOracle, err := k.HasOracle(ctx, targetOracleAddress)
if err != nil {
return err
}
if hasOracle {
return fmt.Errorf("already registered oracle. address(%s)", targetOracleAddress)
}

return nil
}

func (k Keeper) SetOracleRegistration(ctx sdk.Context, regOracle *types.OracleRegistration) error {
store := ctx.KVStore(k.storeKey)

Expand Down Expand Up @@ -163,3 +226,13 @@ func (k Keeper) GetOracle(ctx sdk.Context, address string) (*types.Oracle, error

return oracle, nil
}

func (k Keeper) HasOracle(ctx sdk.Context, address string) (bool, error) {
store := ctx.KVStore(k.storeKey)
accAddr, err := sdk.AccAddressFromBech32(address)
if err != nil {
return false, err
}

return store.Has(types.GetOracleKey(accAddr)), nil
}
174 changes: 174 additions & 0 deletions x/oracle/keeper/oracle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,177 @@ func (suite *oracleTestSuite) TestRegisterOracleNotSameUniqueID() {
suite.Require().Error(err, types.ErrOracleRegistration)
suite.Require().ErrorContains(err, "is not match the currently active uniqueID")
}

func (suite *oracleTestSuite) TestApproveOracleRegistrationSuccess() {
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)

encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize())
suite.Require().NoError(err)

approveOracleRegistration := &types.ApproveOracleRegistration{
UniqueId: suite.uniqueID,
ApproverOracleAddress: suite.oracleAccAddr.String(),
TargetOracleAddress: suite.oracleAccAddr.String(),
EncryptedOraclePrivKey: encryptedOraclePrivKey,
}

approveOracleRegistrationBz, err := suite.Cdc.Marshaler.Marshal(approveOracleRegistration)
suite.Require().NoError(err)
signature, err := suite.oraclePrivKey.Sign(approveOracleRegistrationBz)
suite.Require().NoError(err)

msgApproveOracleRegistration := types.NewMsgApproveOracleRegistration(approveOracleRegistration, signature.Serialize())

err = suite.OracleKeeper.ApproveOracleRegistration(ctx, msgApproveOracleRegistration)
suite.Require().NoError(err)

events := suite.Ctx.EventManager().Events()
suite.Require().Equal(2, len(events))
suite.Require().Equal(types.EventTypeApproveOracleRegistration, events[1].Type)

eventVoteAttributes := events[1].Attributes
suite.Require().Equal(1, len(eventVoteAttributes))
suite.Require().Equal(types.AttributeKeyOracleAddress, string(eventVoteAttributes[0].Key))
suite.Require().Equal(suite.oracleAccAddr.String(), string(eventVoteAttributes[0].Value))

getOracle, err := suite.OracleKeeper.GetOracle(ctx, suite.oracleAccAddr.String())
suite.Require().NoError(err)
suite.Require().Equal(suite.uniqueID, getOracle.UniqueId)
suite.Require().Equal(suite.oracleAccAddr.String(), getOracle.OracleAddress)
suite.Require().Equal(suite.endpoint, getOracle.Endpoint)
suite.Require().Equal(suite.oracleCommissionRate, getOracle.OracleCommissionRate)
}

func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedInvalidUniqueID() {
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)

encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize())
suite.Require().NoError(err)

approveOracleRegistration := &types.ApproveOracleRegistration{
UniqueId: "uniqueID2",
ApproverOracleAddress: suite.oracleAccAddr.String(),
TargetOracleAddress: suite.oracleAccAddr.String(),
EncryptedOraclePrivKey: encryptedOraclePrivKey,
}
approveOracleRegistrationBz, err := suite.Cdc.Marshaler.Marshal(approveOracleRegistration)
suite.Require().NoError(err)
signature, err := suite.oraclePrivKey.Sign(approveOracleRegistrationBz)
suite.Require().NoError(err)

msgApproveOracleRegistration := types.NewMsgApproveOracleRegistration(approveOracleRegistration, signature.Serialize())

err = suite.OracleKeeper.ApproveOracleRegistration(ctx, msgApproveOracleRegistration)
suite.Require().Error(err, types.ErrInvalidUniqueID)
}

func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedInvalidSignature() {
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)

encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize())
suite.Require().NoError(err)

approveOracleRegistration := &types.ApproveOracleRegistration{
UniqueId: "uniqueID",
ApproverOracleAddress: suite.oracleAccAddr.String(),
TargetOracleAddress: suite.oracleAccAddr.String(),
EncryptedOraclePrivKey: encryptedOraclePrivKey,
}
approveOracleRegistrationBz, err := suite.Cdc.Marshaler.Marshal(approveOracleRegistration)
suite.Require().NoError(err)
newPrivateKey, err := btcec.NewPrivateKey(btcec.S256())
suite.Require().NoError(err)
signature, err := newPrivateKey.Sign(approveOracleRegistrationBz)
suite.Require().NoError(err)

msgApproveOracleRegistration := types.NewMsgApproveOracleRegistration(approveOracleRegistration, signature.Serialize())

err = suite.OracleKeeper.ApproveOracleRegistration(ctx, msgApproveOracleRegistration)
suite.Require().Error(err, "failed to signature validation")
}

func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedAlreadyExistOracle() {
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,
}

oracleRegistration := types.NewOracleRegistration(msgRegisterOracle)

err = suite.OracleKeeper.SetOracleRegistration(ctx, oracleRegistration)
suite.Require().NoError(err)

encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize())
suite.Require().NoError(err)

approveOracleRegistration := &types.ApproveOracleRegistration{
UniqueId: "uniqueID",
ApproverOracleAddress: suite.oracleAccAddr.String(),
TargetOracleAddress: suite.oracleAccAddr.String(),
EncryptedOraclePrivKey: encryptedOraclePrivKey,
}
approveOracleRegistrationBz, err := suite.Cdc.Marshaler.Marshal(approveOracleRegistration)
suite.Require().NoError(err)
signature, err := suite.oraclePrivKey.Sign(approveOracleRegistrationBz)
suite.Require().NoError(err)

msgApproveOracleRegistration := types.NewMsgApproveOracleRegistration(approveOracleRegistration, signature.Serialize())

err = suite.OracleKeeper.ApproveOracleRegistration(ctx, msgApproveOracleRegistration)
suite.Require().Error(err, types.ErrOracleRegistration)
suite.Require().ErrorContains(err, fmt.Sprintf("already registered oracle. address(%s)", msgRegisterOracle.OracleAddress))
}
1 change: 1 addition & 0 deletions x/oracle/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
&MsgApproveOracleRegistration{},
&MsgUpdateOracleInfo{},
)

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}

Expand Down
10 changes: 6 additions & 4 deletions x/oracle/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,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")
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")
ErrInvalidUniqueID = sdkerrors.Register(ModuleName, 5, "invalid unique id")
ErrApproveOracleRegistration = sdkerrors.Register(ModuleName, 6, "error while approving an oracle registration")
)
Loading

0 comments on commit eb408fc

Please sign in to comment.