diff --git a/proto/panacea/oracle/v2/tx.proto b/proto/panacea/oracle/v2/tx.proto index 2848f0f7..dd55c5dd 100644 --- a/proto/panacea/oracle/v2/tx.proto +++ b/proto/panacea/oracle/v2/tx.proto @@ -54,6 +54,7 @@ message MsgApproveOracleRegistration { // ApprovalSharingOracleKey defines approval for sharing oracle key encrypted with target oracle's node public key message ApprovalSharingOracleKey { + // approver's unique ID string unique_id = 1; string approver_oracle_address = 2; string target_oracle_address = 3; diff --git a/x/oracle/keeper/msg_server_oracle.go b/x/oracle/keeper/msg_server_oracle.go index fbbcb358..9f66af3c 100644 --- a/x/oracle/keeper/msg_server_oracle.go +++ b/x/oracle/keeper/msg_server_oracle.go @@ -67,6 +67,18 @@ func (m msgServer) UpgradeOracle(goCtx context.Context, msg *types.MsgUpgradeOra } func (m msgServer) ApproveOracleUpgrade(goCtx context.Context, msg *types.MsgApproveOracleUpgrade) (*types.MsgApproveOracleUpgradeResponse, error) { - // TODO: Implementation + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := m.Keeper.ApproveOracleUpgrade(ctx, msg); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + ) + return &types.MsgApproveOracleUpgradeResponse{}, nil } diff --git a/x/oracle/keeper/oracle.go b/x/oracle/keeper/oracle.go index 06561c89..0702637f 100644 --- a/x/oracle/keeper/oracle.go +++ b/x/oracle/keeper/oracle.go @@ -12,25 +12,21 @@ import ( func (k Keeper) RegisterOracle(ctx sdk.Context, msg *types.MsgRegisterOracle) error { oracleRegistration := types.NewOracleRegistration(msg) - if err := oracleRegistration.ValidateBasic(); err != nil { - return err - } - params := k.GetParams(ctx) - if params.UniqueId != oracleRegistration.UniqueId { - return sdkerrors.Wrapf(types.ErrOracleRegistration, "is not match the currently active uniqueID") + if params.UniqueId != msg.UniqueId { + return sdkerrors.Wrapf(types.ErrRegisterOracle, types.ErrInvalidUniqueID.Error()) } - 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()) - } + existing, err := k.GetOracleRegistration(ctx, msg.GetUniqueId(), msg.GetOracleAddress()) + if err != nil && !errors.Is(err, types.ErrOracleRegistrationNotFound) { + return sdkerrors.Wrapf(types.ErrRegisterOracle, err.Error()) + } + if existing != nil { + return sdkerrors.Wrapf(types.ErrRegisterOracle, fmt.Sprintf("already registered oracle. address(%s)", msg.OracleAddress)) } if err := k.SetOracleRegistration(ctx, oracleRegistration); err != nil { - return err + return sdkerrors.Wrapf(types.ErrRegisterOracle, err.Error()) } ctx.EventManager().EmitEvent( @@ -40,20 +36,27 @@ func (k Keeper) RegisterOracle(ctx sdk.Context, msg *types.MsgRegisterOracle) er sdk.NewAttribute(types.AttributeKeyOracleAddress, oracleRegistration.OracleAddress), ), ) + return nil } func (k Keeper) ApproveOracleRegistration(ctx sdk.Context, msg *types.MsgApproveOracleRegistration) error { - - if err := k.validateApproveOracleRegistration(ctx, msg); err != nil { + // validate approval for oracle registration + if err := k.validateApprovalSharingOracleKey(ctx, msg.GetApprovalSharingOracleKey(), msg.GetSignature()); err != nil { return sdkerrors.Wrapf(types.ErrApproveOracleRegistration, err.Error()) } + // get oracle registration oracleRegistration, err := k.GetOracleRegistration(ctx, msg.ApprovalSharingOracleKey.UniqueId, msg.ApprovalSharingOracleKey.TargetOracleAddress) if err != nil { return sdkerrors.Wrapf(types.ErrApproveOracleRegistration, err.Error()) } + // if EncryptedOraclePrivKey is already set, return error + if oracleRegistration.EncryptedOraclePrivKey != nil { + return sdkerrors.Wrapf(types.ErrApproveOracleRegistration, "already approved oracle registration. if you want to be shared oracle private key again, please register oracle again") + } + oracleRegistration.EncryptedOraclePrivKey = msg.ApprovalSharingOracleKey.EncryptedOraclePrivKey // add an encrypted oracle private key to oracleRegistration @@ -88,33 +91,31 @@ func (k Keeper) ApproveOracleRegistration(ctx sdk.Context, msg *types.MsgApprove } -// validateApproveOracleRegistration checks signature -func (k Keeper) validateApproveOracleRegistration(ctx sdk.Context, msg *types.MsgApproveOracleRegistration) error { - +// validateApprovalSharingOracleKey validate unique ID of ApprovalSharingOracleKey and its signature +func (k Keeper) validateApprovalSharingOracleKey(ctx sdk.Context, approval *types.ApprovalSharingOracleKey, signature []byte) error { params := k.GetParams(ctx) - targetOracleAddress := msg.ApprovalSharingOracleKey.TargetOracleAddress // check unique id - if msg.ApprovalSharingOracleKey.UniqueId != params.UniqueId { + if approval.UniqueId != params.UniqueId { return types.ErrInvalidUniqueID } - // verify signature - if err := k.VerifyOracleSignature(ctx, msg.ApprovalSharingOracleKey, msg.Signature); err != nil { - return err + // check if the approver oracle exists + isActive, err := k.IsActiveOracle(ctx, approval.ApproverOracleAddress) + if err != nil { + return fmt.Errorf("error occurs while checking if the oracle(%s) exists: %w", approval.ApproverOracleAddress, err) } - - if msg.ApprovalSharingOracleKey.EncryptedOraclePrivKey == nil { - return fmt.Errorf("encrypted oracle private key is nil") + if !isActive { + return fmt.Errorf("oracle(%s) is not an active oracle", approval.ApproverOracleAddress) } - // check if the oracle has been already registered - hasOracle, err := k.HasOracle(ctx, targetOracleAddress) - if err != nil { + // verify signature + if err := k.VerifyOracleSignature(ctx, approval, signature); err != nil { return err } - if hasOracle { - return fmt.Errorf("already registered oracle. address(%s)", targetOracleAddress) + + if approval.EncryptedOraclePrivKey == nil { + return fmt.Errorf("encrypted oracle private key is empty") } return nil @@ -189,12 +190,12 @@ func (k Keeper) GetOracleRegistration(ctx sdk.Context, uniqueID, address string) store := ctx.KVStore(k.storeKey) accAddr, err := sdk.AccAddressFromBech32(address) if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(types.ErrGetOracleRegistration, err.Error()) } key := types.GetOracleRegistrationKey(uniqueID, accAddr) bz := store.Get(key) if bz == nil { - return nil, sdkerrors.Wrapf(types.ErrGetOracleRegistration, "oracle registration not found") + return nil, types.ErrOracleRegistrationNotFound } oracleRegistration := &types.OracleRegistration{} @@ -249,7 +250,7 @@ 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 + return nil, sdkerrors.Wrapf(types.ErrGetOracle, err.Error()) } key := types.GetOracleKey(accAddr) bz := store.Get(key) @@ -267,12 +268,17 @@ 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) +func (k Keeper) IsActiveOracle(ctx sdk.Context, oracleAddress string) (bool, error) { + oracle, err := k.GetOracle(ctx, oracleAddress) if err != nil { return false, err } - return store.Has(types.GetOracleKey(accAddr)), nil + params := k.GetParams(ctx) + + if oracle.GetUniqueId() != params.GetUniqueId() { + return false, types.ErrInvalidUniqueID + } + + return true, nil } diff --git a/x/oracle/keeper/oracle_test.go b/x/oracle/keeper/oracle_test.go index 3dbbb6a6..820cbe36 100644 --- a/x/oracle/keeper/oracle_test.go +++ b/x/oracle/keeper/oracle_test.go @@ -10,7 +10,6 @@ import ( "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" @@ -133,10 +132,11 @@ func (suite *oracleTestSuite) TestRegisterOracleSuccess() { suite.Require().Equal(suite.oracleCommissionMaxChangeRate, oracleFromKeeper.OracleCommissionMaxChangeRate) } -func (suite *oracleTestSuite) TestRegisterOracleFailedValidateToMsgOracleRegistration() { +func (suite *oracleTestSuite) TestRegisterOracleAlreadyExistOracleRegistration() { ctx := suite.Ctx msgRegisterOracle := &types.MsgRegisterOracle{ + UniqueId: suite.uniqueID, OracleAddress: suite.oracleAccAddr.String(), NodePubKey: suite.nodePubKey.SerializeCompressed(), NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, @@ -148,76 +148,12 @@ func (suite *oracleTestSuite) TestRegisterOracleFailedValidateToMsgOracleRegistr OracleCommissionMaxChangeRate: suite.oracleCommissionMaxChangeRate, } + // first registration 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.OracleCommissionRate = sdk.NewInt(-1).ToDec() - err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) - suite.Require().Error(err, sdkerrors.ErrInvalidRequest) - suite.Require().ErrorContains(err, "oracleCommissionRate must be between 0 and OracleCommissionMaxRate") - - msgRegisterOracle.OracleCommissionRate = sdk.NewInt(2).ToDec() - err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) - suite.Require().Error(err, sdkerrors.ErrInvalidRequest) - suite.Require().ErrorContains(err, "oracleCommissionRate must be between 0 and OracleCommissionMaxRate") - - 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, suite.oracleCommissionMaxRate, suite.oracleCommissionMaxChangeRate, ctx.BlockTime()) - 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, - OracleCommissionMaxRate: suite.oracleCommissionMaxRate, - OracleCommissionMaxChangeRate: suite.oracleCommissionMaxChangeRate, - } - err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) - suite.Require().Error(err, types.ErrOracleRegistration) + suite.Require().Error(err, types.ErrRegisterOracle) suite.Require().ErrorContains(err, fmt.Sprintf("already registered oracle. address(%s)", msgRegisterOracle.OracleAddress)) } @@ -238,13 +174,17 @@ func (suite *oracleTestSuite) TestRegisterOracleNotSameUniqueID() { } err := suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) - suite.Require().Error(err, types.ErrOracleRegistration) - suite.Require().ErrorContains(err, "is not match the currently active uniqueID") + suite.Require().Error(err, types.ErrRegisterOracle) + suite.Require().ErrorContains(err, types.ErrInvalidUniqueID.Error()) } func (suite *oracleTestSuite) TestApproveOracleRegistrationSuccess() { ctx := suite.Ctx + oracle := types.NewOracle(suite.oracleAccAddr.String(), suite.uniqueID, suite.endpoint, suite.oracleCommissionRate, suite.oracleCommissionMaxRate, suite.oracleCommissionMaxChangeRate, ctx.BlockTime()) + err := suite.OracleKeeper.SetOracle(ctx, oracle) + suite.Require().NoError(err) + msgRegisterOracle := &types.MsgRegisterOracle{ UniqueId: suite.uniqueID, OracleAddress: suite.oracleAccAddr.String(), @@ -258,7 +198,7 @@ func (suite *oracleTestSuite) TestApproveOracleRegistrationSuccess() { OracleCommissionMaxChangeRate: suite.oracleCommissionMaxChangeRate, } - err := suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) + err = suite.OracleKeeper.RegisterOracle(ctx, msgRegisterOracle) suite.Require().NoError(err) encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize()) @@ -384,7 +324,7 @@ func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedInvalidSignatur suite.Require().Error(err, "failed to signature validation") } -func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedAlreadyExistOracle() { +func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedAlreadyApprovedOracleRegistration() { ctx := suite.Ctx oracle := types.NewOracle(suite.oracleAccAddr.String(), suite.uniqueID, suite.endpoint, suite.oracleCommissionRate, suite.oracleCommissionMaxRate, suite.oracleCommissionMaxChangeRate, ctx.BlockTime()) @@ -405,6 +345,7 @@ func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedAlreadyExistOra } oracleRegistration := types.NewOracleRegistration(msgRegisterOracle) + oracleRegistration.EncryptedOraclePrivKey = []byte("already registered") err = suite.OracleKeeper.SetOracleRegistration(ctx, oracleRegistration) suite.Require().NoError(err) @@ -426,8 +367,8 @@ func (suite *oracleTestSuite) TestApproveOracleRegistrationFailedAlreadyExistOra 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)) + suite.Require().Error(err, types.ErrRegisterOracle) + suite.Require().ErrorContains(err, "already approved oracle registration") } func (suite *oracleTestSuite) TestUpdateOracleInfoSuccess() { diff --git a/x/oracle/keeper/upgrade.go b/x/oracle/keeper/upgrade.go index 52d72633..a6cb3368 100644 --- a/x/oracle/keeper/upgrade.go +++ b/x/oracle/keeper/upgrade.go @@ -156,3 +156,47 @@ func (k Keeper) GetAllOracleUpgradeList(ctx sdk.Context) ([]types.OracleUpgrade, return oracleUpgrades, nil } + +func (k Keeper) ApproveOracleUpgrade(ctx sdk.Context, msg *types.MsgApproveOracleUpgrade) error { + // validate approval for oracle upgrade + if err := k.validateApprovalSharingOracleKey(ctx, msg.GetApprovalSharingOracleKey(), msg.GetSignature()); err != nil { + return sdkerrors.Wrapf(types.ErrApproveOracleUpgrade, err.Error()) + } + + // get oracle upgrade and upgrade info + upgradeInfo, err := k.GetOracleUpgradeInfo(ctx) + if err != nil { + return sdkerrors.Wrapf(types.ErrApproveOracleUpgrade, err.Error()) + } + + oracleUpgrade, err := k.GetOracleUpgrade(ctx, upgradeInfo.GetUniqueId(), msg.GetApprovalSharingOracleKey().GetTargetOracleAddress()) + if err != nil { + return sdkerrors.Wrapf(types.ErrApproveOracleUpgrade, err.Error()) + } + + // if EncryptedOraclePrivKey is already set, return error + if oracleUpgrade.EncryptedOraclePrivKey != nil { + return sdkerrors.Wrapf(types.ErrApproveOracleUpgrade, "already approved oracle upgrade. if you want to be shared oracle private key again, please upgrade oracle again") + } + + // update encrypted oracle private key + oracleUpgrade.EncryptedOraclePrivKey = msg.GetApprovalSharingOracleKey().EncryptedOraclePrivKey + + // set oracle upgrade + if err := k.SetOracleUpgrade(ctx, oracleUpgrade); err != nil { + return sdkerrors.Wrapf(types.ErrApproveOracleUpgrade, err.Error()) + } + + // emit event + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeApproveOracleUpgrade, + sdk.NewAttribute(types.AttributeKeyOracleAddress, msg.GetApprovalSharingOracleKey().GetTargetOracleAddress()), + sdk.NewAttribute(types.AttributeKeyUniqueID, upgradeInfo.GetUniqueId()), + ), + ) + + // TODO: add to queue(?) for update unique ID + + return nil +} diff --git a/x/oracle/keeper/upgrade_test.go b/x/oracle/keeper/upgrade_test.go index 3f3974f4..f1921ef5 100644 --- a/x/oracle/keeper/upgrade_test.go +++ b/x/oracle/keeper/upgrade_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "encoding/base64" "testing" "github.com/btcsuite/btcd/btcec" @@ -20,10 +21,20 @@ type oracleUpgradeTestSuite struct { oracleAccPubKey cryptotypes.PubKey oracleAccAddr sdk.AccAddress + approverAccPrivKey cryptotypes.PrivKey + approverAccPubKey cryptotypes.PubKey + approverAccAddr sdk.AccAddress + + oraclePrivKey *btcec.PrivateKey + oraclePubKey *btcec.PublicKey + nodePrivKey *btcec.PrivateKey nodePubKey *btcec.PublicKey nodePubKeyRemoteReport []byte + + currentUniqueID string + upgradeUniqueID string } func TestOracleUpgradeTestSuite(t *testing.T) { @@ -35,15 +46,31 @@ func (suite *oracleUpgradeTestSuite) BeforeTest(_, _ string) { suite.oracleAccPubKey = suite.oracleAccPrivKey.PubKey() suite.oracleAccAddr = sdk.AccAddress(suite.oracleAccPubKey.Address()) + suite.approverAccPrivKey = secp256k1.GenPrivKey() + suite.approverAccPubKey = suite.approverAccPrivKey.PubKey() + suite.approverAccAddr = sdk.AccAddress(suite.approverAccPubKey.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.currentUniqueID = "currentUniqueID" + suite.upgradeUniqueID = "upgradeUniqueID" + + suite.OracleKeeper.SetParams(suite.Ctx, types.Params{ + OraclePublicKey: base64.StdEncoding.EncodeToString(suite.oraclePubKey.SerializeCompressed()), + OraclePubKeyRemoteReport: "", + UniqueId: suite.currentUniqueID, + }) } func (suite *oracleUpgradeTestSuite) TestOracleUpgradeInfo() { upgradeInfo := &types.OracleUpgradeInfo{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, Height: 10000000, } @@ -64,12 +91,12 @@ func (suite *oracleUpgradeTestSuite) TestApplyUpgradeSuccess() { ctx := suite.Ctx params := types.DefaultParams() - params.UniqueId = "orgUniqueID" + params.UniqueId = suite.currentUniqueID suite.OracleKeeper.SetParams(ctx, params) suite.Require().Equal(params.UniqueId, suite.OracleKeeper.GetParams(ctx).UniqueId) upgradeInfo := &types.OracleUpgradeInfo{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, Height: 1, } @@ -81,7 +108,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleSuccess() { ctx := suite.Ctx upgradeInfo := &types.OracleUpgradeInfo{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, Height: 10, } @@ -89,7 +116,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleSuccess() { oracle := &types.Oracle{ OracleAddress: suite.oracleAccAddr.String(), - UniqueId: "currentUniqueID", + UniqueId: suite.currentUniqueID, Endpoint: "test.com", UpdateTime: ctx.BlockTime(), OracleCommissionRate: sdk.NewDecWithPrec(1, 1), @@ -100,7 +127,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleSuccess() { suite.Require().NoError(suite.OracleKeeper.SetOracle(ctx, oracle)) msgOracleUpgrade := &types.MsgUpgradeOracle{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, OracleAddress: suite.oracleAccAddr.String(), NodePubKey: suite.nodePubKey.SerializeCompressed(), NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, @@ -110,7 +137,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleSuccess() { suite.Require().NoError(suite.OracleKeeper.UpgradeOracle(ctx, msgOracleUpgrade)) - upgrade, err := suite.OracleKeeper.GetOracleUpgrade(ctx, "upgradeUniqueID", suite.oracleAccAddr.String()) + upgrade, err := suite.OracleKeeper.GetOracleUpgrade(ctx, suite.upgradeUniqueID, suite.oracleAccAddr.String()) suite.Require().NoError(err) suite.Require().Equal(msgOracleUpgrade.OracleAddress, upgrade.OracleAddress) suite.Require().Equal(msgOracleUpgrade.UniqueId, upgrade.UniqueId) @@ -126,7 +153,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleSuccess() { eventVoteAttributes := events[0].Attributes suite.Require().Equal(2, len(eventVoteAttributes)) suite.Require().Equal(types.AttributeKeyUniqueID, string(eventVoteAttributes[0].Key)) - suite.Require().Equal("upgradeUniqueID", string(eventVoteAttributes[0].Value)) + suite.Require().Equal(suite.upgradeUniqueID, string(eventVoteAttributes[0].Value)) suite.Require().Equal(types.AttributeKeyOracleAddress, string(eventVoteAttributes[1].Key)) suite.Require().Equal(suite.oracleAccAddr.String(), string(eventVoteAttributes[1].Value)) } @@ -135,7 +162,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleFailedNotMatchedUniqueID() ctx := suite.Ctx upgradeInfo := &types.OracleUpgradeInfo{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, Height: 10, } @@ -143,7 +170,7 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleFailedNotMatchedUniqueID() oracle := &types.Oracle{ OracleAddress: suite.oracleAccAddr.String(), - UniqueId: "currentUniqueID", + UniqueId: suite.currentUniqueID, Endpoint: "test.com", UpdateTime: ctx.BlockTime(), OracleCommissionRate: sdk.NewDecWithPrec(1, 1), @@ -171,14 +198,14 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleFailedNoRegisteredOracle() ctx := suite.Ctx upgradeInfo := &types.OracleUpgradeInfo{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, Height: 10, } suite.Require().NoError(suite.OracleKeeper.SetOracleUpgradeInfo(ctx, upgradeInfo)) msgOracleUpgrade := &types.MsgUpgradeOracle{ - UniqueId: "upgradeUniqueID", + UniqueId: suite.upgradeUniqueID, OracleAddress: suite.oracleAccAddr.String(), NodePubKey: suite.nodePubKey.SerializeCompressed(), NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, @@ -190,3 +217,186 @@ func (suite *oracleUpgradeTestSuite) TestUpgradeOracleFailedNoRegisteredOracle() suite.Require().Error(err, types.ErrUpgradeOracle) suite.Require().ErrorContains(err, "is not registered oracle") } + +func (suite *oracleUpgradeTestSuite) TestApproveOracleUpgradeSuccess() { + // requester : suite.oracleAcc + // approver : suite.approverAcc + + ctx := suite.Ctx + + // set oracle upgrade info + upgradeInfo := &types.OracleUpgradeInfo{ + UniqueId: suite.upgradeUniqueID, + Height: 10, + } + + suite.Require().NoError(suite.OracleKeeper.SetOracleUpgradeInfo(ctx, upgradeInfo)) + + // set approver oracle + approverOracle := &types.Oracle{ + OracleAddress: suite.approverAccAddr.String(), + UniqueId: suite.currentUniqueID, + Endpoint: "iam-approver.com", + UpdateTime: ctx.BlockTime(), + OracleCommissionRate: sdk.NewDecWithPrec(1, 1), + OracleCommissionMaxRate: sdk.NewDecWithPrec(2, 1), + OracleCommissionMaxChangeRate: sdk.NewDecWithPrec(1, 2), + } + + suite.Require().NoError(suite.OracleKeeper.SetOracle(ctx, approverOracle)) + + oracle := &types.Oracle{ + OracleAddress: suite.oracleAccAddr.String(), + UniqueId: suite.currentUniqueID, + Endpoint: "test.com", + UpdateTime: ctx.BlockTime(), + OracleCommissionRate: sdk.NewDecWithPrec(1, 1), + OracleCommissionMaxRate: sdk.NewDecWithPrec(2, 1), + OracleCommissionMaxChangeRate: sdk.NewDecWithPrec(1, 2), + } + + suite.Require().NoError(suite.OracleKeeper.SetOracle(ctx, oracle)) + + msgOracleUpgrade := &types.MsgUpgradeOracle{ + UniqueId: suite.upgradeUniqueID, + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, + TrustedBlockHeight: int64(1), + TrustedBlockHash: []byte("trustedBlockHash"), + } + + suite.Require().NoError(suite.OracleKeeper.UpgradeOracle(ctx, msgOracleUpgrade)) + + // approve oracle upgrade + encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize()) + suite.Require().NoError(err) + + approveOracleUpgrade := &types.ApprovalSharingOracleKey{ + UniqueId: suite.currentUniqueID, + ApproverOracleAddress: suite.approverAccAddr.String(), + TargetOracleAddress: suite.oracleAccAddr.String(), + EncryptedOraclePrivKey: encryptedOraclePrivKey, + } + + approveOracleRegistrationBz, err := suite.Cdc.Marshaler.Marshal(approveOracleUpgrade) + suite.Require().NoError(err) + signature, err := suite.oraclePrivKey.Sign(approveOracleRegistrationBz) + suite.Require().NoError(err) + + msgApproveOracleUpgrade := types.NewMsgApproveOracleUpgrade(approveOracleUpgrade, signature.Serialize()) + + err = suite.OracleKeeper.ApproveOracleUpgrade(ctx, msgApproveOracleUpgrade) + suite.Require().NoError(err) + + // check events + events := ctx.EventManager().Events() + + requiredEvents := map[string]bool{ + types.EventTypeApproveOracleUpgrade: false, + } + + for _, e := range events { + if e.Type == types.EventTypeApproveOracleUpgrade { + requiredEvents[e.Type] = true + suite.Require().Equal(types.AttributeKeyOracleAddress, string(e.Attributes[0].Key)) + suite.Require().Equal(suite.oracleAccAddr.String(), string(e.Attributes[0].Value)) + suite.Require().Equal(types.AttributeKeyUniqueID, string(e.Attributes[1].Key)) + suite.Require().Equal(suite.upgradeUniqueID, string(e.Attributes[1].Value)) + } + } + + for _, v := range requiredEvents { + suite.Require().True(v) + } +} + +func (suite *oracleUpgradeTestSuite) TestApproveOracleUpgradeFailAlreadyApproved() { + // requester : suite.oracleAcc + // approver : suite.approverAcc + + ctx := suite.Ctx + + // set oracle upgrade info + upgradeInfo := &types.OracleUpgradeInfo{ + UniqueId: suite.upgradeUniqueID, + Height: 10, + } + + suite.Require().NoError(suite.OracleKeeper.SetOracleUpgradeInfo(ctx, upgradeInfo)) + + // set approver oracle + approverOracle := &types.Oracle{ + OracleAddress: suite.approverAccAddr.String(), + UniqueId: suite.currentUniqueID, + Endpoint: "iam-approver.com", + UpdateTime: ctx.BlockTime(), + OracleCommissionRate: sdk.NewDecWithPrec(1, 1), + OracleCommissionMaxRate: sdk.NewDecWithPrec(2, 1), + OracleCommissionMaxChangeRate: sdk.NewDecWithPrec(1, 2), + } + + suite.Require().NoError(suite.OracleKeeper.SetOracle(ctx, approverOracle)) + + oracle := &types.Oracle{ + OracleAddress: suite.oracleAccAddr.String(), + UniqueId: suite.currentUniqueID, + Endpoint: "test.com", + UpdateTime: ctx.BlockTime(), + OracleCommissionRate: sdk.NewDecWithPrec(1, 1), + OracleCommissionMaxRate: sdk.NewDecWithPrec(2, 1), + OracleCommissionMaxChangeRate: sdk.NewDecWithPrec(1, 2), + } + + suite.Require().NoError(suite.OracleKeeper.SetOracle(ctx, oracle)) + + msgOracleUpgrade := &types.MsgUpgradeOracle{ + UniqueId: suite.upgradeUniqueID, + OracleAddress: suite.oracleAccAddr.String(), + NodePubKey: suite.nodePubKey.SerializeCompressed(), + NodePubKeyRemoteReport: suite.nodePubKeyRemoteReport, + TrustedBlockHeight: int64(1), + TrustedBlockHash: []byte("trustedBlockHash"), + } + + suite.Require().NoError(suite.OracleKeeper.UpgradeOracle(ctx, msgOracleUpgrade)) + + // approve oracle upgrade + encryptedOraclePrivKey, err := btcec.Encrypt(suite.nodePubKey, suite.oraclePrivKey.Serialize()) + suite.Require().NoError(err) + + approveOracleUpgrade := &types.ApprovalSharingOracleKey{ + UniqueId: suite.currentUniqueID, + ApproverOracleAddress: suite.approverAccAddr.String(), + TargetOracleAddress: suite.oracleAccAddr.String(), + EncryptedOraclePrivKey: encryptedOraclePrivKey, + } + + approveOracleRegistrationBz, err := suite.Cdc.Marshaler.Marshal(approveOracleUpgrade) + suite.Require().NoError(err) + signature, err := suite.oraclePrivKey.Sign(approveOracleRegistrationBz) + suite.Require().NoError(err) + + msgApproveOracleUpgrade := types.NewMsgApproveOracleUpgrade(approveOracleUpgrade, signature.Serialize()) + + err = suite.OracleKeeper.ApproveOracleUpgrade(ctx, msgApproveOracleUpgrade) + suite.Require().NoError(err) + + // do second approve + secondApprove := &types.ApprovalSharingOracleKey{ + UniqueId: suite.currentUniqueID, + ApproverOracleAddress: suite.oracleAccAddr.String(), + TargetOracleAddress: suite.oracleAccAddr.String(), + EncryptedOraclePrivKey: encryptedOraclePrivKey, + } + + secondOracleUpgradeBz, err := suite.Cdc.Marshaler.Marshal(secondApprove) + suite.Require().NoError(err) + secondSig, err := suite.oraclePrivKey.Sign(secondOracleUpgradeBz) + suite.Require().NoError(err) + + secondMsgApproveOracleUpgrade := types.NewMsgApproveOracleUpgrade(secondApprove, secondSig.Serialize()) + err = suite.OracleKeeper.ApproveOracleUpgrade(ctx, secondMsgApproveOracleUpgrade) + suite.Require().Error(types.ErrApproveOracleUpgrade) + suite.Require().ErrorContains(err, "already approved oracle upgrade") +} diff --git a/x/oracle/types/errors.go b/x/oracle/types/errors.go index 6c262bc1..c0a1edfe 100644 --- a/x/oracle/types/errors.go +++ b/x/oracle/types/errors.go @@ -3,20 +3,22 @@ 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") - ErrInvalidUniqueID = sdkerrors.Register(ModuleName, 5, "invalid unique id") - ErrApproveOracleRegistration = sdkerrors.Register(ModuleName, 6, "error while approving an oracle registration") - ErrUpdateOracle = sdkerrors.Register(ModuleName, 7, "error while updating oracle info") - ErrCommissionNegative = sdkerrors.Register(ModuleName, 8, "commission must be positive") - ErrCommissionGTMaxRate = sdkerrors.Register(ModuleName, 9, "commission cannot be more than the max rate") - ErrCommissionUpdateTime = sdkerrors.Register(ModuleName, 10, "commission cannot be changed more than once in 24h") - ErrCommissionGTMaxChangeRate = sdkerrors.Register(ModuleName, 11, "commission cannot be changed more than max change rate") - ErrOracleUpgradeInfoNotFound = sdkerrors.Register(ModuleName, 12, "oracle upgrade information not found") - ErrGetOracleUpgradeInfo = sdkerrors.Register(ModuleName, 13, "error while get oracleUpgradeInfo") - ErrUpgradeOracle = sdkerrors.Register(ModuleName, 14, "error while upgrading a oracle") - ErrGetOracleUpgrade = sdkerrors.Register(ModuleName, 15, "error while get oracleUpgrade") - ErrOracleUpgradeNotFound = sdkerrors.Register(ModuleName, 16, "oracle upgrade not found") + ErrRegisterOracle = sdkerrors.Register(ModuleName, 1, "error while registering a oracle") + ErrGetOracleRegistration = sdkerrors.Register(ModuleName, 2, "error while getting a oracle registration") + ErrOracleRegistrationNotFound = sdkerrors.Register(ModuleName, 3, "oracle registration not found") + ErrGetOracle = sdkerrors.Register(ModuleName, 4, "error while getting a oracle") + ErrOracleNotFound = sdkerrors.Register(ModuleName, 5, "oracle not found") + ErrInvalidUniqueID = sdkerrors.Register(ModuleName, 6, "invalid unique id") + ErrApproveOracleRegistration = sdkerrors.Register(ModuleName, 7, "error while approving an oracle registration") + ErrUpdateOracle = sdkerrors.Register(ModuleName, 8, "error while updating oracle info") + ErrCommissionNegative = sdkerrors.Register(ModuleName, 9, "commission must be positive") + ErrCommissionGTMaxRate = sdkerrors.Register(ModuleName, 10, "commission cannot be more than the max rate") + ErrCommissionUpdateTime = sdkerrors.Register(ModuleName, 11, "commission cannot be changed more than once in 24h") + ErrCommissionGTMaxChangeRate = sdkerrors.Register(ModuleName, 12, "commission cannot be changed more than max change rate") + ErrOracleUpgradeInfoNotFound = sdkerrors.Register(ModuleName, 13, "oracle upgrade information not found") + ErrGetOracleUpgradeInfo = sdkerrors.Register(ModuleName, 14, "error while get oracleUpgradeInfo") + ErrUpgradeOracle = sdkerrors.Register(ModuleName, 15, "error while upgrading a oracle") + ErrGetOracleUpgrade = sdkerrors.Register(ModuleName, 16, "error while get oracleUpgrade") + ErrApproveOracleUpgrade = sdkerrors.Register(ModuleName, 17, "error while approving oracle upgrade") + ErrOracleUpgradeNotFound = sdkerrors.Register(ModuleName, 18, "oracle upgrade not found") ) diff --git a/x/oracle/types/message_oracle.go b/x/oracle/types/message_oracle.go index de1e5092..9cd1864b 100644 --- a/x/oracle/types/message_oracle.go +++ b/x/oracle/types/message_oracle.go @@ -58,12 +58,14 @@ func (m *MsgRegisterOracle) ValidateBasic() error { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "trusted block hash should not be nil") } - if m.OracleCommissionRate.LT(sdk.ZeroDec()) || m.OracleCommissionRate.GT(sdk.OneDec()) { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "oracleCommissionRate must be between 0 and 1") - } if m.OracleCommissionMaxRate.LT(sdk.ZeroDec()) || m.OracleCommissionMaxRate.GT(sdk.OneDec()) { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "OracleCommissionMaxRate must be between 0 and 1") } + + if m.OracleCommissionRate.LT(sdk.ZeroDec()) || m.OracleCommissionRate.GT(m.OracleCommissionMaxRate) { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "oracleCommissionRate must be between 0 and OracleCommissionMaxRate") + } + if m.OracleCommissionMaxChangeRate.LT(sdk.ZeroDec()) || m.OracleCommissionMaxChangeRate.GT(m.OracleCommissionMaxRate) { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "OracleCommissionMaxChangeRate must be between 0 and OracleCommissionMaxRate") } @@ -249,6 +251,13 @@ func (m *MsgUpgradeOracle) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{oracleAddress} } +func NewMsgApproveOracleUpgrade(approve *ApprovalSharingOracleKey, signature []byte) *MsgApproveOracleUpgrade { + return &MsgApproveOracleUpgrade{ + ApprovalSharingOracleKey: approve, + Signature: signature, + } +} + func (m *MsgApproveOracleUpgrade) Route() string { return RouterKey } @@ -258,7 +267,14 @@ func (m *MsgApproveOracleUpgrade) Type() string { } func (m *MsgApproveOracleUpgrade) ValidateBasic() error { - // TODO: Implementation + if err := m.GetApprovalSharingOracleKey().ValidateBasic(); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, err.Error()) + } + + if m.Signature == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "signature is empty") + } + return nil } diff --git a/x/oracle/types/tx.pb.go b/x/oracle/types/tx.pb.go index f7344471..03d38288 100644 --- a/x/oracle/types/tx.pb.go +++ b/x/oracle/types/tx.pb.go @@ -220,6 +220,7 @@ func (m *MsgApproveOracleRegistration) GetSignature() []byte { // ApprovalSharingOracleKey defines approval for sharing oracle key encrypted with target oracle's node public key type ApprovalSharingOracleKey struct { + // approver's unique ID UniqueId string `protobuf:"bytes,1,opt,name=unique_id,json=uniqueId,proto3" json:"unique_id,omitempty"` ApproverOracleAddress string `protobuf:"bytes,2,opt,name=approver_oracle_address,json=approverOracleAddress,proto3" json:"approver_oracle_address,omitempty"` TargetOracleAddress string `protobuf:"bytes,3,opt,name=target_oracle_address,json=targetOracleAddress,proto3" json:"target_oracle_address,omitempty"`