From 2b7858c8fcc76907aae76cf9140b72b99b9100a6 Mon Sep 17 00:00:00 2001 From: hacheigriega Date: Sat, 30 Nov 2024 08:25:20 -0500 Subject: [PATCH] chore(pubkey): skip endblock if proving scheme already enabled and add test --- scripts/local_multi_setup.sh | 2 +- x/pubkey/client/cli/query.go | 2 +- x/pubkey/keeper/endblock.go | 21 ++- x/pubkey/keeper/endblock_test.go | 224 ++++++++++++++++++++++++++++++ x/pubkey/keeper/grpc_query.go | 2 +- x/pubkey/keeper/keeper.go | 8 ++ x/vesting/keeper/clawback_test.go | 1 - 7 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 x/pubkey/keeper/endblock_test.go diff --git a/scripts/local_multi_setup.sh b/scripts/local_multi_setup.sh index b47b0c3d..306d8004 100755 --- a/scripts/local_multi_setup.sh +++ b/scripts/local_multi_setup.sh @@ -101,7 +101,7 @@ sed -i '' -E 's|prometheus = false|prometheus = true|g' $VALIDATOR4_CONFIG sed -i '' -E 's|prometheus_listen_addr = ":26660"|prometheus_listen_addr = ":26610"|g' $VALIDATOR4_CONFIG # modify genesis file -jq '.consensus.params.abci.vote_extensions_enable_height = "15"' $HOME/.sedad/validator1/config/genesis.json > temp.json && mv temp.json $HOME/.sedad/validator1/config/genesis.json +jq '.consensus.params.abci.vote_extensions_enable_height = "10"' $HOME/.sedad/validator1/config/genesis.json > temp.json && mv temp.json $HOME/.sedad/validator1/config/genesis.json cp $HOME/.sedad/validator1/config/genesis.json $HOME/.sedad/validator2/config/genesis.json cp $HOME/.sedad/validator1/config/genesis.json $HOME/.sedad/validator3/config/genesis.json diff --git a/x/pubkey/client/cli/query.go b/x/pubkey/client/cli/query.go index e404e6c2..96cd79f8 100644 --- a/x/pubkey/client/cli/query.go +++ b/x/pubkey/client/cli/query.go @@ -63,7 +63,7 @@ func GetProvingSchemes() *cobra.Command { Use: "proving-schemes", Short: "Query statuses of the SEDA proving schemes", Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err diff --git a/x/pubkey/keeper/endblock.go b/x/pubkey/keeper/endblock.go index 9f2bf307..fddb746d 100644 --- a/x/pubkey/keeper/endblock.go +++ b/x/pubkey/keeper/endblock.go @@ -25,13 +25,22 @@ func (k Keeper) EndBlock(ctx sdk.Context) (err error) { err = nil }() + // If secp256k1 proving scheme is already enabled, do nothing. + isEnabled, err := k.IsProvingSchemeEnabled(ctx, utils.SEDAKeyIndexSecp256k1) + if err != nil { + return err + } + if isEnabled { + return + } + + // If the sum of the voting power has reached 80%, enable secp256k1 + // proving scheme. totalPower, err := k.stakingKeeper.GetLastTotalPower(ctx) if err != nil { return err } - // If the sum of the voting power has reached (2/3 + 1), enable - // secp256k1 proving scheme. var powerSum uint64 err = k.stakingKeeper.IterateLastValidatorPowers(ctx, func(valAddr sdk.ValAddress, power int64) (stop bool) { _, err := k.GetValidatorKeyAtIndex(ctx, valAddr, utils.SEDAKeyIndexSecp256k1) @@ -42,6 +51,7 @@ func (k Keeper) EndBlock(ctx sdk.Context) (err error) { panic(err) } } else { + //nolint:gosec // G115: We shouldn't get negative power anyway. powerSum += uint64(power) } return false @@ -50,7 +60,10 @@ func (k Keeper) EndBlock(ctx sdk.Context) (err error) { return err } - if requiredPower := ((totalPower.Int64() * 4) / 5) + 1; powerSum >= uint64(requiredPower) { + gotPower := powerSum * 100 + //nolint:gosec // G115: We shouldn't get negative power anyway. + requiredPower := uint64(totalPower.Int64()*100*4/5 + 1) + if gotPower >= requiredPower { err = k.EnableProvingScheme(ctx, utils.SEDAKeyIndexSecp256k1) if err != nil { return err @@ -58,6 +71,6 @@ func (k Keeper) EndBlock(ctx sdk.Context) (err error) { // TODO: Jail validators (active and inactive) without required // public keys. } - + k.Logger(ctx).Info("checked status of secp256k1 proving scheme", "required", requiredPower, "got", gotPower) return } diff --git a/x/pubkey/keeper/endblock_test.go b/x/pubkey/keeper/endblock_test.go new file mode 100644 index 00000000..3b3574ff --- /dev/null +++ b/x/pubkey/keeper/endblock_test.go @@ -0,0 +1,224 @@ +package keeper_test + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + "testing" + "time" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + + "cosmossdk.io/core/appmodule" + "cosmossdk.io/log" + "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" + + "github.com/cosmos/cosmos-sdk/codec" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/runtime" + sdkintegration "github.com/cosmos/cosmos-sdk/testutil/integration" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + sdkstakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + sdkstakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/sedaprotocol/seda-chain/app" + "github.com/sedaprotocol/seda-chain/app/params" + "github.com/sedaprotocol/seda-chain/app/utils" + "github.com/sedaprotocol/seda-chain/integration" + "github.com/sedaprotocol/seda-chain/x/pubkey" + "github.com/sedaprotocol/seda-chain/x/pubkey/keeper" + "github.com/sedaprotocol/seda-chain/x/pubkey/types" + "github.com/sedaprotocol/seda-chain/x/staking" + stakingkeeper "github.com/sedaprotocol/seda-chain/x/staking/keeper" + "github.com/sedaprotocol/seda-chain/x/vesting" +) + +const ( + bech32Prefix = "seda" + bondDenom = "aseda" +) + +var ( + zeroCoins sdk.Coins + funderAddr = sdk.MustAccAddressFromBech32("seda1gujynygp0tkwzfpt0g7dv4829jwyk8f0yhp88d") + testAddrs = []sdk.AccAddress{ + sdk.AccAddress([]byte("to0_________________")), + sdk.AccAddress([]byte("to1_________________")), + sdk.AccAddress([]byte("to2_________________")), + sdk.AccAddress([]byte("to3_________________")), + sdk.AccAddress([]byte("to4_________________")), + sdk.AccAddress([]byte("to5_________________")), + sdk.AccAddress([]byte("to6_________________")), + sdk.AccAddress([]byte("to7_________________")), + sdk.AccAddress([]byte("to8_________________")), + sdk.AccAddress([]byte("to9_________________")), + } +) + +type fixture struct { + *integration.IntegationApp + cdc codec.Codec + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + stakingKeeper stakingkeeper.Keeper + keeper keeper.Keeper +} + +func initFixture(tb testing.TB) *fixture { + tb.Helper() + keys := storetypes.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, sdkstakingtypes.StoreKey, types.StoreKey, + ) + cdc := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{}, vesting.AppModuleBasic{}).Codec + + logger := log.NewTestLogger(tb) + cms := sdkintegration.CreateMultiStore(keys, logger) + + newCtx := sdk.NewContext(cms, cmtproto.Header{Time: time.Now().UTC()}, true, logger) + + authority := authtypes.NewModuleAddress(govtypes.ModuleName) + + maccPerms := map[string][]string{ + minttypes.ModuleName: {authtypes.Minter}, + sdkstakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + sdkstakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + types.ModuleName: {authtypes.Burner}, + } + + accountKeeper := authkeeper.NewAccountKeeper( + cdc, + runtime.NewKVStoreService(keys[authtypes.StoreKey]), + authtypes.ProtoBaseAccount, + maccPerms, + addresscodec.NewBech32Codec(params.Bech32PrefixAccAddr), + params.Bech32PrefixAccAddr, + authority.String(), + ) + + blockedAddresses := map[string]bool{ + accountKeeper.GetAuthority(): false, + } + bankKeeper := bankkeeper.NewBaseKeeper( + cdc, + runtime.NewKVStoreService(keys[banktypes.StoreKey]), + accountKeeper, + blockedAddresses, + authority.String(), + log.NewNopLogger(), + ) + + sdkstakingKeeper := sdkstakingkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[sdkstakingtypes.StoreKey]), accountKeeper, bankKeeper, authority.String(), addresscodec.NewBech32Codec(params.Bech32PrefixValAddr), addresscodec.NewBech32Codec(params.Bech32PrefixConsAddr)) + stakingKeeper := stakingkeeper.NewKeeper(sdkstakingKeeper) + + stakingParams := sdkstakingtypes.DefaultParams() + stakingParams.BondDenom = bondDenom + err := stakingKeeper.SetParams(newCtx, stakingParams) + require.NoError(tb, err) + + pubkeyKeeper := keeper.NewKeeper( + runtime.NewKVStoreService(keys[types.StoreKey]), + stakingKeeper, + addresscodec.NewBech32Codec(params.Bech32PrefixValAddr), + ) + + authModule := auth.NewAppModule(cdc, accountKeeper, app.RandomGenesisAccounts, nil) + bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper, nil) + stakingModule := staking.NewAppModule(cdc, stakingKeeper, accountKeeper, bankKeeper, nil) + pubkeyModule := pubkey.NewAppModule(cdc, *pubkeyKeeper) + + integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, map[string]appmodule.AppModule{ + authtypes.ModuleName: authModule, + banktypes.ModuleName: bankModule, + sdkstakingtypes.ModuleName: stakingModule, + types.ModuleName: pubkeyModule, + }) + + types.RegisterMsgServer(integrationApp.MsgServiceRouter(), keeper.NewMsgServerImpl(*pubkeyKeeper)) + sdkstakingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), sdkstakingkeeper.NewMsgServerImpl(sdkstakingKeeper)) + + return &fixture{ + IntegationApp: integrationApp, + cdc: cdc, + accountKeeper: accountKeeper, + bankKeeper: bankKeeper, + stakingKeeper: *stakingKeeper, + keeper: *pubkeyKeeper, + } +} + +func createValidators(t *testing.T, f *fixture, powers []int64) ([]sdk.AccAddress, []sdk.ValAddress, []cryptotypes.PubKey) { + t.Helper() + addrs := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, f.Context(), len(powers), math.NewIntFromUint64(1e19)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) + valPks := simtestutil.CreateTestPubKeys(len(powers)) + + for i := 0; i < len(powers); i++ { + val, err := sdkstakingtypes.NewValidator(valAddrs[i].String(), valPks[i], sdkstakingtypes.Description{}) + require.NoError(t, err) + + require.NoError(t, f.stakingKeeper.SetValidator(f.Context(), val)) + require.NoError(t, f.stakingKeeper.SetValidatorByConsAddr(f.Context(), val)) + require.NoError(t, f.stakingKeeper.SetNewValidatorByPowerIndex(f.Context(), val)) + + _, err = f.stakingKeeper.Delegate(f.Context(), addrs[i], f.stakingKeeper.TokensFromConsensusPower(f.Context(), powers[i]), sdkstakingtypes.Unbonded, val, true) + require.NoError(t, err) + } + + _, err := f.stakingKeeper.EndBlocker(f.Context()) + require.NoError(t, err) + return addrs, valAddrs, valPks +} + +func TestEndBlock(t *testing.T) { + f := initFixture(t) + ctx := f.Context() + + f.keeper.InitGenesis(ctx, *types.DefaultGenesisState()) + + _, valAddrs, _ := createValidators(t, f, []int64{1, 3, 5, 7, 2, 1}) // 1+3+5+7+2+1 = 19 + pubKeys := generatePubKeys(t, 6) + + expectedIsEnabled := false + for i := range valAddrs { + err := f.keeper.SetValidatorKeyAtIndex(ctx, valAddrs[i], utils.SEDAKeyIndexSecp256k1, pubKeys[i]) + require.NoError(t, err) + err = f.keeper.EndBlock(ctx) + require.NoError(t, err) + + if i >= 3 { + expectedIsEnabled = true + } + isEnabled, err := f.keeper.IsProvingSchemeEnabled(ctx, utils.SEDAKeyIndexSecp256k1) + require.NoError(t, err) + require.Equal(t, expectedIsEnabled, isEnabled) + } +} + +func generatePubKeys(t *testing.T, num int) [][]byte { + t.Helper() + var pubKeys [][]byte + for i := 0; i < num; i++ { + privKey, err := ecdsa.GenerateKey(ethcrypto.S256(), rand.Reader) + if err != nil { + panic(fmt.Sprintf("failed to generate secp256k1 private key: %v", err)) + } + pubKeys = append(pubKeys, elliptic.Marshal(privKey.PublicKey, privKey.PublicKey.X, privKey.PublicKey.Y)) + } + return pubKeys +} diff --git a/x/pubkey/keeper/grpc_query.go b/x/pubkey/keeper/grpc_query.go index 270b427d..439419e8 100644 --- a/x/pubkey/keeper/grpc_query.go +++ b/x/pubkey/keeper/grpc_query.go @@ -21,7 +21,7 @@ func (q Querier) ValidatorKeys(ctx context.Context, req *types.QueryValidatorKey return &types.QueryValidatorKeysResponse{ValidatorPubKeys: result}, nil } -func (q Querier) ProvingSchemes(ctx context.Context, req *types.QueryProvingSchemesRequest) (*types.QueryProvingSchemesResponse, error) { +func (q Querier) ProvingSchemes(ctx context.Context, _ *types.QueryProvingSchemesRequest) (*types.QueryProvingSchemesResponse, error) { schemes, err := q.GetAllProvingSchemes(sdk.UnwrapSDKContext(ctx)) if err != nil { return nil, err diff --git a/x/pubkey/keeper/keeper.go b/x/pubkey/keeper/keeper.go index dcbdcd1d..a194aa03 100644 --- a/x/pubkey/keeper/keeper.go +++ b/x/pubkey/keeper/keeper.go @@ -148,6 +148,14 @@ func (k Keeper) EnableProvingScheme(ctx context.Context, index utils.SEDAKeyInde return nil } +func (k Keeper) IsProvingSchemeEnabled(ctx context.Context, index utils.SEDAKeyIndex) (bool, error) { + isEnabled, err := k.provingSchemes.Get(ctx, uint32(index)) + if err != nil { + return false, err + } + return isEnabled, nil +} + func (k Keeper) GetAllProvingSchemes(ctx sdk.Context) ([]types.ProvingScheme, error) { itr, err := k.provingSchemes.Iterate(ctx, nil) if err != nil { diff --git a/x/vesting/keeper/clawback_test.go b/x/vesting/keeper/clawback_test.go index f10791e9..e2640862 100644 --- a/x/vesting/keeper/clawback_test.go +++ b/x/vesting/keeper/clawback_test.go @@ -21,7 +21,6 @@ func TestClawback(t *testing.T) { require.NoError(t, err) _, valAddrs, valPks := createValidators(t, f, []int64{5, 5, 5}) - require.NoError(t, err) testCases := []struct { testName string