Skip to content

Commit

Permalink
feat(launch): automatically fetch mainnet account balances for quer…
Browse files Browse the repository at this point in the history
…y `GenesisAccounts` with mainnet (#805)

* expected keeper

* modify queries

* import

* add tests

* tests

* update comment

* fix tests
  • Loading branch information
lumtis authored Jun 3, 2022
1 parent 53277e4 commit 832b702
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 34 deletions.
3 changes: 2 additions & 1 deletion x/launch/client/cli/query_genesis_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ func networkWithGenesisAccountObjects(t *testing.T, n int) (*network.Network, []
for i := 0; i < n; i++ {
state.GenesisAccountList = append(
state.GenesisAccountList,
sample.GenesisAccount(r, uint64(0), strconv.Itoa(i)),
sample.GenesisAccount(r, 0, strconv.Itoa(i)),
)
}
state.ChainList = append(state.ChainList, sample.Chain(r, 0, sample.Uint64(r)))
buf, err := cfg.Codec.MarshalJSON(&state)
require.NoError(t, err)
cfg.GenesisState[types.ModuleName] = buf
Expand Down
4 changes: 2 additions & 2 deletions x/launch/keeper/genesis_account_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package keeper_test

import (
"strconv"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -16,7 +15,8 @@ import (
func createNGenesisAccount(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.GenesisAccount {
items := make([]types.GenesisAccount, n)
for i := range items {
items[i] = sample.GenesisAccount(r, uint64(i), strconv.Itoa(i))
keeper.SetChain(ctx, sample.Chain(r, uint64(i), sample.Uint64(r)))
items[i] = sample.GenesisAccount(r, uint64(i), sample.Address(r))
keeper.SetGenesisAccount(ctx, items[i])
}
return items
Expand Down
95 changes: 75 additions & 20 deletions x/launch/keeper/grpc_genesis_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,62 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

campaigntypes "github.com/tendermint/spn/x/campaign/types"
"github.com/tendermint/spn/x/launch/types"
)

func (k Keeper) GenesisAccountAll(c context.Context, req *types.QueryAllGenesisAccountRequest) (*types.QueryAllGenesisAccountResponse, error) {
var (
genesisAccounts []types.GenesisAccount
pageRes *query.PageResponse
err error
)

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

var genesisAccounts []types.GenesisAccount
ctx := sdk.UnwrapSDKContext(c)

store := ctx.KVStore(k.storeKey)
genesisAccountStore := prefix.NewStore(store, types.GenesisAccountAllKey(req.LaunchID))
chain, found := k.GetChain(ctx, req.LaunchID)
if !found {
return nil, status.Error(codes.NotFound, "chain not found")
}

// if the chain is a mainnet, the account balances must be fetched from the campaign
if chain.IsMainnet {
res, err := k.campaignKeeper.MainnetAccountBalanceAll(c, &campaigntypes.QueryAllMainnetAccountBalanceRequest{
CampaignID: chain.CampaignID,
Pagination: req.Pagination,
})
if err != nil {
return nil, err
}

pageRes, err := query.Paginate(genesisAccountStore, req.Pagination, func(key []byte, value []byte) error {
var genesisAccount types.GenesisAccount
if err := k.cdc.Unmarshal(value, &genesisAccount); err != nil {
return err
for _, acc := range res.MainnetAccountBalance {
genesisAccounts = append(genesisAccounts, types.GenesisAccount{
LaunchID: req.LaunchID,
Address: acc.Address,
Coins: acc.Coins,
})
}

genesisAccounts = append(genesisAccounts, genesisAccount)
return nil
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
} else {
store := ctx.KVStore(k.storeKey)
genesisAccountStore := prefix.NewStore(store, types.GenesisAccountAllKey(req.LaunchID))

pageRes, err = query.Paginate(genesisAccountStore, req.Pagination, func(key []byte, value []byte) error {
var genesisAccount types.GenesisAccount
if err := k.cdc.Unmarshal(value, &genesisAccount); err != nil {
return err
}

genesisAccounts = append(genesisAccounts, genesisAccount)
return nil
})

if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
}

return &types.QueryAllGenesisAccountResponse{GenesisAccount: genesisAccounts, Pagination: pageRes}, nil
Expand All @@ -44,15 +75,39 @@ func (k Keeper) GenesisAccount(c context.Context, req *types.QueryGetGenesisAcco
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
ctx := sdk.UnwrapSDKContext(c)
var genesisAccount types.GenesisAccount

val, found := k.GetGenesisAccount(
ctx,
req.LaunchID,
req.Address,
)
chain, found := k.GetChain(ctx, req.LaunchID)
if !found {
return nil, status.Error(codes.NotFound, "not found")
return nil, status.Error(codes.NotFound, "chain not found")
}

// if the chain is a mainnet, the account balance must be fetched from the campaign
if chain.IsMainnet {
res, err := k.campaignKeeper.MainnetAccountBalance(c, &campaigntypes.QueryGetMainnetAccountBalanceRequest{
CampaignID: chain.CampaignID,
Address: req.Address,
})
if err != nil {
return nil, err
}

genesisAccount = types.GenesisAccount{
LaunchID: req.LaunchID,
Address: req.Address,
Coins: res.MainnetAccountBalance.Coins,
}

} else {
genesisAccount, found = k.GetGenesisAccount(
ctx,
req.LaunchID,
req.Address,
)
if !found {
return nil, status.Error(codes.NotFound, "account not found")
}
}

return &types.QueryGetGenesisAccountResponse{GenesisAccount: val}, nil
return &types.QueryGetGenesisAccountResponse{GenesisAccount: genesisAccount}, nil
}
108 changes: 97 additions & 11 deletions x/launch/keeper/grpc_genesis_account_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package keeper_test

import (
"strconv"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -10,16 +9,20 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

tc "github.com/tendermint/spn/testutil/constructor"
testkeeper "github.com/tendermint/spn/testutil/keeper"
"github.com/tendermint/spn/testutil/sample"
campaigntypes "github.com/tendermint/spn/x/campaign/types"
"github.com/tendermint/spn/x/launch/keeper"
"github.com/tendermint/spn/x/launch/types"
)

func createNGenesisAccountForChainID(keeper *keeper.Keeper, ctx sdk.Context, n int, chainID uint64) []types.GenesisAccount {
keeper.SetChain(ctx, sample.Chain(r, chainID, sample.Uint64(r)))

items := make([]types.GenesisAccount, n)
for i := range items {
items[i] = sample.GenesisAccount(r, chainID, strconv.Itoa(i))
items[i] = sample.GenesisAccount(r, chainID, sample.Address(r))
keeper.SetGenesisAccount(ctx, items[i])
}
return items
Expand All @@ -36,31 +39,39 @@ func TestGenesisAccountQuerySingle(t *testing.T) {
err error
}{
{
desc: "First",
desc: "first",
request: &types.QueryGetGenesisAccountRequest{
LaunchID: msgs[0].LaunchID,
Address: msgs[0].Address,
},
response: &types.QueryGetGenesisAccountResponse{GenesisAccount: msgs[0]},
},
{
desc: "Second",
desc: "second",
request: &types.QueryGetGenesisAccountRequest{
LaunchID: msgs[1].LaunchID,
Address: msgs[1].Address,
},
response: &types.QueryGetGenesisAccountResponse{GenesisAccount: msgs[1]},
},
{
desc: "KeyNotFound",
desc: "chain not found",
request: &types.QueryGetGenesisAccountRequest{
LaunchID: uint64(100000),
Address: strconv.Itoa(100000),
LaunchID: 100000,
Address: msgs[0].Address,
},
err: status.Error(codes.NotFound, "not found"),
err: status.Error(codes.NotFound, "chain not found"),
},
{
desc: "InvalidRequest",
desc: "account not found",
request: &types.QueryGetGenesisAccountRequest{
LaunchID: msgs[1].LaunchID,
Address: "foo",
},
err: status.Error(codes.NotFound, "account not found"),
},
{
desc: "invalid request",
err: status.Error(codes.InvalidArgument, "invalid request"),
},
} {
Expand Down Expand Up @@ -115,14 +126,89 @@ func TestGenesisAccountQueryPaginated(t *testing.T) {
next = resp.Pagination.NextKey
}
})
t.Run("Total", func(t *testing.T) {
t.Run("total", func(t *testing.T) {
resp, err := tk.LaunchKeeper.GenesisAccountAll(wctx, request(chainID, nil, 0, 0, true))
require.NoError(t, err)
require.Equal(t, len(msgs), int(resp.Pagination.Total))
require.ElementsMatch(t, msgs, resp.GenesisAccount)
})
t.Run("InvalidRequest", func(t *testing.T) {
t.Run("chain not found", func(t *testing.T) {
_, err := tk.LaunchKeeper.GenesisAccountAll(wctx, &types.QueryAllGenesisAccountRequest{
LaunchID: 10000,
})
require.ErrorIs(t, err, status.Error(codes.NotFound, "chain not found"))
})
t.Run("invalid request", func(t *testing.T) {
_, err := tk.LaunchKeeper.GenesisAccountAll(wctx, nil)
require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request"))
})
}

// TODO: These tests must be refactored and use mocking to abstract campaign logic
// https://github.com/tendermint/spn/issues/807
func TestGenesisAccountMainnet(t *testing.T) {
var (
ctx, tk, _ = testkeeper.NewTestSetup(t)
wctx = sdk.WrapSDKContext(ctx)

campaignID = uint64(5)
campaign = sample.Campaign(r, campaignID)
launchID = uint64(10)
chain = sample.Chain(r, launchID, sample.Uint64(r))
totalSupply = tc.Coins(t, "1000foo")
totalShares = uint64(100)
addr1 = sample.Address(r)
addr2 = sample.Address(r)
)

// create campaign and mainnet accounts and mainnet chain
campaign.TotalSupply = totalSupply
tk.CampaignKeeper.SetCampaign(ctx, campaign)
tk.CampaignKeeper.SetTotalShares(ctx, totalShares)
tk.CampaignKeeper.SetMainnetAccount(ctx, campaigntypes.MainnetAccount{
CampaignID: campaignID,
Address: addr1,
Shares: tc.Shares(t, "60foo"),
})
tk.CampaignKeeper.SetMainnetAccount(ctx, campaigntypes.MainnetAccount{
CampaignID: campaignID,
Address: addr2,
Shares: tc.Shares(t, "40foo"),
})
chain.IsMainnet = true
chain.CampaignID = campaignID
tk.LaunchKeeper.SetChain(ctx, chain)

t.Run("single account", func(t *testing.T) {
res, err := tk.LaunchKeeper.GenesisAccount(wctx, &types.QueryGetGenesisAccountRequest{
LaunchID: launchID,
Address: addr1,
})
require.NoError(t, err)
require.EqualValues(t, types.GenesisAccount{
LaunchID: launchID,
Address: addr1,
Coins: tc.Coins(t, "600foo"),
}, res.GenesisAccount)
})
t.Run("account list", func(t *testing.T) {
res, err := tk.LaunchKeeper.GenesisAccountAll(wctx, &types.QueryAllGenesisAccountRequest{
LaunchID: launchID,
Pagination: &query.PageRequest{
CountTotal: true,
},
})
require.NoError(t, err)
require.Len(t, res.GenesisAccount, 2)
require.Contains(t, res.GenesisAccount, types.GenesisAccount{
LaunchID: launchID,
Address: addr1,
Coins: tc.Coins(t, "600foo"),
})
require.Contains(t, res.GenesisAccount, types.GenesisAccount{
LaunchID: launchID,
Address: addr2,
Coins: tc.Coins(t, "400foo"),
})
})
}
10 changes: 10 additions & 0 deletions x/launch/types/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

Expand All @@ -13,6 +15,14 @@ type CampaignKeeper interface {
AddChainToCampaign(ctx sdk.Context, campaignID, launchID uint64) error
GetAllCampaign(ctx sdk.Context) (list []campaigntypes.Campaign)
GetCampaignChains(ctx sdk.Context, campaignID uint64) (val campaigntypes.CampaignChains, found bool)
MainnetAccountBalanceAll(
c context.Context,
req *campaigntypes.QueryAllMainnetAccountBalanceRequest,
) (*campaigntypes.QueryAllMainnetAccountBalanceResponse, error)
MainnetAccountBalance(
c context.Context,
req *campaigntypes.QueryGetMainnetAccountBalanceRequest,
) (*campaigntypes.QueryGetMainnetAccountBalanceResponse, error)
}

type MonitoringConsumerKeeper interface {
Expand Down

0 comments on commit 832b702

Please sign in to comment.