Skip to content

Commit

Permalink
feat(x/pubkey): add proving scheme activation lag
Browse files Browse the repository at this point in the history
  • Loading branch information
hacheigriega committed Dec 2, 2024
1 parent 2b7858c commit 5bf148d
Show file tree
Hide file tree
Showing 15 changed files with 420 additions and 326 deletions.
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ func NewApp(
)

app.PubKeyKeeper = *pubkeykeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[pubkeytypes.StoreKey]),
app.StakingKeeper,
authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
Expand Down
9 changes: 1 addition & 8 deletions proto/sedachain/pubkey/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ option go_package = "github.com/sedaprotocol/seda-chain/x/pubkey/types";
message GenesisState {
repeated ValidatorPubKeys validator_pub_keys = 1
[ (gogoproto.nullable) = false ];
repeated ProvingScheme proving_schemes = 2
[ (gogoproto.nullable) = false ];
repeated ProvingScheme proving_schemes = 2 [ (gogoproto.nullable) = false ];
}

// ValidatorPubKeys defines a validator's list of registered public keys
Expand All @@ -22,9 +21,3 @@ message ValidatorPubKeys {
[ (cosmos_proto.scalar) = "cosmos.ValidatorAddressString" ];
repeated IndexedPubKey indexed_pub_keys = 2 [ (gogoproto.nullable) = false ];
}

