Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add confidential transfer queries to seid #1931

Merged
merged 14 commits into from
Nov 15, 2024
3 changes: 2 additions & 1 deletion app/apptesting/test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package apptesting
import (
"context"
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/crypto"
"time"

"github.com/ethereum/go-ethereum/crypto"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
Expand Down
13 changes: 13 additions & 0 deletions proto/confidentialtransfers/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,17 @@ message GetAllCtAccountsResponse {

// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message DecryptedCtAccount {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

bytes public_key = 1; // serialized public key
uint32 pending_balance_lo = 2; // lo bits of the pending balance
uint64 pending_balance_hi = 3; // hi bits of the pending balance
uint64 combined_pending_balance = 4; // combined pending balance
uint32 pending_balance_credit_counter = 5;
string available_balance = 6; // decrypted available balance
uint64 decryptable_available_balance = 7; // decrypted aes encrypted available balance
}
159 changes: 159 additions & 0 deletions x/confidentialtransfers/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package cli

import (
"fmt"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/sei-protocol/sei-chain/x/confidentialtransfers/types"
"github.com/sei-protocol/sei-cryptography/pkg/encryption"
"github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal"
"github.com/spf13/cobra"
)

// GetQueryCmd returns the cli query commands for the minting module.
func GetQueryCmd() *cobra.Command {
confidentialTransfersQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the confidential transfer module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

confidentialTransfersQueryCmd.AddCommand(
GetCmdQueryAccount(),
GetCmdQueryAllAccount(),
)

return confidentialTransfersQueryCmd
}

// GetCmdQueryAccount implements a command to return an account asssociated with the address and denom
func GetCmdQueryAccount() *cobra.Command {
cmd := &cobra.Command{
Use: "account [denom] [address] [flags]",
Short: "Query the account state",
Long: "Queries the account state associated with the address and denom." +
"Pass the --from flag to decrypt the account." +
"Pass the --decryptAvailableBalance flag to attempt to decrypt the available balance.",
Copy link
Contributor

@dssei dssei Nov 14, 2024

Choose a reason for hiding this comment

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

usually multi word flags I think are concatenated with - e.g.
--decrypt-available-balance

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed to --decrypt-available-balance

Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think we were moving those functions to separate methods in tx. Might make our code consistent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to separate functions for both

clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

denom := args[0]
mj850 marked this conversation as resolved.
Show resolved Hide resolved
address := args[1]
// Validate address
_, err = sdk.AccAddressFromBech32(address)
if err != nil {
return fmt.Errorf("invalid address: %v", err)
}

from, err := cmd.Flags().GetString(flags.FlagFrom)
dssei marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
fromAddr, _, _, err := client.GetFromFields(clientCtx, clientCtx.Keyring, from)
if err != nil {
return err
}

res, err := queryClient.GetCtAccount(cmd.Context(), &types.GetCtAccountRequest{

Check failure on line 66 in x/confidentialtransfers/client/cli/query.go

View workflow job for this annotation

GitHub Actions / lint

ineffectual assignment to err (ineffassign)
Fixed Show fixed Hide fixed
Address: address,
mj850 marked this conversation as resolved.
Show resolved Hide resolved
Denom: denom,
})

if fromAddr.String() == args[0] {
account, err := res.Account.FromProto()
if err != nil {
return err
}
privateKey, err := getPrivateKey(cmd)
if err != nil {
return err
}

aesKey, err := encryption.GetAESKey(*privateKey, denom)
if err != nil {
return err
}

decryptor := elgamal.NewTwistedElgamal()
keyPair, err := decryptor.KeyGen(*privateKey, denom)
if err != nil {
return err
}

decryptAvailableBalance, err := cmd.Flags().GetBool("decryptAvailableBalance")
mj850 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

decryptedAccount, err := account.Decrypt(decryptor, keyPair, aesKey, decryptAvailableBalance)
if err != nil {
return err
}

return clientCtx.PrintProto(decryptedAccount)

} else {
return clientCtx.PrintProto(res.Account)
}
},
}

flags.AddQueryFlagsToCmd(cmd)
cmd.Flags().String(flags.FlagFrom, "", "Name or address of private key to decrypt the account")
cmd.Flags().Bool("decryptAvailableBalance", false, "Set this to attempt to decrypt the available balance")
return cmd
}

