Skip to content

Commit

Permalink
chore(pubkey): skip endblock if proving scheme already enabled and ad…
Browse files Browse the repository at this point in the history
…d test
  • Loading branch information
hacheigriega committed Nov 30, 2024
1 parent dc5f3da commit 2b7858c
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 8 deletions.
2 changes: 1 addition & 1 deletion scripts/local_multi_setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion x/pubkey/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 17 additions & 4 deletions x/pubkey/keeper/endblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -50,14 +60,17 @@ 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
}
// 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
}
224 changes: 224 additions & 0 deletions x/pubkey/keeper/endblock_test.go
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion x/pubkey/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions x/pubkey/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 0 additions & 1 deletion x/vesting/keeper/clawback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 2b7858c

Please sign in to comment.