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

Ct queries #1927

Merged
merged 16 commits into from
Nov 12, 2024
14 changes: 14 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import (
"github.com/sei-protocol/sei-chain/utils"
"github.com/sei-protocol/sei-chain/utils/metrics"
"github.com/sei-protocol/sei-chain/wasmbinding"
ctmodule "github.com/sei-protocol/sei-chain/x/confidentialtransfers"
ctkeeper "github.com/sei-protocol/sei-chain/x/confidentialtransfers/keeper"
cttypes "github.com/sei-protocol/sei-chain/x/confidentialtransfers/types"
epochmodule "github.com/sei-protocol/sei-chain/x/epoch"
Expand Down Expand Up @@ -209,6 +210,7 @@ var (
wasm.AppModuleBasic{},
epochmodule.AppModuleBasic{},
tokenfactorymodule.AppModuleBasic{},
ctmodule.AppModuleBasic{},
// this line is used by starport scaffolding # stargate/app/moduleBasic
)

Expand All @@ -226,6 +228,7 @@ var (
wasm.ModuleName: {authtypes.Burner},
evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner},
tokenfactorytypes.ModuleName: {authtypes.Minter, authtypes.Burner},
cttypes.ModuleName: {},
// this line is used by starport scaffolding # stargate/app/maccPerms
}

Expand Down Expand Up @@ -555,6 +558,12 @@ func New(
tokenFactoryConfig,
)

app.ConfidentialTransfersKeeper = ctkeeper.NewKeeper(
appCodec,
app.keys[(cttypes.StoreKey)],
app.GetSubspace(cttypes.ModuleName),
app.AccountKeeper)

// The last arguments can contain custom message handlers, and custom query handlers,
// if we want to allow any custom callbacks
supportedFeatures := "iterator,staking,stargate,sei"
Expand Down Expand Up @@ -757,6 +766,7 @@ func New(
epochModule,
tokenfactorymodule.NewAppModule(app.TokenFactoryKeeper, app.AccountKeeper, app.BankKeeper),
authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
ctmodule.NewAppModule(app.ConfidentialTransfersKeeper),
// this line is used by starport scaffolding # stargate/app/appModule
)

Expand Down Expand Up @@ -788,6 +798,7 @@ func New(
evmtypes.ModuleName,
wasm.ModuleName,
tokenfactorytypes.ModuleName,
cttypes.ModuleName,
acltypes.ModuleName,
)

Expand Down Expand Up @@ -819,6 +830,7 @@ func New(
evmtypes.ModuleName,
wasm.ModuleName,
tokenfactorytypes.ModuleName,
cttypes.ModuleName,
acltypes.ModuleName,
)

