-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d10dd40
commit 540d59f
Showing
107 changed files
with
31,177 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package zoneconcierge | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/keeper" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types" | ||
abci "github.com/cometbft/cometbft/abci/types" | ||
"github.com/cosmos/cosmos-sdk/telemetry" | ||
) | ||
|
||
// BeginBlocker sends a pending packet for every channel upon each new block, | ||
// so that the relayer is kept awake to relay headers | ||
func BeginBlocker(ctx context.Context, k keeper.Keeper) error { | ||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) | ||
return nil | ||
} | ||
|
||
func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) { | ||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) | ||
|
||
k.BroadcastBTCStakingConsumerEvents(ctx) | ||
return []abci.ValidatorUpdate{}, nil | ||
} |
105 changes: 105 additions & 0 deletions
105
proto/babylon/finality/zoneconcierge/client/cli/query.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
|
||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types" | ||
) | ||
|
||
// GetQueryCmd returns the cli query commands for this module | ||
func GetQueryCmd(queryRoute string) *cobra.Command { | ||
// Group zoneconcierge queries under a subcommand | ||
cmd := &cobra.Command{ | ||
Use: types.ModuleName, | ||
Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), | ||
DisableFlagParsing: true, | ||
SuggestionsMinimumDistance: 2, | ||
RunE: client.ValidateCmd, | ||
} | ||
|
||
cmd.AddCommand(CmdChainsInfo()) | ||
cmd.AddCommand(CmdFinalizedChainsInfo()) | ||
cmd.AddCommand(CmdEpochChainsInfoInfo()) | ||
return cmd | ||
} | ||
|
||
func CmdChainsInfo() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "chains-info <consumer-ids>", | ||
Short: "retrieve the latest info for a given list of consumers", | ||
Args: cobra.ArbitraryArgs, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
queryClient := types.NewQueryClient(clientCtx) | ||
req := types.QueryChainsInfoRequest{ConsumerIds: args} | ||
resp, err := queryClient.ChainsInfo(cmd.Context(), &req) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return clientCtx.PrintProto(resp) | ||
}, | ||
} | ||
|
||
flags.AddQueryFlagsToCmd(cmd) | ||
return cmd | ||
} | ||
|
||
func CmdFinalizedChainsInfo() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "finalized-chains-info <consumer-ids>", | ||
Short: "retrieve the finalized info for a given list of consumers", | ||
Args: cobra.ArbitraryArgs, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
prove, _ := cmd.Flags().GetBool("prove") | ||
|
||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
queryClient := types.NewQueryClient(clientCtx) | ||
req := types.QueryFinalizedChainsInfoRequest{ConsumerIds: args, Prove: prove} | ||
resp, err := queryClient.FinalizedChainsInfo(cmd.Context(), &req) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return clientCtx.PrintProto(resp) | ||
}, | ||
} | ||
|
||
cmd.Flags().Bool("prove", false, "whether to retrieve proofs for each FinalizedChainInfo") | ||
flags.AddQueryFlagsToCmd(cmd) | ||
|
||
return cmd | ||
} | ||
|
||
func CmdEpochChainsInfoInfo() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "epoch-chains-info <epoch-num> <consumer-ids>", | ||
Short: "retrieve the latest info for a list of consumers in a given epoch", | ||
Args: cobra.MinimumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
queryClient := types.NewQueryClient(clientCtx) | ||
|
||
epoch, err := strconv.ParseUint(args[0], 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
req := types.QueryEpochChainsInfoRequest{EpochNum: epoch, ConsumerIds: args[1:]} | ||
resp, err := queryClient.EpochChainsInfo(cmd.Context(), &req) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return clientCtx.PrintProto(resp) | ||
}, | ||
} | ||
|
||
flags.AddQueryFlagsToCmd(cmd) | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
// "github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types" | ||
) | ||
|
||
// GetTxCmd returns the transaction commands for this module | ||
func GetTxCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: types.ModuleName, | ||
Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), | ||
DisableFlagParsing: true, | ||
SuggestionsMinimumDistance: 2, | ||
RunE: client.ValidateCmd, | ||
} | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package zoneconcierge | ||
|
||
import ( | ||
"context" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/keeper" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// InitGenesis initializes the module's state from a provided genesis state. | ||
func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { | ||
sdkCtx := sdk.UnwrapSDKContext(ctx) | ||
// set params for this module | ||
if err := k.SetParams(ctx, genState.Params); err != nil { | ||
panic(err) | ||
} | ||
|
||
k.SetPort(ctx, genState.PortId) | ||
// Only try to bind to port if it is not already bound, since we may already own | ||
// port capability from capability InitGenesis | ||
if !k.IsBound(sdkCtx, genState.PortId) { | ||
// module binds to the port on InitChain | ||
// and claims the returned capability | ||
err := k.BindPort(sdkCtx, genState.PortId) | ||
if err != nil { | ||
panic("could not claim port capability: " + err.Error()) | ||
} | ||
} | ||
} | ||
|
||
// ExportGenesis returns the module's exported genesis | ||
func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { | ||
genesis := types.DefaultGenesis() | ||
genesis.Params = k.GetParams(ctx) | ||
genesis.PortId = k.GetPort(ctx) | ||
return genesis | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package zoneconcierge_test | ||
|
||
import ( | ||
"testing" | ||
|
||
keepertest "github.com/babylonlabs-io/babylon/testutil/keeper" | ||
"github.com/babylonlabs-io/babylon/testutil/nullify" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestGenesis(t *testing.T) { | ||
genesisState := types.GenesisState{ | ||
PortId: types.PortID, | ||
Params: types.Params{IbcPacketTimeoutSeconds: 100}, | ||
} | ||
|
||
k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil, nil) | ||
zoneconcierge.InitGenesis(ctx, *k, genesisState) | ||
got := zoneconcierge.ExportGenesis(ctx, *k) | ||
require.NotNil(t, got) | ||
|
||
nullify.Fill(&genesisState) | ||
nullify.Fill(got) | ||
|
||
require.Equal(t, genesisState.PortId, got.PortId) | ||
require.Equal(t, genesisState.Params, got.Params) | ||
} |
74 changes: 74 additions & 0 deletions
74
proto/babylon/finality/zoneconcierge/keeper/canonical_chain_indexer.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package keeper | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/cosmos/cosmos-sdk/runtime" | ||
|
||
sdkerrors "cosmossdk.io/errors" | ||
"cosmossdk.io/store/prefix" | ||
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// FindClosestHeader finds the IndexedHeader that is closest to (but not after) the given height | ||
func (k Keeper) FindClosestHeader(ctx context.Context, consumerID string, height uint64) (*types.IndexedHeader, error) { | ||
chainInfo, err := k.GetChainInfo(ctx, consumerID) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get chain info for chain with ID %s: %w", consumerID, err) | ||
} | ||
|
||
// if the given height is no lower than the latest header, return the latest header directly | ||
if chainInfo.LatestHeader.Height <= height { | ||
return chainInfo.LatestHeader, nil | ||
} | ||
|
||
// the requested height is lower than the latest header, trace back until finding a timestamped header | ||
store := k.canonicalChainStore(ctx, consumerID) | ||
heightBytes := sdk.Uint64ToBigEndian(height) | ||
iter := store.ReverseIterator(nil, heightBytes) | ||
defer iter.Close() | ||
// if there is no key within range [0, height], return error | ||
if !iter.Valid() { | ||
return nil, fmt.Errorf("chain with ID %s does not have a timestamped header before height %d", consumerID, height) | ||
} | ||
// find the header in bytes, decode and return | ||
headerBytes := iter.Value() | ||
var header types.IndexedHeader | ||
k.cdc.MustUnmarshal(headerBytes, &header) | ||
return &header, nil | ||
} | ||
|
||
func (k Keeper) GetHeader(ctx context.Context, consumerID string, height uint64) (*types.IndexedHeader, error) { | ||
store := k.canonicalChainStore(ctx, consumerID) | ||
heightBytes := sdk.Uint64ToBigEndian(height) | ||
if !store.Has(heightBytes) { | ||
return nil, types.ErrHeaderNotFound | ||
} | ||
headerBytes := store.Get(heightBytes) | ||
var header types.IndexedHeader | ||
k.cdc.MustUnmarshal(headerBytes, &header) | ||
return &header, nil | ||
} | ||
|
||
func (k Keeper) insertHeader(ctx context.Context, consumerID string, header *types.IndexedHeader) error { | ||
if header == nil { | ||
return sdkerrors.Wrapf(types.ErrInvalidHeader, "header is nil") | ||
} | ||
// NOTE: we can accept header without ancestor since IBC connection can be established at any height | ||
store := k.canonicalChainStore(ctx, consumerID) | ||
store.Set(sdk.Uint64ToBigEndian(header.Height), k.cdc.MustMarshal(header)) | ||
return nil | ||
} | ||
|
||
// canonicalChainStore stores the canonical chain of a CZ, formed as a list of IndexedHeader | ||
// prefix: CanonicalChainKey || consumerID | ||
// key: height | ||
// value: IndexedHeader | ||
func (k Keeper) canonicalChainStore(ctx context.Context, consumerID string) prefix.Store { | ||
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) | ||
canonicalChainStore := prefix.NewStore(storeAdapter, types.CanonicalChainKey) | ||
consumerIDBytes := []byte(consumerID) | ||
return prefix.NewStore(canonicalChainStore, consumerIDBytes) | ||
} |
85 changes: 85 additions & 0 deletions
85
proto/babylon/finality/zoneconcierge/keeper/canonical_chain_indexer_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package keeper_test | ||
|
||
import ( | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/babylonlabs-io/babylon/app" | ||
"github.com/babylonlabs-io/babylon/testutil/datagen" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func FuzzCanonicalChainIndexer(f *testing.F) { | ||
datagen.AddRandomSeedsToFuzzer(f, 10) | ||
|
||
f.Fuzz(func(t *testing.T, seed int64) { | ||
r := rand.New(rand.NewSource(seed)) | ||
|
||
babylonApp := app.Setup(t, false) | ||
zcKeeper := babylonApp.ZoneConciergeKeeper | ||
ctx := babylonApp.NewContext(false) | ||
czConsumerId := "test-consumerid" | ||
|
||
// simulate a random number of blocks | ||
numHeaders := datagen.RandomInt(r, 100) + 1 | ||
headers := SimulateNewHeaders(ctx, r, &zcKeeper, czConsumerId, 0, numHeaders) | ||
|
||
// check if the canonical chain index is correct or not | ||
for i := uint64(0); i < numHeaders; i++ { | ||
header, err := zcKeeper.GetHeader(ctx, czConsumerId, i) | ||
require.NoError(t, err) | ||
require.NotNil(t, header) | ||
require.Equal(t, czConsumerId, header.ConsumerId) | ||
require.Equal(t, i, header.Height) | ||
require.Equal(t, headers[i].Header.AppHash, header.Hash) | ||
} | ||
|
||
// check if the chain info is updated or not | ||
chainInfo, err := zcKeeper.GetChainInfo(ctx, czConsumerId) | ||
require.NoError(t, err) | ||
require.NotNil(t, chainInfo.LatestHeader) | ||
require.Equal(t, czConsumerId, chainInfo.LatestHeader.ConsumerId) | ||
require.Equal(t, numHeaders-1, chainInfo.LatestHeader.Height) | ||
require.Equal(t, headers[numHeaders-1].Header.AppHash, chainInfo.LatestHeader.Hash) | ||
}) | ||
} | ||
|
||
func FuzzFindClosestHeader(f *testing.F) { | ||
datagen.AddRandomSeedsToFuzzer(f, 10) | ||
|
||
f.Fuzz(func(t *testing.T, seed int64) { | ||
r := rand.New(rand.NewSource(seed)) | ||
|
||
babylonApp := app.Setup(t, false) | ||
zcKeeper := babylonApp.ZoneConciergeKeeper | ||
ctx := babylonApp.NewContext(false) | ||
czConsumerId := "test-consumerid" | ||
|
||
// no header at the moment, FindClosestHeader invocation should give error | ||
_, err := zcKeeper.FindClosestHeader(ctx, czConsumerId, 100) | ||
require.Error(t, err) | ||
|
||
// simulate a random number of blocks | ||
numHeaders := datagen.RandomInt(r, 100) + 1 | ||
headers := SimulateNewHeaders(ctx, r, &zcKeeper, czConsumerId, 0, numHeaders) | ||
|
||
header, err := zcKeeper.FindClosestHeader(ctx, czConsumerId, numHeaders) | ||
require.NoError(t, err) | ||
require.Equal(t, headers[len(headers)-1].Header.AppHash, header.Hash) | ||
|
||
// skip a non-zero number of headers in between, in order to create a gap of non-timestamped headers | ||
gap := datagen.RandomInt(r, 10) + 1 | ||
|
||
// simulate a random number of blocks | ||
// where the new batch of headers has a gap with the previous batch | ||
SimulateNewHeaders(ctx, r, &zcKeeper, czConsumerId, numHeaders+gap+1, numHeaders) | ||
|
||
// get a random height that is in this gap | ||
randomHeightInGap := datagen.RandomInt(r, int(gap+1)) + numHeaders | ||
// find the closest header with the given randomHeightInGap | ||
header, err = zcKeeper.FindClosestHeader(ctx, czConsumerId, randomHeightInGap) | ||
require.NoError(t, err) | ||
// the header should be the same as the last header in the last batch | ||
require.Equal(t, headers[len(headers)-1].Header.AppHash, header.Hash) | ||
}) | ||
} |
Oops, something went wrong.