// GetCmdQueryAccount implements a command to return an account asssociated with the address and denom
func GetCmdQueryAllAccount() *cobra.Command {
cmd := &cobra.Command{
Use: "accounts [address]",
Short: "Query all the confidential token accounts associated with the address",
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)

address := args[0]
// Validate address
_, err = sdk.AccAddressFromBech32(address)
if err != nil {
return fmt.Errorf("invalid address: %v", err)
}

res, err := queryClient.GetAllCtAccounts(cmd.Context(), &types.GetAllCtAccountsRequest{
Address: args[0],
})

if err != nil {
return err
}

for i := range res.Accounts {
clientCtx.PrintString("\n")

Check failure on line 145 in x/confidentialtransfers/client/cli/query.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `clientCtx.PrintString` is not checked (errcheck)
err = clientCtx.PrintProto(&res.Accounts[i])
if err != nil {
return err
}
}

return nil
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
12 changes: 3 additions & 9 deletions x/confidentialtransfers/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ type Keeper interface {
GetParams(ctx sdk.Context) types.Params
SetParams(ctx sdk.Context, params types.Params)

// TODO: See if there's a way to put this somewhere else
SendTokens(ctx sdk.Context, to sdk.AccAddress, amount sdk.Coins) error
ReceiveTokens(ctx sdk.Context, from sdk.AccAddress, amount sdk.Coins) error
BankKeeper() types.BankKeeper

CreateModuleAccount(ctx sdk.Context)

Expand Down Expand Up @@ -177,12 +175,8 @@ func (k BaseKeeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramSpace.SetParamSet(ctx, &params)
}

func (k BaseKeeper) SendTokens(ctx sdk.Context, to sdk.AccAddress, amount sdk.Coins) error {
return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, to, amount)
}

func (k BaseKeeper) ReceiveTokens(ctx sdk.Context, from sdk.AccAddress, amount sdk.Coins) error {
return k.bankKeeper.SendCoinsFromAccountToModule(ctx, from, types.ModuleName, amount)
func (k BaseKeeper) BankKeeper() types.BankKeeper {
return k.bankKeeper
}

func (k BaseKeeper) getAccountStore(ctx sdk.Context) prefix.Store {
Expand Down
5 changes: 2 additions & 3 deletions x/confidentialtransfers/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (m msgServer) Deposit(goCtx context.Context, req *types.MsgDeposit) (*types
coins := sdk.NewCoins(sdk.NewCoin(req.Denom, sdk.NewIntFromUint64(req.Amount)))

// Transfer the amount from the sender's account to the module account
if err := m.Keeper.ReceiveTokens(ctx, address, coins); err != nil {
if err := m.Keeper.BankKeeper().SendCoinsFromAccountToModule(ctx, address, types.ModuleName, coins); err != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "insufficient funds to deposit %d %s", req.Amount, req.Denom)
}

Expand Down Expand Up @@ -228,7 +228,7 @@ func (m msgServer) Withdraw(goCtx context.Context, req *types.MsgWithdraw) (*typ

// Return the tokens to the sender
coins := sdk.NewCoins(sdk.NewCoin(instruction.Denom, sdk.NewIntFromUint64(instruction.Amount)))
if err := m.Keeper.SendTokens(ctx, address, coins); err != nil {
if err := m.Keeper.BankKeeper().SendCoinsFromModuleToAccount(ctx, types.ModuleName, address, coins); err != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "insufficient funds to withdraw %d %s", req.Amount, req.Denom)
}

Expand Down Expand Up @@ -408,7 +408,6 @@ func (m msgServer) Transfer(goCtx context.Context, req *types.MsgTransfer) (*typ
}

// Calculate and Update the account states.
// TODO: Is there a possibility for a race condition here if multiple Transfer transactions are made to the same account at the same time?
recipientPendingBalanceLo, err := elgamal.AddCiphertext(recipientAccount.PendingBalanceLo, instruction.RecipientTransferAmountLo)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "error adding recipient transfer amount lo")
Expand Down
15 changes: 8 additions & 7 deletions x/confidentialtransfers/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package keeper_test

import (
"math"
"math/big"

"github.com/coinbase/kryptology/pkg/core/curves"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/crypto"
Expand All @@ -10,8 +13,6 @@ import (
"github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal"
"github.com/sei-protocol/sei-cryptography/pkg/zkproofs"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"math"
"math/big"
)

// Tests the InitializeAccount method of the MsgServer.
Expand Down Expand Up @@ -279,9 +280,9 @@ func (suite *KeeperTestSuite) TestMsgServer_DepositBasic() {

// Check that pending balances were increased by the deposit amount
keyPair, _ := teg.KeyGen(*testPk, DefaultTestDenom)
oldPendingBalancePlaintext, _ := initialState.GetPendingBalancePlaintext(teg, keyPair)
oldPendingBalancePlaintext, _, _, _ := initialState.GetPendingBalancePlaintext(teg, keyPair)

newPendingBalancePlaintext, _ := account.GetPendingBalancePlaintext(teg, keyPair)
newPendingBalancePlaintext, _, _, _ := account.GetPendingBalancePlaintext(teg, keyPair)

// Check that newPendingBalancePlaintext = oldPendingBalancePlaintext + DepositAmount
suite.Require().Equal(
Expand Down Expand Up @@ -312,7 +313,7 @@ func (suite *KeeperTestSuite) TestMsgServer_DepositBasic() {
updatedAccount, _ := suite.App.ConfidentialTransfersKeeper.GetAccount(suite.Ctx, testAddr.String(), DefaultTestDenom)

oldPendingBalancePlaintext = newPendingBalancePlaintext
newPendingBalancePlaintext, _ = updatedAccount.GetPendingBalancePlaintext(teg, keyPair)
newPendingBalancePlaintext, _, _, _ = updatedAccount.GetPendingBalancePlaintext(teg, keyPair)
suite.Require().Equal(
new(big.Int).Add(oldPendingBalancePlaintext, new(big.Int).SetUint64(depositStruct.Amount)),
newPendingBalancePlaintext,
Expand Down Expand Up @@ -939,8 +940,8 @@ func (suite *KeeperTestSuite) TestMsgServer_TransferHappyPath() {

// Check that new pending balances of the recipient account have been updated to reflect the change
suite.Require().Equal(initialRecipientState.PendingBalanceCreditCounter+1, recipientAccountState.PendingBalanceCreditCounter)
oldRecipientPendingBalance, _ := initialRecipientState.GetPendingBalancePlaintext(teg, recipientKeypair)
newRecipientPendingBalance, _ := recipientAccountState.GetPendingBalancePlaintext(teg, recipientKeypair)
oldRecipientPendingBalance, _, _, _ := initialRecipientState.GetPendingBalancePlaintext(teg, recipientKeypair)
newRecipientPendingBalance, _, _, _ := recipientAccountState.GetPendingBalancePlaintext(teg, recipientKeypair)

transferAmountBigInt := new(big.Int).SetUint64(transferAmount)
newTotal := new(big.Int).Add(oldRecipientPendingBalance, transferAmountBigInt)
Expand Down
9 changes: 1 addition & 8 deletions x/confidentialtransfers/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,9 @@ func (am AppModuleBasic) ValidateGenesisStream(cdc codec.JSONCodec, config clien
return nil
}

// TODO: Look into whether we require REST endpoints
// RegisterRESTRoutes registers the capability module's REST service handlers.
func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {}

// TODO: Look into whether we require gRPC Gateway support
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module.
func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) //nolint:errcheck
Expand All @@ -101,26 +99,22 @@ func (am AppModuleBasic) GetTxCmd() *cobra.Command {
return cli.NewTxCmd()
}

// TODO: Implement this when we add the CLI methods
// GetQueryCmd returns the x/confidentialtransfers module's root query command.
func (AppModuleBasic) GetQueryCmd() *cobra.Command {
//return cli.GetQueryCmd()
return nil
return cli.GetQueryCmd()
}

// ----------------------------------------------------------------------------
// AppModule
// ----------------------------------------------------------------------------

// TODO: Add any required keepers here
// AppModule implements the AppModule interface for the capability module.
type AppModule struct {
AppModuleBasic

keeper keeper.Keeper
}

// TODO: Revisit if any other keepers are required.
func NewAppModule(
keeper keeper.Keeper,
) AppModule {
Expand Down Expand Up @@ -178,7 +172,6 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.Ra
// ExportGenesis returns the x/confidentialtransfers module's exported genesis state as raw
// JSON bytes.
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
// TODO: Implement once we implement keeper/Genesis
genState := am.keeper.ExportGenesis(ctx)
return cdc.MustMarshalJSON(genState)
}
Expand Down
Loading
Loading