Expand Down Expand Up @@ -848,6 +860,7 @@ func New(
feegrant.ModuleName,
oracletypes.ModuleName,
tokenfactorytypes.ModuleName,
cttypes.ModuleName,
epochmoduletypes.ModuleName,
wasm.ModuleName,
evmtypes.ModuleName,
Expand Down Expand Up @@ -879,6 +892,7 @@ func New(
transferModule,
epochModule,
tokenfactorymodule.NewAppModule(app.TokenFactoryKeeper, app.AccountKeeper, app.BankKeeper),
ctmodule.NewAppModule(app.ConfidentialTransfersKeeper),
// this line is used by starport scaffolding # stargate/app/appModule
)
app.sm.RegisterStoreDecoders()
Expand Down
32 changes: 22 additions & 10 deletions proto/confidentialtransfers/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,38 @@ package seiprotocol.seichain.confidentialtransfers;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "confidentialtransfers/confidential.proto";

option go_package = "github.com/sei-protocol/sei-chain/x/confidentialtransfers/types";

// TODO: Define any query messages here
// Query defines the gRPC querier service.
service Query {
// TODO: This is a mock method. Remove when we add real queries
rpc TestQuery (TestQueryRequest) returns (TestQueryResponse) {
rpc GetAccount (GetAccountRequest) returns (GetAccountResponse) {
option (google.api.http) = {
get: "/seichain/confidentialtransfers/test_query"
get: "/seichain/confidentialtransfers/account/{address}/{denom}"
};
}

rpc GetAllAccounts (GetAllAccountsRequest) returns (GetAllAccountsResponse) {
option (google.api.http) = {
get: "/seichain/confidentialtransfers/account/{address}"
};
}
}

message GetAccountRequest {
string address = 1;
string denom = 2;
}

message GetAccountResponse {
CtAccount account = 1;
}

// TODO: This is a mock method. Remove when we add real queries
message TestQueryRequest {
string test_field = 1;
message GetAllAccountsRequest {
string address = 1;
}

// TODO: This is a mock method. Remove when we add real queries
message TestQueryResponse {
string test_field = 1;
message GetAllAccountsResponse {
repeated CtAccount accounts = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

CtAccount itself does not have reference to the denom associated with the account - we should make sure this query responds with the names of the denoms that the address has accounts for

(actually that's probably the most important thing for this query, I think it's mostly useful to let users know what denoms some account has an confidential transfer accounts for, the user can then query that account directly to get the account details)

}
9 changes: 4 additions & 5 deletions x/confidentialtransfers/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package keeper
import (
"fmt"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/sei-protocol/sei-chain/x/confidentialtransfers/types"
)

func (k BaseKeeper) InitGenesis(ctx sdk.Context, gs *types.GenesisState) {
k.SetParams(ctx, gs.Params)
store := k.getAccountStore(ctx)
for i := range gs.Accounts {
genesisCtAccount := gs.Accounts[i]
store := ctx.KVStore(k.storeKey)

bz := k.cdc.MustMarshal(&genesisCtAccount.Account) // Marshal the Account object into bytes
store.Set(genesisCtAccount.Key, bz)
}
Expand All @@ -31,11 +31,10 @@ func (k BaseKeeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
}

func (k BaseKeeper) GetPaginatedAccounts(ctx sdk.Context, pagination *query.PageRequest) ([]types.GenesisCtAccount, *query.PageResponse, error) {
store := ctx.KVStore(k.storeKey)
supplyStore := prefix.NewStore(store, types.AccountsKey)
store := k.getAccountStore(ctx)

genesisAccounts := make([]types.GenesisCtAccount, 0)
pageRes, err := query.Paginate(supplyStore, pagination, func(key, value []byte) error {
pageRes, err := query.Paginate(store, pagination, func(key, value []byte) error {
var ctAccount types.CtAccount
err := ctAccount.Unmarshal(value)
if err != nil {
Expand Down
65 changes: 65 additions & 0 deletions x/confidentialtransfers/keeper/genesis_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package keeper_test

import (
"crypto/ecdsa"
"fmt"
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"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)

Expand All @@ -16,3 +21,63 @@ func (suite *KeeperTestSuite) TestDefaultGenesisState() {
suite.Require().NotNil(exportedGenesis)
suite.Require().Equal(genesisState, exportedGenesis)
}

func (suite *KeeperTestSuite) TestGenesisExportImportState() {
pk1, _ := encryption.GenerateKey()
pk2, _ := encryption.GenerateKey()
addr1 := sdk.AccAddress("addr1")
addr2 := sdk.AccAddress("addr2")
testDenom1 := fmt.Sprintf("factory/%s/TEST1", addr1.String())
testDenom2 := fmt.Sprintf("factory/%s/TEST2", addr2.String())

ctAcc1 := generateCtAccount(pk1, testDenom1, 1000)
ctAcc2 := generateCtAccount(pk2, testDenom2, 2000)

accounts := []types.GenesisCtAccount{
{
Key: []byte(addr1.String() + testDenom1),
Account: ctAcc1,
},
{
Key: []byte(addr2.String() + testDenom2),
Account: ctAcc2,
},
}
genesisState := types.NewGenesisState(types.DefaultParams(), accounts)
app := suite.App
suite.Ctx = app.BaseApp.NewContext(false, tmproto.Header{})

suite.App.ConfidentialTransfersKeeper.InitGenesis(suite.Ctx, genesisState)

exportedGenesis := suite.App.ConfidentialTransfersKeeper.ExportGenesis(suite.Ctx)
suite.Require().NotNil(exportedGenesis)
suite.Require().Equal(genesisState, exportedGenesis)
}

func generateCtAccount(pk *ecdsa.PrivateKey, testDenom string, balance uint64) types.CtAccount {
eg := elgamal.NewTwistedElgamal()
keyPair, _ := eg.KeyGen(*pk, testDenom)

aesPK1, _ := encryption.GetAESKey(*pk, testDenom)

amountLo := uint64(100)
amountHi := uint64(0)

decryptableBalance, _ := encryption.EncryptAESGCM(balance, aesPK1)

ciphertextLo, _, _ := eg.Encrypt(keyPair.PublicKey, amountLo)
ciphertextHi, _, _ := eg.Encrypt(keyPair.PublicKey, amountHi)

zeroCiphertextAvailable, _, _ := eg.Encrypt(keyPair.PublicKey, amountLo)

account := &types.Account{
PublicKey: keyPair.PublicKey,
PendingBalanceLo: ciphertextLo,
PendingBalanceHi: ciphertextHi,
PendingBalanceCreditCounter: 1,
AvailableBalance: zeroCiphertextAvailable,
DecryptableAvailableBalance: decryptableBalance,
}

return *types.NewCtAccount(account)
}
67 changes: 67 additions & 0 deletions x/confidentialtransfers/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/sei-protocol/sei-chain/x/confidentialtransfers/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

var _ types.QueryServer = BaseKeeper{}

func (k BaseKeeper) GetAccount(ctx context.Context, req *types.GetAccountRequest) (*types.GetAccountResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

Check warning on line 17 in x/confidentialtransfers/keeper/grpc_query.go

View check run for this annotation

Codecov / codecov/patch

x/confidentialtransfers/keeper/grpc_query.go#L16-L17

Added lines #L16 - L17 were not covered by tests

if req.Address == "" {
return nil, status.Error(codes.InvalidArgument, "address cannot be empty")
}

address, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid address: %s", err.Error())
}

if req.Denom == "" {
return nil, status.Error(codes.InvalidArgument, "invalid denom")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)

ctAccount, found := k.getCtAccount(sdkCtx, address, req.Denom)
if !found {
return nil, status.Errorf(codes.NotFound, "account not found for account %s and denom %s",
req.Address, req.Denom)
}

return &types.GetAccountResponse{Account: &ctAccount}, nil
}

func (k BaseKeeper) GetAllAccounts(ctx context.Context, req *types.GetAllAccountsRequest) (*types.GetAllAccountsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

Check warning on line 46 in x/confidentialtransfers/keeper/grpc_query.go

View check run for this annotation

Codecov / codecov/patch

x/confidentialtransfers/keeper/grpc_query.go#L45-L46

Added lines #L45 - L46 were not covered by tests

if req.Address == "" {
return nil, status.Error(codes.InvalidArgument, "address cannot be empty")
}

address, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid address: %s", err.Error())
}

sdkCtx := sdk.UnwrapSDKContext(ctx)

accounts, err := k.getCtAccountsForAddress(sdkCtx, address)

if err != nil {
return nil, status.Errorf(codes.Internal, "failed to fetch accounts: %s", err.Error())
}

Check warning on line 63 in x/confidentialtransfers/keeper/grpc_query.go

View check run for this annotation

Codecov / codecov/patch

x/confidentialtransfers/keeper/grpc_query.go#L62-L63

Added lines #L62 - L63 were not covered by tests

return &types.GetAllAccountsResponse{Accounts: accounts}, nil

}
Loading
Loading