Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(da): SubmitBatchV2 method for interchain DA #929

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ da_config = "{{ .DAConfig }}"
# da_config = "{\"base_url\":\"http:\/\/127.0.0.1:26658\",\"timeout\":30000000000,\"gas_prices\":0.1,\"auth_token\":\"TOKEN\",\"backoff\":{\"initial_delay\":6000000000,\"max_delay\":6000000000,\"growth_factor\":2},\"retry_attempts\":4,\"retry_delay\":3000000000}"
# Avail config example:
# da_config = "{\"seed\": \"MNEMONIC\", \"api_url\": \"wss://kate.avail.tools/ws\", \"app_id\": 0, \"tip\":10}"
# Interchain DA config example:
# da_config = "{\"client_id\":\"dym-interchain\",\"chain_id\":\"interchain-da-test\",\"keyring_backend\":\"test\",\"keyring_home_dir\":\"/Users/keruch/.simapp\",\"address_prefix\":\"cosmos\",\"account_name\":\"sequencer\",\"node_address\":\"http://127.0.0.1:36657\",\"gas_prices\":\"10stake\",\"da_params\":{\"cost_per_byte\":{\"amount\":\"0\"}},\"retry_min_delay\":100000000,\"retry_max_delay\":2000000000,\"retry_attempts\":10}"


### p2p config ###
Expand Down
68 changes: 63 additions & 5 deletions da/da.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (

"github.com/celestiaorg/celestia-openrpc/types/blob"
"github.com/cometbft/cometbft/crypto/merkle"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/tendermint/tendermint/libs/pubsub"

"github.com/dymensionxyz/dymint/store"
"github.com/dymensionxyz/dymint/types"
"github.com/tendermint/tendermint/libs/pubsub"
)

// StatusCode is a type for DA layer return status.
Expand All @@ -37,10 +39,11 @@ type Client string

// Data availability clients
const (
Mock Client = "mock"
Grpc Client = "grpc"
Celestia Client = "celestia"
Avail Client = "avail"
Mock Client = "mock"
Grpc Client = "grpc"
Celestia Client = "celestia"
Avail Client = "avail"
Interchain Client = "interchain"
)

// Option is a function that sets a parameter on the da layer.
Expand Down Expand Up @@ -177,6 +180,13 @@ type ResultSubmitBatch struct {
SubmitMetaData *DASubmitMetaData
}

// ResultSubmitBatchV2 contains information returned from DA layer after block submission.
type ResultSubmitBatchV2 struct {
BaseResult
// DAPath instructs how to retrieve the submitted batch from the DA layer.
DAPath Path
}

