Skip to content

Commit

Permalink
feat: batching module
Browse files Browse the repository at this point in the history
  • Loading branch information
hacheigriega committed Sep 4, 2024
1 parent 3811dff commit 76468fc
Show file tree
Hide file tree
Showing 19 changed files with 3,640 additions and 0 deletions.
60 changes: 60 additions & 0 deletions proto/sedachain/batching/v1/batching.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
syntax = "proto3";
package sedachain.batching.v1;

import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
import "cosmos_proto/cosmos.proto";

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

// Batch is an aggregation of data request results along with validator
// signatures used to prove these results on destination chains.
message Batch {
// batch_number is a unique identifier of the batch incremented
// every time a batch is created.
uint64 batch_number = 1;
// block_height is the height at which the batch was created.
int64 block_height = 2;
// data_result_root is the root of the data result merkle tree.
string data_result_root = 3;
// validator_root is the root of the validator merkle tree.
string validator_root = 4;
// votes is the canonical set of votes on the batch from validators.
repeated Vote votes = 5;
// block_time is the time at which the batch was created.
google.protobuf.Timestamp block_time = 6
[ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ];
}

// Vote contains basic validator data and its batch signatures under various
// cryptographic schemes.
message Vote {
string validator_addr = 1 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
int64 voting_power = 2;
repeated Signature signatures = 3;
}

// Signature is a batch signature under a given scheme.
message Signature {
option (gogoproto.equal) = true;

// scheme is the signature scheme that corresponds to index in x/pubkey.
uint32 scheme = 1;
// signature is the signature of the batch.
string signature = 2;
// public_key is the public key corresponding to the signature that
// should have been registered in x/pubkey
string public_key = 3;
// merkle_proof is the proof of inclusion of the signer in the validator
// tree.
string merkle_proof = 4;
}

// Module parameters which can be changed through governance.
message Params {
option (gogoproto.equal) = true;

// validator_set_trim_percentage is the percentage of the validator
// set to store in the validator merkle tree in the batch.
uint32 validator_set_trim_percentage = 1;
}
18 changes: 18 additions & 0 deletions proto/sedachain/batching/v1/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
syntax = "proto3";
package sedachain.batching.v1;

import "gogoproto/gogo.proto";
import "sedachain/batching/v1/batching.proto";

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

// GenesisState defines the batching module's genesis state.
message GenesisState {
// current_batch_number is the batch number of the most recently-
// created batch.
uint64 current_batch_number = 1;
repeated Batch batches = 2
[ (gogoproto.nullable) = false ];
Params params = 3
[ (gogoproto.nullable) = false ];
}
35 changes: 35 additions & 0 deletions proto/sedachain/batching/v1/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
syntax = "proto3";
package sedachain.batching.v1;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "sedachain/batching/v1/batching.proto";

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

// Query defines the gRPC querier service.
service Query {
rpc Batch(QueryBatchRequest)
returns (QueryBatchResponse) {
option (google.api.http).get =
"/seda-chain/batching/batch/{batch_number}";
}

rpc Batches(QueryBatchesRequest)
returns (QueryBatchesResponse) {
option (google.api.http).get =
"/seda-chain/batching/batches";
}
}

// The request message for QueryBatch RPC.
message QueryBatchRequest { uint64 batch_number = 1; }

// The response message for QueryBatch RPC.
message QueryBatchResponse { Batch batch = 1 [(gogoproto.nullable) = false]; }

// The request message for QueryBatches RPC.
message QueryBatchesRequest {}

// The response message for QueryBatches RPC.
message QueryBatchesResponse { repeated Batch batches = 1 [(gogoproto.nullable) = false]; }
87 changes: 87 additions & 0 deletions x/batching/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package cli

