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
}
141 changes: 141 additions & 0 deletions x/confidentialtransfers/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package cli

import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"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 [address] [denom]",
Short: "Query the account state",
Long: "Queries the account state associated with the address and denom",
Args: cobra.ExactArgs(2),
mj850 marked this conversation as resolved.
Show resolved Hide resolved
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)

address := args[0]
denom := args[1]

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 56 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, "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)

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

if err != nil {
return err
}

for i := range res.Accounts {
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
45 changes: 41 additions & 4 deletions x/confidentialtransfers/types/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package types

import (
"math/big"
"strconv"

"github.com/coinbase/kryptology/pkg/core/curves"
"github.com/sei-protocol/sei-cryptography/pkg/encryption"
"github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal"
)

Expand Down Expand Up @@ -34,14 +36,14 @@ type Account struct {
DecryptableAvailableBalance string
}

func (a *Account) GetPendingBalancePlaintext(decryptor *elgamal.TwistedElGamal, keypair *elgamal.KeyPair) (*big.Int, error) {
func (a *Account) GetPendingBalancePlaintext(decryptor *elgamal.TwistedElGamal, keypair *elgamal.KeyPair) (*big.Int, uint64, uint64, error) {
actualPendingBalanceLo, err := decryptor.Decrypt(keypair.PrivateKey, a.PendingBalanceLo, elgamal.MaxBits32)
if err != nil {
return big.NewInt(0), err
return big.NewInt(0), 0, 0, err
}
actualPendingBalanceHi, err := decryptor.DecryptLargeNumber(keypair.PrivateKey, a.PendingBalanceHi, elgamal.MaxBits48)
if err != nil {
return big.NewInt(0), err
return big.NewInt(0), 0, 0, err
}

loBig := new(big.Int).SetUint64(actualPendingBalanceLo)
Expand All @@ -52,5 +54,40 @@ func (a *Account) GetPendingBalancePlaintext(decryptor *elgamal.TwistedElGamal,

// Combine by adding hiBig with loBig
combined := new(big.Int).Add(hiBig, loBig)
return combined, nil
return combined, actualPendingBalanceLo, actualPendingBalanceHi, nil
}

// Returns the decrypted account state.
// Does not decyrpt the available balance unless specified. Decrypting the is only feasible for small numbers under 40 bits.
// TODO: Add tests for this method
func (a *Account) Decrypt(decryptor *elgamal.TwistedElGamal, keypair *elgamal.KeyPair, aesKey []byte, decryptAvailableBalance bool) (*DecryptedCtAccount, error) {
pendingBalanceCombined, pendingBalanceLo, pendingBalanceHi, err := a.GetPendingBalancePlaintext(decryptor, keypair)
if err != nil {
return nil, err
}

aesAvailableBalance, err := encryption.DecryptAESGCM(a.DecryptableAvailableBalance, aesKey)
if err != nil {
return nil, err
}

availableBalanceString := "Not Decrypted"
if decryptAvailableBalance {
availableBalance, err := decryptor.DecryptLargeNumber(keypair.PrivateKey, a.AvailableBalance, elgamal.MaxBits40)
if err != nil {
return nil, err
}

availableBalanceString = strconv.FormatUint(availableBalance, 10)
}

return &DecryptedCtAccount{
PublicKey: a.PublicKey.ToAffineCompressed(),
PendingBalanceLo: uint32(pendingBalanceLo),
PendingBalanceHi: pendingBalanceHi,
CombinedPendingBalance: pendingBalanceCombined.Uint64(),
PendingBalanceCreditCounter: uint32(a.PendingBalanceCreditCounter),
AvailableBalance: availableBalanceString,
DecryptableAvailableBalance: aesAvailableBalance,
}, nil
}
Loading
Loading