// ProvingScheme defines the status of a proving scheme.
message ProvingScheme {
uint32 index = 1;
bool is_enabled = 2;
}
14 changes: 14 additions & 0 deletions proto/sedachain/pubkey/v1/pubkey.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ message IndexedPubKey {
bytes pub_key = 2
[ (cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey" ];
}

// ProvingScheme defines a proving scheme.
message ProvingScheme {
// index is the SEDA key index.
uint32 index = 1;
// is_activated indicates if the proving scheme has been activated.
bool is_activated = 2;
// activation_height is the height at which the proving scheme is to
// be activated. This field is set to -1 by default until the public
// key registration rate reaches the activation threshold and is reset
// if the public key registration rate goes below the threshold before
// the scheme is activated.
int64 activation_height = 3;
}
12 changes: 6 additions & 6 deletions proto/sedachain/pubkey/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "google/api/annotations.proto";
import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "sedachain/pubkey/v1/genesis.proto";
import "sedachain/pubkey/v1/pubkey.proto";

option go_package = "github.com/sedaprotocol/seda-chain/x/pubkey/types";

Expand All @@ -19,10 +20,9 @@ service Query {

// ProvingSchemes returns the statuses of the SEDA proving schemes.
rpc ProvingSchemes(QueryProvingSchemesRequest)
returns (QueryProvingSchemesResponse) {
option (google.api.http).get =
"/seda-chain/pubkey/proving_schemes";
}
returns (QueryProvingSchemesResponse) {
option (google.api.http).get = "/seda-chain/pubkey/proving_schemes";
}
}

// QueryValidatorKeysRequest is request type for the Query/ValidatorKeys
Expand All @@ -38,11 +38,11 @@ message QueryValidatorKeysResponse {
ValidatorPubKeys validator_pub_keys = 1 [ (gogoproto.nullable) = false ];
}

// QueryProvingSchemesRequest is request type for the Query/ProvingSchemes
// QueryProvingSchemesRequest is request type for the Query/ProvingSchemes
// RPC method.
message QueryProvingSchemesRequest {}

// QueryProvingSchemesResponse is response type for the Query/ProvingSchemes
// QueryProvingSchemesResponse is response type for the Query/ProvingSchemes
// RPC method.
message QueryProvingSchemesResponse {
repeated ProvingScheme proving_schemes = 1 [ (gogoproto.nullable) = false ];
Expand Down
4 changes: 2 additions & 2 deletions scripts/go-lint-all.bash
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ lint_module() {
shift
cd "$(dirname "$root")" &&
echo "linting $(grep "^module" go.mod) [$(date -Iseconds -u)]" &&
golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" "$@"
golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" --tests=false --exclude-dirs="e2e" "$@"
}
export -f lint_module

Expand All @@ -31,7 +31,7 @@ else
for f in $(dirname $(echo "$GIT_DIFF" | tr -d "'") | uniq); do
echo "linting $f [$(date -Iseconds -u)]" &&
cd $f &&
golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" "$@" &&
golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" --tests=false --exclude-dirs="e2e" "$@" &&
cd $REPO_ROOT
done
fi
6 changes: 3 additions & 3 deletions scripts/local_multi_setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ echo "4 validators are up and running!"

# generate and upload SEDA keys
$BIN tx pubkey add-seda-keys --from validator1 --keyring-backend test --home $HOME/.sedad/validator1 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes
$BIN tx pubkey add-seda-keys --from validator2 --keyring-backend test --home $HOME/.sedad/validator2 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes
$BIN tx pubkey add-seda-keys --from validator3 --keyring-backend test --home $HOME/.sedad/validator3 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes
$BIN tx pubkey add-seda-keys --from validator4 --keyring-backend test --home $HOME/.sedad/validator4 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes
# $BIN tx pubkey add-seda-keys --from validator2 --keyring-backend test --home $HOME/.sedad/validator2 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes
# $BIN tx pubkey add-seda-keys --from validator3 --keyring-backend test --home $HOME/.sedad/validator3 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes
# $BIN tx pubkey add-seda-keys --from validator4 --keyring-backend test --home $HOME/.sedad/validator4 --gas-prices 10000000000aseda --gas auto --gas-adjustment 2.0 --keyring-backend test --chain-id=testing --node http://localhost:26657 --yes

# restart to use SEDA keys
sleep 10
Expand Down
1 change: 1 addition & 0 deletions x/batching/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
abci "github.com/cometbft/cometbft/abci/types"

"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

Expand Down
63 changes: 43 additions & 20 deletions x/pubkey/keeper/endblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sedaprotocol/seda-chain/app/utils"
"github.com/sedaprotocol/seda-chain/x/pubkey/types"
)

func (k Keeper) EndBlock(ctx sdk.Context) (err error) {
Expand All @@ -25,52 +26,74 @@ 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)
scheme, err := k.GetProvingScheme(ctx, utils.SEDAKeyIndexSecp256k1)
if err != nil {
return err
}
if isEnabled {
if scheme.ActivationHeight != types.DefaultActivationHeight && ctx.BlockHeight() >= scheme.ActivationHeight {
scheme.IsActivated = true
scheme.ActivationHeight = types.DefaultActivationHeight
err = k.SetProvingScheme(ctx, scheme)
if err != nil {
return err
}

// TODO: Jail validators (active and inactive) without required
// public keys.
}
if scheme.IsActivated {
return
}
activationInProgress := scheme.ActivationHeight != types.DefaultActivationHeight

met, err := k.CheckKeyRegistrationRate(ctx, utils.SEDAKeyIndexSecp256k1)
if err != nil {
return err
}
if (activationInProgress && !met) || (!activationInProgress && met) {
err = k.StartProvingSchemeActivation(ctx, utils.SEDAKeyIndexSecp256k1)
if err != nil {
return err
}
}
return
}

// CheckKeyRegistrationRate checks if the current registration rate of
// public keys of the given key scheme surpasses the threshold.
func (k Keeper) CheckKeyRegistrationRate(ctx sdk.Context, index utils.SEDAKeyIndex) (bool, error) {
// 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
return false, err
}

var powerSum uint64
err = k.stakingKeeper.IterateLastValidatorPowers(ctx, func(valAddr sdk.ValAddress, power int64) (stop bool) {
_, err := k.GetValidatorKeyAtIndex(ctx, valAddr, utils.SEDAKeyIndexSecp256k1)
_, err := k.GetValidatorKeyAtIndex(ctx, valAddr, index)
if err != nil {
if errors.Is(err, collections.ErrNotFound) {
return false
} else {
panic(err)
}
} else {
//nolint:gosec // G115: We shouldn't get negative power anyway.
powerSum += uint64(power)
panic(err)
}
//nolint:gosec // G115: We shouldn't get negative power anyway.
powerSum += uint64(power)
return false
})
if err != nil {
return err
return false, err
}

gotPower := powerSum * 100
//nolint:gosec // G115: We shouldn't get negative power anyway.
requiredPower := uint64(totalPower.Int64()*100*4/5 + 1)
gotPower := powerSum * 100

k.Logger(ctx).Info("checked status of secp256k1 proving scheme", "required", requiredPower, "got", gotPower)

if gotPower >= requiredPower {
err = k.EnableProvingScheme(ctx, utils.SEDAKeyIndexSecp256k1)
if err != nil {
return err
}
// TODO: Jail validators (active and inactive) without required
// public keys.
return true, nil
}
k.Logger(ctx).Info("checked status of secp256k1 proving scheme", "required", requiredPower, "got", gotPower)
return
return false, nil
}
2 changes: 1 addition & 1 deletion x/pubkey/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) {
}
}
for _, scheme := range data.ProvingSchemes {
err := k.SetProvingScheme(ctx, utils.SEDAKeyIndex(scheme.Index), scheme.IsEnabled)
err := k.SetProvingScheme(ctx, scheme)
if err != nil {
panic(err)
}
Expand Down
1 change: 1 addition & 0 deletions x/pubkey/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sedaprotocol/seda-chain/x/pubkey/types"
)

Expand Down
46 changes: 28 additions & 18 deletions x/pubkey/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,39 @@ import (
storetypes "cosmossdk.io/core/store"
"cosmossdk.io/log"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/sedaprotocol/seda-chain/app/utils"
"github.com/sedaprotocol/seda-chain/x/pubkey/types"
)

// ActivationLag is the number of blocks to wait before activating
// a proving scheme once the threshold of public key registration rate
// is reached.
const ActivationLag = 25