// ResultCheckBatch contains information about block availability, returned from DA layer client.
type ResultCheckBatch struct {
BaseResult
Expand All @@ -194,6 +204,22 @@ type ResultRetrieveBatch struct {
CheckMetaData *DACheckMetaData
}

// ResultRetrieveBatchV2 contains a batch of blocks returned from the DA layer client.
type ResultRetrieveBatchV2 struct {
BaseResult
// Batches is the full block retrieved from the DA layer.
// If BaseResult.Code is not StatusSuccess, this field is nil.
Batches []*types.Batch
}

// Path TODO: move to the Dymension proto file
type Path struct {
// DAType identifies the DA type being used by the sequencer to post the blob.
DaType string
// Commitment is a generic commitment interpreted by the DA Layer.
Commitment *cdctypes.Any
}

// DataAvailabilityLayerClient defines generic interface for DA layer block submission.
// It also contains life-cycle methods.
type DataAvailabilityLayerClient interface {
Expand All @@ -219,6 +245,28 @@ type DataAvailabilityLayerClient interface {
Synced() <-chan struct{}
}

// ClientV2 defines generic interface for DA layer block submission.
type ClientV2 interface {
// DataAvailabilityLayerClient closure for backward compatibility
DataAvailabilityLayerClient

// Init is called once to allow DA client to read configuration and initialize resources.
Init([]byte, *pubsub.Server, store.KV, types.Logger, ...Option) error

// Start is called once, after Init. It starts the operation of ClientV2.
Start() error

// Stop is called once, when DAClientV2 is no longer needed.
Stop() error

// SubmitBatchV2 submits the passed in block to the DA layer.
SubmitBatchV2(*types.Batch) ResultSubmitBatchV2

GetClientType() Client

Synced() <-chan struct{}
}

// BatchRetriever is additional interface that can be implemented by Data Availability Layer Client that is able to retrieve
// block data from DA layer. This gives the ability to use it for block synchronization.
type BatchRetriever interface {
Expand All @@ -227,3 +275,13 @@ type BatchRetriever interface {
// CheckBatchAvailability checks the availability of the blob received getting proofs and validating them
CheckBatchAvailability(daMetaData *DASubmitMetaData) ResultCheckBatch
}

// BatchRetrieverV2 is an additional interface implemented by the DA layer client. It allows to retrieve
// block data from the DA layer and use this interface for block synchronization.
type BatchRetrieverV2 interface {
// BatchRetriever closure for backward compatibility
BatchRetriever

// RetrieveBatchesV2 returns blocks by the given da.Path.
RetrieveBatchesV2(ResultSubmitBatchV2) ResultRetrieveBatchV2
}
69 changes: 69 additions & 0 deletions da/interchain/chain_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package interchain

import (
"context"
"fmt"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/dymensionxyz/cosmosclient/cosmosclient"
"github.com/ignite/cli/ignite/pkg/cosmosaccount"
"github.com/tendermint/tendermint/libs/bytes"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"

interchainda "github.com/dymensionxyz/dymint/types/pb/interchain_da"
)

type daClient struct {
cosmosclient.Client
queryClient interchainda.QueryClient
txService tx.ServiceClient
}

func newDAClient(ctx context.Context, config DAConfig) (*daClient, error) {
c, err := cosmosclient.New(ctx, []cosmosclient.Option{
cosmosclient.WithAddressPrefix(config.AddressPrefix),
cosmosclient.WithBroadcastMode(flags.BroadcastSync),
cosmosclient.WithNodeAddress(config.NodeAddress),
cosmosclient.WithFees(config.GasFees),
cosmosclient.WithGasLimit(config.GasLimit),
cosmosclient.WithGasPrices(config.GasPrices),
cosmosclient.WithGasAdjustment(config.GasAdjustment),
cosmosclient.WithKeyringBackend(cosmosaccount.KeyringBackend(config.KeyringBackend)),
cosmosclient.WithHome(config.KeyringHomeDir),
}...)
if err != nil {
return nil, fmt.Errorf("can't create DA layer cosmos client: %w", err)
}
return &daClient{
Client: c,
queryClient: interchainda.NewQueryClient(c.Context()),
txService: tx.NewServiceClient(c.Context()),
}, nil
}

func (c *daClient) Params(ctx context.Context) (interchainda.Params, error) {
resp, err := c.queryClient.Params(ctx, &interchainda.QueryParamsRequest{})
Fixed Show fixed Hide fixed
if err != nil {
return interchainda.Params{}, fmt.Errorf("can't query DA layer params: %w", err)
}
return resp.GetParams(), nil
}

func (c *daClient) GetTx(ctx context.Context, txHash string) (*tx.GetTxResponse, error) {
return c.txService.GetTx(ctx, &tx.GetTxRequest{Hash: txHash})
}

func (c *daClient) ABCIQueryWithProof(
ctx context.Context,
path string,
data bytes.HexBytes,
height int64,
) (*ctypes.ResultABCIQuery, error) {
opts := rpcclient.ABCIQueryOptions{
Height: height,
Prove: true,
}
return c.RPC.ABCIQueryWithOptions(ctx, path, data, opts)
}
116 changes: 116 additions & 0 deletions da/interchain/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package interchain

import (
"errors"
"os"
"path/filepath"
"time"

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

interchainda "github.com/dymensionxyz/dymint/types/pb/interchain_da"
)

type DAConfig struct {
ClientID string `mapstructure:"client_id" json:"client_id,omitempty"` // IBC client ID between the Hub and DA layer
ChainID string `mapstructure:"chain_id" json:"chain_id,omitempty"` // Chain ID of the DA layer

KeyringBackend string `mapstructure:"keyring_backend" json:"keyring_backend,omitempty"`
KeyringHomeDir string `mapstructure:"keyring_home_dir" json:"keyring_home_dir,omitempty"`
AddressPrefix string `mapstructure:"address_prefix" json:"address_prefix,omitempty"`
AccountName string `mapstructure:"account_name" json:"account_name,omitempty"`
NodeAddress string `mapstructure:"node_address" json:"node_address,omitempty"`
GasLimit uint64 `mapstructure:"gas_limit" json:"gas_limit,omitempty"`
GasPrices string `mapstructure:"gas_prices" json:"gas_prices,omitempty"`
GasAdjustment float64 `mapstructure:"gas_adjustment" json:"gas_adjustment,omitempty"`
GasFees string `mapstructure:"gas_fees" json:"gas_fees,omitempty"`
DAParams interchainda.Params `mapstructure:"da_params" json:"da_params"`

BatchAcceptanceTimeout time.Duration `mapstructure:"batch_acceptance_timeout" json:"batch_acceptance_timeout"`
BatchAcceptanceAttempts uint `mapstructure:"batch_acceptance_attempts" json:"batch_acceptance_attempts"`

RetryMinDelay time.Duration `mapstructure:"retry_min_delay" json:"retry_min_delay,omitempty"`
RetryMaxDelay time.Duration `mapstructure:"retry_min_delay" json:"retry_max_delay,omitempty"`
RetryAttempts uint `mapstructure:"retry_attempts" json:"retry_attempts,omitempty"`
}

func (c *DAConfig) Verify() error {
if c.ClientID == "" {
return errors.New("missing IBC client ID")
}

if c.ChainID == "" {
return errors.New("missing chain ID of the DA layer")
}

if c.KeyringBackend == "" {
return errors.New("missing keyring backend")
}

if c.KeyringHomeDir == "" {
return errors.New("missing keyring home dir")
}

if c.AddressPrefix == "" {
return errors.New("missing address prefix")
}

if c.AccountName == "" {
return errors.New("missing account name")
}

if c.NodeAddress == "" {
return errors.New("missing node address")
}

// GasLimit may be 0

if c.GasPrices == "" && c.GasFees == "" {
return errors.New("either gas prices or gas_prices are required")
}

if c.GasPrices != "" && c.GasFees != "" {
return errors.New("cannot provide both fees and gas prices")
}

// DAParams are set during Init

if c.RetryMinDelay.Nanoseconds() == 0 {
return errors.New("missing retry min delay")
}

if c.RetryMaxDelay.Nanoseconds() == 0 {
return errors.New("missing retry max delay")
}

if c.RetryAttempts == 0 {
return errors.New("missing retry attempts")
}

return nil
}

func DefaultDAConfig() DAConfig {
home, _ := os.UserHomeDir()
keyringHomeDir := filepath.Join(home, ".simapp")
return DAConfig{
ClientID: "dym-interchain",
ChainID: "my-test-chain",
KeyringBackend: keyring.BackendTest,
KeyringHomeDir: keyringHomeDir,
AddressPrefix: sdk.Bech32MainPrefix,
Dismissed Show dismissed Hide dismissed
AccountName: "sequencer",
NodeAddress: "http://127.0.0.1:26657",
GasLimit: 0,
GasPrices: "10stake",
GasAdjustment: 1.1,
GasFees: "",
DAParams: interchainda.Params{},
BatchAcceptanceTimeout: 5 * time.Second,
BatchAcceptanceAttempts: 10,
RetryMinDelay: 100 * time.Millisecond,
RetryMaxDelay: 2 * time.Second,
RetryAttempts: 10,
}
}
Loading
Loading