import (
"fmt"
"strconv"

"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"

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

// GetQueryCmd returns the CLI query commands for batching module.
func GetQueryCmd(_ string) *cobra.Command {
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(
GetCmdQueryBatch(),
GetCmdQueryBatches(),
)
return cmd
}

// GetCmdQueryBatch returns the command for querying a batch.
func GetCmdQueryBatch() *cobra.Command {
cmd := &cobra.Command{
Use: "batch <batch_number>",
Short: "Get a batch given its batch number",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

batchNum, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
req := &types.QueryBatchRequest{
BatchNumber: batchNum,
}
res, err := queryClient.Batch(cmd.Context(), req)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}

// GetCmdQueryBatches returns the command for querying all batches.
func GetCmdQueryBatches() *cobra.Command {
cmd := &cobra.Command{
Use: "batches",
Short: "List all batches in the store",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.Batches(cmd.Context(), &types.QueryBatchesRequest{})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}
24 changes: 24 additions & 0 deletions x/batching/keeper/abci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

func (k Keeper) EndBlock(ctx sdk.Context) (err error) {
// Use defer to prevent returning an error, which would cause
// the chain to halt.
defer func() {
// Handle a panic.
if r := recover(); r != nil {
k.Logger(ctx).Error("recovered from panic in batching EndBlock", "err", r)
}
// Handle an error.
if err != nil {
k.Logger(ctx).Error("error in batching EndBlock", "err", err)
}
err = nil
}()

// TODO Construct batches and persist
return
}
39 changes: 39 additions & 0 deletions x/batching/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package keeper

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

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

// InitGenesis puts all data from genesis state into store.
func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) {
if err := k.SetCurrentBatchNum(ctx, data.CurrentBatchNumber); err != nil {
panic(err)
}
for _, batch := range data.Batches {
if err := k.SetBatch(ctx, batch.BatchNumber, batch); err != nil {
panic(err)
}
}
if err := k.SetParams(ctx, data.Params); err != nil {
panic(err)
}
}

// ExportGenesis extracts all data from store to genesis state.
func (k Keeper) ExportGenesis(ctx sdk.Context) types.GenesisState {
curBatchNum, err := k.GetCurrentBatchNum(ctx)
if err != nil {
panic(err)
}
batches, err := k.GetAllBatches(ctx)
if err != nil {
panic(err)
}
params, err := k.GetParams(ctx)
if err != nil {
panic(err)
}
return types.NewGenesisState(curBatchNum, batches, params)
}
115 changes: 115 additions & 0 deletions x/batching/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package keeper

import (
"context"
"fmt"

"cosmossdk.io/collections"
storetypes "cosmossdk.io/core/store"
"cosmossdk.io/log"

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

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

type Keeper struct {
stakingKeeper types.StakingKeeper

// authority is the address capable of executing MsgUpdateParams.
// Typically, this should be the gov module address.
authority string

Schema collections.Schema
currentBatchNumber collections.Sequence
batch collections.Map[uint64, types.Batch]
params collections.Item[types.Params]
}

func NewKeeper(cdc codec.BinaryCodec, storeService storetypes.KVStoreService, authority string, sk types.StakingKeeper) *Keeper {
sb := collections.NewSchemaBuilder(storeService)

k := Keeper{
authority: authority,
stakingKeeper: sk,
currentBatchNumber: collections.NewSequence(sb, types.CurrentBatchNumberKey, "current_batch_number"),
batch: collections.NewMap(sb, types.BatchPrefix, "batch", collections.Uint64Key, codec.CollValue[types.Batch](cdc)),
params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
}

schema, err := sb.Build()
if err != nil {
panic(err)
}
k.Schema = schema
return &k
}

func (k Keeper) SetCurrentBatchNum(ctx context.Context, batchNum uint64) error {
return k.currentBatchNumber.Set(ctx, batchNum)
}

func (k Keeper) IncrementCurrentBatchNum(ctx context.Context, batchNum uint64) error {

Check warning on line 53 in x/batching/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / golangci

unused-parameter: parameter 'batchNum' seems to be unused, consider removing or renaming it as _ (revive)
_, err := k.currentBatchNumber.Next(ctx)
return err
}

func (k Keeper) GetCurrentBatchNum(ctx context.Context) (uint64, error) {
batchNum, err := k.currentBatchNumber.Peek(ctx)
if err != nil {
return 0, err
}
return batchNum, nil
}

func (k Keeper) SetBatch(ctx context.Context, batchNum uint64, batch types.Batch) error {
return k.batch.Set(ctx, batchNum, batch)
}

func (k Keeper) GetBatch(ctx context.Context, batchNum uint64) (types.Batch, error) {
batch, err := k.batch.Get(ctx, batchNum)
if err != nil {
return types.Batch{}, err
}
return batch, nil
}

// IterateBatches iterates over the batches and performs a given
// callback function.
func (k Keeper) IterateBatches(ctx sdk.Context, callback func(types.Batch) (stop bool)) error {
iter, err := k.batch.Iterate(ctx, nil)
if err != nil {
return err
}
defer iter.Close()

for ; iter.Valid(); iter.Next() {
kv, err := iter.KeyValue()
if err != nil {
return err
}

if callback(kv.Value) {
break
}
}
return nil
}

// GetAllBatches returns all batches in the store.
func (k Keeper) GetAllBatches(ctx sdk.Context) ([]types.Batch, error) {
var batches []types.Batch
err := k.IterateBatches(ctx, func(batch types.Batch) bool {
batches = append(batches, batch)
return false
})
if err != nil {
return nil, err
}
return batches, nil
}

func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
Loading

0 comments on commit 76468fc

Please sign in to comment.