type Keeper struct {
stakingKeeper types.StakingKeeper
validatorAddressCodec address.Codec

Schema collections.Schema
pubKeys collections.Map[collections.Pair[[]byte, uint32], []byte]
provingSchemes collections.Map[uint32, bool]
provingSchemes collections.Map[uint32, types.ProvingScheme]
}

func NewKeeper(storeService storetypes.KVStoreService, sk types.StakingKeeper, validatorAddressCodec address.Codec) *Keeper {
if validatorAddressCodec == nil {
func NewKeeper(cdc codec.BinaryCodec, storeService storetypes.KVStoreService, sk types.StakingKeeper, valAddrCdc address.Codec) *Keeper {
if valAddrCdc == nil {
panic("validator address codec is nil")
}

sb := collections.NewSchemaBuilder(storeService)
k := Keeper{
stakingKeeper: sk,
validatorAddressCodec: validatorAddressCodec,
validatorAddressCodec: valAddrCdc,
pubKeys: collections.NewMap(sb, types.PubKeysPrefix, "pubkeys", collections.PairKeyCodec(collections.BytesKey, collections.Uint32Key), collections.BytesValue),
provingSchemes: collections.NewMap(sb, types.ProvingSchemesPrefix, "proving_schemes", collections.Uint32Key, collections.BoolValue),
provingSchemes: collections.NewMap(sb, types.ProvingSchemesPrefix, "proving_schemes", collections.Uint32Key, codec.CollValue[types.ProvingScheme](cdc)),
}

schema, err := sb.Build()
Expand Down Expand Up @@ -132,28 +138,35 @@ func (k Keeper) GetAllValidatorPubKeys(ctx context.Context) ([]types.ValidatorPu
return valPubKeys, err
}

func (k Keeper) SetProvingScheme(ctx context.Context, index utils.SEDAKeyIndex, isEnabled bool) error {
err := k.provingSchemes.Set(ctx, uint32(index), isEnabled)
func (k Keeper) SetProvingScheme(ctx context.Context, scheme types.ProvingScheme) error {
err := k.provingSchemes.Set(ctx, scheme.Index, scheme)
if err != nil {
return err
}
return nil
}

func (k Keeper) EnableProvingScheme(ctx context.Context, index utils.SEDAKeyIndex) error {
err := k.provingSchemes.Set(ctx, uint32(index), true)
func (k Keeper) GetProvingScheme(ctx context.Context, index utils.SEDAKeyIndex) (types.ProvingScheme, error) {
return k.provingSchemes.Get(ctx, uint32(index))
}

// StartProvingSchemeActivation starts the activation of the given
// proving scheme.
func (k Keeper) StartProvingSchemeActivation(ctx sdk.Context, index utils.SEDAKeyIndex) error {
scheme, err := k.provingSchemes.Get(ctx, uint32(index))
if err != nil {
return err
}
return nil
scheme.ActivationHeight = ctx.BlockHeight() + ActivationLag
return k.SetProvingScheme(ctx, scheme)
}

func (k Keeper) IsProvingSchemeEnabled(ctx context.Context, index utils.SEDAKeyIndex) (bool, error) {
isEnabled, err := k.provingSchemes.Get(ctx, uint32(index))
func (k Keeper) IsProvingSchemeActivated(ctx context.Context, index utils.SEDAKeyIndex) (bool, error) {
scheme, err := k.provingSchemes.Get(ctx, uint32(index))
if err != nil {
return false, err
}
return isEnabled, nil
return scheme.IsActivated, nil
}

func (k Keeper) GetAllProvingSchemes(ctx sdk.Context) ([]types.ProvingScheme, error) {
Expand All @@ -170,14 +183,11 @@ func (k Keeper) GetAllProvingSchemes(ctx sdk.Context) ([]types.ProvingScheme, er
return nil, err
}

isEnabled, err := k.provingSchemes.Get(ctx, kv.Key)
scheme, err := k.provingSchemes.Get(ctx, kv.Key)
if err != nil {
return nil, err
}
schemes = append(schemes, types.ProvingScheme{
Index: kv.Key,
IsEnabled: isEnabled,
})
schemes = append(schemes, scheme)
}
return schemes, nil
}
Expand Down
10 changes: 8 additions & 2 deletions x/pubkey/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import (
fmt "fmt"
)

const (
// DefaultActivationHeight indicates that activation is not in progress.
DefaultActivationHeight = -1
)

func DefaultGenesisState() *GenesisState {
return &GenesisState{
ProvingSchemes: []ProvingScheme{
{
Index: 0, // TODO resolve import cycle for uint32(utils.SEDAKeyIndexSecp256k1),
IsEnabled: false,
Index: 0, // TODO resolve import cycle for uint32(utils.SEDAKeyIndexSecp256k1),
IsActivated: false,
ActivationHeight: DefaultActivationHeight,
},
},
}
Expand Down
Loading

0 comments on commit 5bf148d

Please sign in to comment.