cosmos-sdk/x/auth/keeper/deterministic_test.go

474 lines
15 KiB
Go

package keeper_test
import (
"encoding/hex"
"sort"
"testing"
"github.com/stretchr/testify/suite"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"pgregory.net/rapid"
"github.com/cosmos/cosmos-sdk/baseapp"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/keeper"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
type DeterministicTestSuite struct {
suite.Suite
key *storetypes.KVStoreKey
ctx sdk.Context
queryClient types.QueryClient
accountKeeper keeper.AccountKeeper
encCfg moduletestutil.TestEncodingConfig
maccPerms map[string][]string
}
var (
iterCount = 1000
addr = sdk.MustAccAddressFromBech32("cosmos1j364pjm8jkxxmujj0vp2xjg0y7w8tyveuamfm6")
pub, _ = hex.DecodeString("01090C02812F010C25200ED40E004105160196E801F70005070EA21603FF06001E")
)
func TestDeterministicTestSuite(t *testing.T) {
suite.Run(t, new(DeterministicTestSuite))
}
func (suite *DeterministicTestSuite) SetupTest() {
suite.encCfg = moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{})
key := sdk.NewKVStoreKey(types.StoreKey)
testCtx := testutil.DefaultContextWithDB(suite.T(), key, sdk.NewTransientStoreKey("transient_test"))
suite.ctx = testCtx.Ctx.WithBlockHeader(tmproto.Header{})
maccPerms := map[string][]string{
"fee_collector": nil,
"mint": {"minter"},
"bonded_tokens_pool": {"burner", "staking"},
"not_bonded_tokens_pool": {"burner", "staking"},
multiPerm: {"burner", "minter", "staking"},
randomPerm: {"random"},
}
suite.accountKeeper = keeper.NewAccountKeeper(
suite.encCfg.Codec,
key,
types.ProtoBaseAccount,
maccPerms,
"cosmos",
types.NewModuleAddress("gov").String(),
)
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.encCfg.InterfaceRegistry)
types.RegisterQueryServer(queryHelper, suite.accountKeeper)
suite.queryClient = types.NewQueryClient(queryHelper)
suite.key = key
suite.maccPerms = maccPerms
}
// createAndSetAccount creates a random account and sets to the keeper store.
func (suite *DeterministicTestSuite) createAndSetAccounts(t *rapid.T, count int) []types.AccountI {
accs := make([]types.AccountI, 0, count)
// We need all generated account-numbers unique
accNums := rapid.SliceOfNDistinct(rapid.Uint64(), count, count, func(i uint64) uint64 { return i }).Draw(t, "acc-numss")
for i := 0; i < count; i++ {
pub := pubkeyGenerator(t).Draw(t, "pubkey")
addr := sdk.AccAddress(pub.Address())
accNum := accNums[i]
seq := rapid.Uint64().Draw(t, "sequence")
acc1 := types.NewBaseAccount(addr, &pub, accNum, seq)
suite.accountKeeper.SetAccount(suite.ctx, acc1)
accs = append(accs, acc1)
}
return accs
}
func (suite *DeterministicTestSuite) runAccountIterations(addr sdk.AccAddress, prevRes *codectypes.Any) {
for i := 0; i < iterCount; i++ {
acc, err := suite.queryClient.Account(suite.ctx, &types.QueryAccountRequest{Address: addr.String()})
suite.Require().NoError(err)
suite.Require().NotNil(acc)
suite.Require().Equal(acc.Account, prevRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryAccount() {
rapid.Check(suite.T(), func(t *rapid.T) {
accs := suite.createAndSetAccounts(t, 1)
suite.Require().Len(accs, 1)
any, err := codectypes.NewAnyWithValue(accs[0])
suite.Require().NoError(err)
suite.runAccountIterations(accs[0].GetAddress(), any)
})
// Regression tests
accNum := uint64(10087)
seq := uint64(98)
acc1 := types.NewBaseAccount(addr, &secp256k1.PubKey{Key: pub}, accNum, seq)
suite.accountKeeper.SetAccount(suite.ctx, acc1)
any, err := codectypes.NewAnyWithValue(acc1)
suite.Require().NoError(err)
suite.runAccountIterations(addr, any)
}
// pubkeyGenerator creates and returns a random pubkey generator using rapid.
func pubkeyGenerator(t *rapid.T) *rapid.Generator[secp256k1.PubKey] {
return rapid.Custom(func(t *rapid.T) secp256k1.PubKey {
pkBz := rapid.SliceOfN(rapid.Byte(), 33, 33).Draw(t, "hex")
return secp256k1.PubKey{Key: pkBz}
})
}
func (suite *DeterministicTestSuite) runAccountsIterations(req *types.QueryAccountsRequest, prevRes *types.QueryAccountsResponse) {
for i := 0; i < iterCount; i++ {
res, err := suite.queryClient.Accounts(suite.ctx, req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Len(res.GetAccounts(), len(prevRes.GetAccounts()))
suite.Require().Equal(res, prevRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryAccounts() {
rapid.Check(suite.T(), func(t *rapid.T) {
numAccs := rapid.IntRange(1, 10).Draw(t, "accounts")
accs := suite.createAndSetAccounts(t, numAccs)
req := types.QueryAccountsRequest{
Pagination: testdata.PaginationGenerator(t, uint64(numAccs)).Draw(t, "accounts"),
}
res, err := suite.queryClient.Accounts(suite.ctx, &req)
suite.Require().NoError(err)
suite.runAccountsIterations(&req, res)
for i := 0; i < numAccs; i++ {
suite.accountKeeper.RemoveAccount(suite.ctx, accs[i])
}
})
// Regression test
addr1, err := sdk.AccAddressFromBech32("cosmos1892yr6fzlj7ud0kfkah2ctrav3a4p4n060ze8f")
suite.Require().NoError(err)
pub1, err := hex.DecodeString("D1002E1B019000010BB7034500E71F011F1CA90D5B000E134BFB0F3603030D0303")
suite.Require().NoError(err)
accNum1 := uint64(107)
seq1 := uint64(10001)
accNum2 := uint64(100)
seq2 := uint64(10)
acc1 := types.NewBaseAccount(addr1, &secp256k1.PubKey{Key: pub1}, accNum1, seq1)
acc2 := types.NewBaseAccount(addr, &secp256k1.PubKey{Key: pub}, accNum2, seq2)
suite.accountKeeper.SetAccount(suite.ctx, acc1)
suite.accountKeeper.SetAccount(suite.ctx, acc2)
req := types.QueryAccountsRequest{}
res, err := suite.queryClient.Accounts(suite.ctx, &req)
suite.Require().NoError(err)
suite.runAccountsIterations(&req, res)
}
func (suite *DeterministicTestSuite) runAccountAddressByIDIterations(id int64, prevRes string) {
for i := 0; i < iterCount; i++ {
res, err := suite.queryClient.AccountAddressByID(suite.ctx, &types.QueryAccountAddressByIDRequest{Id: id})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(res.AccountAddress, prevRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryAccountAddressByID() {
rapid.Check(suite.T(), func(t *rapid.T) {
pub := pubkeyGenerator(t).Draw(t, "pubkey")
addr := sdk.AccAddress(pub.Address())
accNum := rapid.Int64Min(0).Draw(t, "account-number")
seq := rapid.Uint64().Draw(t, "sequence")
acc1 := types.NewBaseAccount(addr, &pub, uint64(accNum), seq)
suite.accountKeeper.SetAccount(suite.ctx, acc1)
suite.runAccountAddressByIDIterations(accNum, addr.String())
})
// Regression test
accNum := int64(10087)
seq := uint64(0)
acc1 := types.NewBaseAccount(addr, &secp256k1.PubKey{Key: pub}, uint64(accNum), seq)
suite.accountKeeper.SetAccount(suite.ctx, acc1)
suite.runAccountAddressByIDIterations(accNum, addr.String())
}
func (suite *DeterministicTestSuite) runParamsIterations(prevRes types.Params) {
for i := 0; i < iterCount; i++ {
res, err := suite.queryClient.Params(suite.ctx, &types.QueryParamsRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(res.Params, prevRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryParameters() {
rapid.Check(suite.T(), func(t *rapid.T) {
params := types.NewParams(
rapid.Uint64Min(1).Draw(t, "max-memo-characters"),
rapid.Uint64Min(1).Draw(t, "tx-sig-limit"),
rapid.Uint64Min(1).Draw(t, "tx-size-cost-per-byte"),
rapid.Uint64Min(1).Draw(t, "sig-verify-cost-ed25519"),
rapid.Uint64Min(1).Draw(t, "sig-verify-cost-Secp256k1"),
)
err := suite.accountKeeper.SetParams(suite.ctx, params)
suite.Require().NoError(err)
suite.runParamsIterations(params)
})
// Regression test
params := types.NewParams(15, 167, 100, 1, 21457)
err := suite.accountKeeper.SetParams(suite.ctx, params)
suite.Require().NoError(err)
suite.runParamsIterations(params)
}
func (suite *DeterministicTestSuite) runAccountInfoIterations(addr sdk.AccAddress, prevRes *types.BaseAccount) {
for i := 0; i < iterCount; i++ {
res, err := suite.queryClient.AccountInfo(suite.ctx, &types.QueryAccountInfoRequest{Address: addr.String()})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().NotNil(res.Info)
suite.Require().Equal(res.GetInfo().Address, prevRes.Address)
suite.Require().True(res.GetInfo().PubKey.Equal(prevRes.PubKey))
suite.Require().Equal(res.GetInfo().AccountNumber, prevRes.AccountNumber)
suite.Require().Equal(res.GetInfo().Sequence, prevRes.Sequence)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryAccountInfo() {
rapid.Check(suite.T(), func(t *rapid.T) {
accs := suite.createAndSetAccounts(t, 1)
suite.Require().Len(accs, 1)
acc, ok := accs[0].(*types.BaseAccount)
suite.Require().True(ok)
suite.runAccountInfoIterations(acc.GetAddress(), acc)
})
// Regression test
accNum := uint64(10087)
seq := uint64(10)
acc1 := types.NewBaseAccount(addr, &secp256k1.PubKey{Key: pub}, accNum, seq)
suite.accountKeeper.SetAccount(suite.ctx, acc1)
suite.runAccountInfoIterations(addr, acc1)
}
func (suite *DeterministicTestSuite) createAndReturnQueryClient(ak keeper.AccountKeeper) types.QueryClient {
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.encCfg.InterfaceRegistry)
types.RegisterQueryServer(queryHelper, ak)
return types.NewQueryClient(queryHelper)
}
func (suite *DeterministicTestSuite) runBech32PrefixIterations(ak keeper.AccountKeeper, preRes string) {
queryClient := suite.createAndReturnQueryClient(ak)
for i := 0; i < iterCount; i++ {
res, err := queryClient.Bech32Prefix(suite.ctx, &types.Bech32PrefixRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(res.Bech32Prefix, preRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryBech32Prefix() {
rapid.Check(suite.T(), func(t *rapid.T) {
prefix := rapid.StringMatching(`[a-zA-Z]+[1-9a-zA-Z]*`).Draw(t, "prefix")
ak := keeper.NewAccountKeeper(
suite.encCfg.Codec,
suite.key,
types.ProtoBaseAccount,
nil,
prefix,
types.NewModuleAddress("gov").String(),
)
suite.runBech32PrefixIterations(ak, prefix)
})
suite.runBech32PrefixIterations(suite.accountKeeper, "cosmos")
}
func (suite *DeterministicTestSuite) runAddressBytesToStringIterations(addressBytes []byte, prevRes string) {
for i := 0; i < iterCount; i++ {
res, err := suite.queryClient.AddressBytesToString(suite.ctx, &types.AddressBytesToStringRequest{
AddressBytes: addressBytes,
})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(res.AddressString, prevRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryAddressBytesToString() {
rapid.Check(suite.T(), func(t *rapid.T) {
address := testdata.AddressGenerator(t).Draw(t, "address-bytes")
suite.runAddressBytesToStringIterations(address.Bytes(), address.String())
})
suite.runAddressBytesToStringIterations(addr.Bytes(), addr.String())
}
func (suite *DeterministicTestSuite) runStringToAddressBytesIterations(addressString string, prevRes []byte) {
for i := 0; i < iterCount; i++ {
res, err := suite.queryClient.AddressStringToBytes(suite.ctx, &types.AddressStringToBytesRequest{
AddressString: addressString,
})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(res.AddressBytes, prevRes)
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryAddressStringToBytes() {
rapid.Check(suite.T(), func(t *rapid.T) {
address := testdata.AddressGenerator(t).Draw(t, "address-string")
suite.runStringToAddressBytesIterations(address.String(), address.Bytes())
})
suite.runStringToAddressBytesIterations(addr.String(), addr.Bytes())
}
func (suite *DeterministicTestSuite) setModuleAccounts(
ctx sdk.Context, ak keeper.AccountKeeper, maccs []string) []types.AccountI {
sort.Strings(maccs)
moduleAccounts := make([]types.AccountI, 0, len(maccs))
for _, m := range maccs {
acc, _ := ak.GetModuleAccountAndPermissions(ctx, m)
acc1, ok := acc.(types.AccountI)
suite.Require().True(ok)
moduleAccounts = append(moduleAccounts, acc1)
}
return moduleAccounts
}
func (suite *DeterministicTestSuite) runModuleAccountsIterations(ak keeper.AccountKeeper, prevRes []types.AccountI) {
queryClient := suite.createAndReturnQueryClient(ak)
for i := 0; i < iterCount; i++ {
res, err := queryClient.ModuleAccounts(suite.ctx, &types.QueryModuleAccountsRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().NotNil(res.Accounts)
suite.Require().Len(res.Accounts, len(prevRes))
unpackedAccs := make([]types.AccountI, len(res.Accounts))
for i := 0; i < len(res.Accounts); i++ {
var account types.AccountI
err = suite.encCfg.InterfaceRegistry.UnpackAny(res.Accounts[i], &account)
suite.Require().NoError(err)
unpackedAccs[i] = account
}
if prevRes != nil {
for i := 0; i < len(prevRes); i++ {
suite.Require().Equal(unpackedAccs[i].GetAddress(), prevRes[i].GetAddress())
suite.Require().Equal(unpackedAccs[i].GetAccountNumber(), prevRes[i].GetAccountNumber())
suite.Require().Equal(unpackedAccs[i].GetSequence(), prevRes[i].GetSequence())
}
}
}
}
func (suite *DeterministicTestSuite) TestGRPCQueryModuleAccounts() {
permissions := []string{"burner", "minter", "staking", "random"}
rapid.Check(suite.T(), func(t *rapid.T) {
maccsCount := rapid.IntRange(1, 10).Draw(t, "accounts")
maccs := make([]string, maccsCount)
for i := 0; i < maccsCount; i++ {
maccs[i] = rapid.StringMatching(`[a-z]{5,}`).Draw(t, "module-name")
}
maccPerms := make(map[string][]string)
for i := 0; i < maccsCount; i++ {
mPerms := make([]string, 0, 4)
for _, permission := range permissions {
if rapid.Bool().Draw(t, "permissions") {
mPerms = append(mPerms, permission)
}
}
if len(mPerms) == 0 {
num := rapid.IntRange(0, 3).Draw(t, "num")
mPerms = append(mPerms, permissions[num])
}
maccPerms[maccs[i]] = mPerms
}
ak := keeper.NewAccountKeeper(
suite.encCfg.Codec,
suite.key,
types.ProtoBaseAccount,
maccPerms,
"cosmos",
types.NewModuleAddress("gov").String(),
)
storedMaccs := suite.setModuleAccounts(suite.ctx, ak, maccs)
suite.runModuleAccountsIterations(ak, storedMaccs)
for i := 0; i < len(storedMaccs); i++ {
suite.accountKeeper.RemoveAccount(suite.ctx, storedMaccs[i])
}
})
maccs := make([]string, 0, len(suite.maccPerms))
for k := range suite.maccPerms {
maccs = append(maccs, k)
}
sort.Strings(maccs)
storedMaccs := suite.setModuleAccounts(suite.ctx, suite.accountKeeper, maccs)
suite.runModuleAccountsIterations(suite.accountKeeper, storedMaccs)
}