Skip to content

Commit

Permalink
feat: receive pubkey for signature verification (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnletey authored Sep 27, 2024
1 parent 258836b commit 20f9c31
Show file tree
Hide file tree
Showing 13 changed files with 778 additions and 459 deletions.
748 changes: 467 additions & 281 deletions api/v1/tx.pulsar.go

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import (
"context"
"fmt"

"cosmossdk.io/core/address"
"cosmossdk.io/core/store"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/monerium/module-noble/v2/types"
"github.com/monerium/module-noble/v2/types/blacklist"
Expand All @@ -28,22 +30,25 @@ type Keeper struct {
authority string
storeService store.KVStoreService

accountKeeper types.AccountKeeper
bankKeeper types.BankKeeper
cdc codec.Codec
addressCodec address.Codec
bankKeeper types.BankKeeper
}

func NewKeeper(
authority string,
storeService store.KVStoreService,
accountKeeper types.AccountKeeper,
cdc codec.Codec,
addressCodec address.Codec,
bankKeeper types.BankKeeper,
) *Keeper {
return &Keeper{
authority: authority,
storeService: storeService,

accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
cdc: cdc,
addressCodec: addressCodec,
bankKeeper: bankKeeper,
}
}

Expand Down
51 changes: 35 additions & 16 deletions keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
package keeper

import (
"bytes"
"context"
"fmt"

"adr36.dev"
"cosmossdk.io/errors"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/monerium/module-noble/v2/types"
)
Expand Down Expand Up @@ -128,23 +130,28 @@ func (k msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.MsgBu
return nil, types.ErrInvalidSystem
}

address := sdk.MustAccAddressFromBech32(msg.From)
account := k.accountKeeper.GetAccount(ctx, address)
if account == nil || account.GetPubKey() == nil {
return nil, types.ErrNoPubKey
var pubKey cryptotypes.PubKey
if err := k.cdc.UnpackAny(msg.PubKey, &pubKey); err != nil {
return nil, errors.Wrap(err, "unable to unpack pubkey")
}
from, err := k.addressCodec.StringToBytes(msg.From)
if err != nil {
return nil, errors.Wrapf(err, "unable to decode user address %s", msg.From)
}
if !bytes.Equal(from, pubKey.Address()) {
return nil, types.ErrInvalidPubKey
}

if !adr36.VerifySignature(
account.GetPubKey(),
pubKey,
[]byte("I hereby declare that I am the address owner."),
msg.Signature,
) {
return nil, types.ErrInvalidSignature
}

coins := sdk.NewCoins(sdk.NewCoin(msg.Denom, msg.Amount))
from := sdk.MustAccAddressFromBech32(msg.From)
err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, from, types.ModuleName, coins)
err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, from, types.ModuleName, coins)
if err != nil {
return nil, errors.Wrap(err, "unable to transfer from user to module")
}
Expand Down Expand Up @@ -175,8 +182,11 @@ func (k msgServer) Mint(goCtx context.Context, msg *types.MsgMint) (*types.MsgMi
k.Keeper.SetMintAllowance(ctx, msg.Denom, msg.Signer, allowance)

coins := sdk.NewCoins(sdk.NewCoin(msg.Denom, msg.Amount))
to := sdk.MustAccAddressFromBech32(msg.To)
err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
to, err := k.addressCodec.StringToBytes(msg.To)
if err != nil {
return nil, errors.Wrapf(err, "unable to decode user address %s", msg.To)
}
err = k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
if err != nil {
return nil, errors.Wrap(err, "unable to mint to module")
}
Expand All @@ -202,14 +212,20 @@ func (k msgServer) Recover(goCtx context.Context, msg *types.MsgRecover) (*types
return nil, types.ErrInvalidSystem
}

from := sdk.MustAccAddressFromBech32(msg.From)
account := k.accountKeeper.GetAccount(ctx, from)
if account == nil || account.GetPubKey() == nil {
return nil, types.ErrNoPubKey
var pubKey cryptotypes.PubKey
if err := k.cdc.UnpackAny(msg.PubKey, &pubKey); err != nil {
return nil, errors.Wrap(err, "unable to unpack pubkey")
}
from, err := k.addressCodec.StringToBytes(msg.From)
if err != nil {
return nil, errors.Wrapf(err, "unable to decode user address %s", msg.From)
}
if !bytes.Equal(from, pubKey.Address()) {
return nil, types.ErrInvalidPubKey
}

if !adr36.VerifySignature(
account.GetPubKey(),
pubKey,
[]byte("I hereby declare that I am the address owner."),
msg.Signature,
) {
Expand All @@ -221,8 +237,11 @@ func (k msgServer) Recover(goCtx context.Context, msg *types.MsgRecover) (*types
return &types.MsgRecoverResponse{}, nil
}

to := sdk.MustAccAddressFromBech32(msg.To)
err := k.bankKeeper.SendCoins(ctx, from, to, sdk.NewCoins(balance))
to, err := k.addressCodec.StringToBytes(msg.To)
if err != nil {
return nil, errors.Wrapf(err, "unable to decode user address %s", msg.To)
}
err = k.bankKeeper.SendCoins(ctx, from, to, sdk.NewCoins(balance))
if err != nil {
return nil, errors.Wrap(err, "unable to transfer from user to user")
}
Expand Down
113 changes: 69 additions & 44 deletions keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/monerium/module-noble/v2/keeper"
"github.com/monerium/module-noble/v2/types"
"github.com/monerium/module-noble/v2/utils"
Expand Down Expand Up @@ -229,14 +228,11 @@ func TestBurn(t *testing.T) {
bz, _ := base64.StdEncoding.DecodeString("AlE8CxHR19ID5lxrVtTxSgJFlK3T+eYtyDM/vBA3Fowr")
pubkey, _ := codectypes.NewAnyWithValue(&secp256k1.PubKey{Key: bz})

account := mocks.AccountKeeper{
Accounts: make(map[string]sdk.AccountI),
}
bank := mocks.BankKeeper{
Balances: make(map[string]sdk.Coins),
Restriction: mocks.NoOpSendRestrictionFn,
}
k, ctx := mocks.FlorinWithKeepers(account, bank)
k, ctx := mocks.FlorinWithKeepers(bank)
goCtx := sdk.WrapSDKContext(ctx)
server := keeper.NewMsgServer(k)

Expand All @@ -257,34 +253,36 @@ func TestBurn(t *testing.T) {
// ASSERT: The action should've failed due to invalid signer.
require.ErrorIs(t, err, types.ErrInvalidSystem)

// ACT: Attempt to burn with no account in state.
// ACT: Attempt to burn with invalid any.
invalidPubKey, _ := codectypes.NewAnyWithValue(&types.MsgBurn{})
_, err = server.Burn(goCtx, &types.MsgBurn{
Denom: "ueure",
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
PubKey: invalidPubKey,
})
// ASSERT: The action should've failed due to no account.
require.ErrorIs(t, err, types.ErrNoPubKey)
// ASSERT: The action should've failed due to invalid any.
require.ErrorContains(t, err, "unable to unpack pubkey")

// ARRANGE: Set account in state, without pubkey.
account.Accounts["noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2"] = &authtypes.BaseAccount{}
// ACT: Attempt to burn with invalid user address.
_, err = server.Burn(goCtx, &types.MsgBurn{
Denom: "ueure",
Signer: system.Address,
From: utils.TestAccount().Invalid,
PubKey: pubkey,
})
// ASSERT: The action should've failed due to invalid user address.
require.ErrorContains(t, err, "unable to decode user address")

// ACT: Attempt to burn with no pubkey in state.
// ACT: Attempt to burn with invalid public key.
invalidPubKey, _ = codectypes.NewAnyWithValue(secp256k1.GenPrivKey().PubKey())
_, err = server.Burn(goCtx, &types.MsgBurn{
Denom: "ueure",
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
PubKey: invalidPubKey,
})
// ASSERT: The action should've failed due to no pubkey.
require.ErrorIs(t, err, types.ErrNoPubKey)

// ARRANGE: Set pubkey in state.
account.Accounts["noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2"] = &authtypes.BaseAccount{
Address: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
PubKey: pubkey,
AccountNumber: 0,
Sequence: 0,
}
// ASSERT: The action should've failed due to invalid public key.
require.ErrorIs(t, err, types.ErrInvalidPubKey)

// ACT: Attempt to burn with invalid signature.
signature, _ := base64.StdEncoding.DecodeString("QBrRfIqjdBvXx9zaBcuiE9P5SVesxFO/He3deyx2OE0NoSNqwmSb7b5iP2UhZRI1duiOeho3+NETUkCBv14zjQ==")
Expand All @@ -293,6 +291,7 @@ func TestBurn(t *testing.T) {
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've failed due to invalid signature.
require.ErrorIs(t, err, types.ErrInvalidSignature)
Expand All @@ -305,6 +304,7 @@ func TestBurn(t *testing.T) {
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
Amount: One,
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've failed due to insufficient balance.
require.ErrorContains(t, err, "unable to transfer from user to module")
Expand All @@ -319,6 +319,7 @@ func TestBurn(t *testing.T) {
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
Amount: One,
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've succeeded.
require.NoError(t, err)
Expand All @@ -330,7 +331,7 @@ func TestMint(t *testing.T) {
Balances: make(map[string]sdk.Coins),
Restriction: mocks.NoOpSendRestrictionFn,
}
k, ctx := mocks.FlorinWithKeepers(mocks.AccountKeeper{}, bank)
k, ctx := mocks.FlorinWithKeepers(bank)
goCtx := sdk.WrapSDKContext(ctx)
server := keeper.NewMsgServer(k)

Expand Down Expand Up @@ -375,6 +376,16 @@ func TestMint(t *testing.T) {
// ARRANGE: Generate a user account.
user := utils.TestAccount()

// ACT: Attempt to mint with invalid user address.
_, err = server.Mint(goCtx, &types.MsgMint{
Denom: "ueure",
Signer: system.Address,
To: user.Invalid,
Amount: math.ZeroInt(),
})
// ASSERT: The action should've failed due to invalid user address.
require.ErrorContains(t, err, "unable to decode user address")

// ACT: Attempt to mint.
_, err = server.Mint(goCtx, &types.MsgMint{
Denom: "ueure",
Expand Down Expand Up @@ -406,14 +417,11 @@ func TestRecover(t *testing.T) {
bz, _ := base64.StdEncoding.DecodeString("AlE8CxHR19ID5lxrVtTxSgJFlK3T+eYtyDM/vBA3Fowr")
pubkey, _ := codectypes.NewAnyWithValue(&secp256k1.PubKey{Key: bz})

account := mocks.AccountKeeper{
Accounts: make(map[string]sdk.AccountI),
}
bank := mocks.BankKeeper{
Balances: make(map[string]sdk.Coins),
Restriction: mocks.NoOpSendRestrictionFn,
}
k, ctx := mocks.FlorinWithKeepers(account, bank)
k, ctx := mocks.FlorinWithKeepers(bank)
goCtx := sdk.WrapSDKContext(ctx)
server := keeper.NewMsgServer(k)

Expand All @@ -434,34 +442,36 @@ func TestRecover(t *testing.T) {
// ASSERT: The action should've failed due to invalid signer.
require.ErrorIs(t, err, types.ErrInvalidSystem)

// ACT: Attempt to recover with no account in state.
// ACT: Attempt to recover with invalid any.
invalidPubKey, _ := codectypes.NewAnyWithValue(&types.MsgRecover{})
_, err = server.Recover(goCtx, &types.MsgRecover{
Denom: "ueure",
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
PubKey: invalidPubKey,
})
// ASSERT: The action should've failed due to no account.
require.ErrorIs(t, err, types.ErrNoPubKey)
// ASSERT: The action should've failed due to invalid any.
require.ErrorContains(t, err, "unable to unpack pubkey")

// ARRANGE: Set account in state, without pubkey.
account.Accounts["noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2"] = &authtypes.BaseAccount{}
// ACT: Attempt to recover with invalid user address.
_, err = server.Recover(goCtx, &types.MsgRecover{
Denom: "ueure",
Signer: system.Address,
From: utils.TestAccount().Invalid,
PubKey: pubkey,
})
// ASSERT: The action should've failed due to invalid user address.
require.ErrorContains(t, err, "unable to decode user address")

// ACT: Attempt to recover with no pubkey in state.
// ACT: Attempt to recover with invalid public key.
invalidPubKey, _ = codectypes.NewAnyWithValue(secp256k1.GenPrivKey().PubKey())
_, err = server.Recover(goCtx, &types.MsgRecover{
Denom: "ueure",
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
PubKey: invalidPubKey,
})
// ASSERT: The action should've failed due to no pubkey.
require.ErrorIs(t, err, types.ErrNoPubKey)

// ARRANGE: Set pubkey in state.
account.Accounts["noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2"] = &authtypes.BaseAccount{
Address: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
PubKey: pubkey,
AccountNumber: 0,
Sequence: 0,
}
// ASSERT: The action should've failed due to invalid public key.
require.ErrorIs(t, err, types.ErrInvalidPubKey)

// ACT: Attempt to recover with invalid signature.
signature, _ := base64.StdEncoding.DecodeString("QBrRfIqjdBvXx9zaBcuiE9P5SVesxFO/He3deyx2OE0NoSNqwmSb7b5iP2UhZRI1duiOeho3+NETUkCBv14zjQ==")
Expand All @@ -470,6 +480,7 @@ func TestRecover(t *testing.T) {
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've failed due to invalid signature.
require.ErrorIs(t, err, types.ErrInvalidSignature)
Expand All @@ -485,20 +496,34 @@ func TestRecover(t *testing.T) {
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
To: recipient.Address,
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've succeeded.
require.NoError(t, err)

// ARRANGE: Give user 1 $EURe.
bank.Balances["noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2"] = sdk.NewCoins(sdk.NewCoin("ueure", One))

// ACT: Attempt to recover with invalid user address.
_, err = server.Recover(goCtx, &types.MsgRecover{
Denom: "ueure",
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
To: recipient.Invalid,
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've failed due to invalid user address.
require.ErrorContains(t, err, "unable to decode user address")

// ACT: Attempt to recover.
_, err = server.Recover(goCtx, &types.MsgRecover{
Denom: "ueure",
Signer: system.Address,
From: "noble1rwvjzk28l38js7xx6mq23nrpghd8qqvxmj6ep2",
To: recipient.Address,
Signature: signature,
PubKey: pubkey,
})
// ASSERT: The action should've succeeded.
require.NoError(t, err)
Expand Down
Loading

0 comments on commit 20f9c31

Please sign in to comment.