Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions chainstate/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package chainstate

import (
"fmt"
"slices"
"strconv"
"time"

"github.com/Layr-Labs/eigenda/common/config"
)

var _ config.DocumentedConfig = (*RootIndexerConfig)(nil)

// RootIndexerConfig is the root configuration for the chainstate indexer.
// It separates public and secret configuration for safety.
type RootIndexerConfig struct {
Config *IndexerConfig
Secret *IndexerSecretConfig
}

var _ config.VerifiableConfig = (*IndexerConfig)(nil)

// IndexerConfig contains all public configuration for the chainstate indexer.
type IndexerConfig struct {
// EigenDADirectory contract address
EigenDADirectory string `docs:"required"`

// Starting block number for indexing. If 0, starts from contract deployment block.
StartBlockNumber uint64

// Number of blocks to process in each batch during indexing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this be the maximum number of blocks that can be in a batch during indexing? assuming once its synced to head it only asses the diff from head and the last block it went up-to in the last event loop iteration

BlockBatchSize uint64

// Interval between polling for new blocks on the chain.
PollInterval time.Duration

// Path to JSON file for persisting indexed state to disk.
PersistencePath string `docs:"required"`

// Interval for persisting state snapshots to disk.
PersistInterval time.Duration
Comment on lines +37 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

knit:

Suggested change
// Path to JSON file for persisting indexed state to disk.
PersistencePath string `docs:"required"`
// Interval for persisting state snapshots to disk.
PersistInterval time.Duration
// Path to JSON file for persisting indexed state to disk.
SnapshotPath string `docs:"required"`
// Interval for persisting state snapshots to disk.
SnapshotInterval time.Duration


// Port for the HTTP API server that serves indexed data queries.
HTTPPort string `docs:"required"`

// The lowest log level that will be output. Accepted options are "debug", "info", "warn", "error"
LogLevel string

// The format of the log file. Accepted options are 'json' and 'text'
LogFormat string
}

var _ config.VerifiableConfig = (*IndexerSecretConfig)(nil)

// IndexerSecretConfig contains sensitive configuration values.
type IndexerSecretConfig struct {
// Ethereum RPC endpoint URLs for connecting to the blockchain.
EthRpcUrls []string `docs:"required"`
}

// DefaultIndexerConfig returns a default configuration with sensible values.
func DefaultIndexerConfig() *IndexerConfig {
return &IndexerConfig{
StartBlockNumber: 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any reason we'd default 0 vs starting at now - blob_ttl? in practice having to update a StartBlockNumber every-time we do a fresh deployment could become tedious

BlockBatchSize: 1000,
PollInterval: 12 * time.Second,
PersistInterval: 30 * time.Second,
HTTPPort: "8080",
LogLevel: "info",
LogFormat: "json",
}
}

// DefaultRootIndexerConfig returns a default root configuration.
func DefaultRootIndexerConfig() *RootIndexerConfig {
return &RootIndexerConfig{
Config: DefaultIndexerConfig(),
Secret: &IndexerSecretConfig{},
}
}

// GetName returns the name of this service for documentation.
func (c *IndexerConfig) GetName() string {
return "ChainStateIndexer"
}

// GetEnvVarPrefix returns the environment variable prefix for this service.
func (c *IndexerConfig) GetEnvVarPrefix() string {
return "CHAINSTATE_INDEXER"
}

// GetPackagePaths returns the package paths to scan for documentation.
func (c *IndexerConfig) GetPackagePaths() []string {
return []string{
"github.com/Layr-Labs/eigenda/chainstate",
"github.com/Layr-Labs/eigenda/common/config",
}
}

// Verify validates the configuration and returns an error if invalid.
func (c *IndexerConfig) Verify() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add a check for this Verify function for brevity?

if c.EigenDADirectory == "" {
return fmt.Errorf("EigenDA directory address is required")
}
if c.PersistencePath == "" {
return fmt.Errorf("persistence path is required")
}
if c.HTTPPort == "" {
return fmt.Errorf("HTTP port is required")
}
port, err := strconv.Atoi(c.HTTPPort)
if err != nil {
return fmt.Errorf("HTTP port must be a valid integer: %w", err)
}
if port < 1 || port > 65535 {
return fmt.Errorf("HTTP port must be between 1 and 65535, got %d", port)
}
if c.BlockBatchSize <= 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's some relationship here between BlockBatchSize and PollInterval where they can be mis-expressed in-ways that affects the reliability of core service logic

return fmt.Errorf("block batch size must be greater than 0")
}
if c.PollInterval <= 0 {
return fmt.Errorf("poll interval must be greater than 0")
}
if c.PersistInterval <= 0 {
return fmt.Errorf("persist interval must be greater than 0")
}
if c.LogLevel != "debug" && c.LogLevel != "info" && c.LogLevel != "warn" && c.LogLevel != "error" {
return fmt.Errorf("invalid log level %q, accepted values: debug, info, warn, error", c.LogLevel)
}
if c.LogFormat != "json" && c.LogFormat != "text" {
return fmt.Errorf("invalid log format %q, accepted values: json, text", c.LogFormat)
}
return nil
}

// Verify validates the secret configuration.
func (c *IndexerSecretConfig) Verify() error {
if len(c.EthRpcUrls) == 0 {
return fmt.Errorf("at least one Ethereum RPC URL is required")
}
if slices.Contains(c.EthRpcUrls, "") {
return fmt.Errorf("Ethereum RPC URL can not be an empty string")
}
return nil
}

// GetName returns the name of this service for documentation.
func (c *RootIndexerConfig) GetName() string {
return c.Config.GetName()
}

// GetEnvVarPrefix returns the environment variable prefix for this service.
func (c *RootIndexerConfig) GetEnvVarPrefix() string {
return c.Config.GetEnvVarPrefix()
}

// GetPackagePaths returns the package paths to scan for documentation.
func (c *RootIndexerConfig) GetPackagePaths() []string {
return c.Config.GetPackagePaths()
}

// Verify validates the root configuration.
func (c *RootIndexerConfig) Verify() error {
if c.Config == nil {
return fmt.Errorf("config is required")
}
if c.Secret == nil {
return fmt.Errorf("secret config is required")
}
if err := c.Config.Verify(); err != nil {
return fmt.Errorf("config validation failed: %w", err)
}
if err := c.Secret.Verify(); err != nil {
return fmt.Errorf("secret config validation failed: %w", err)
}
return nil
}
4 changes: 4 additions & 0 deletions common/config/doc_generator/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"github.com/Layr-Labs/eigenda/chainstate"
"github.com/Layr-Labs/eigenda/common/config"
"github.com/Layr-Labs/eigenda/common/enforce"
"github.com/Layr-Labs/eigenda/disperser/controller"
Expand All @@ -20,4 +21,7 @@ func main() {

err = config.DocumentConfig(controller.DefaultControllerConfig, configDocsDir, true)
enforce.NilError(err, "failed to generate docs for the disperser controller config")

err = config.DocumentConfig(chainstate.DefaultRootIndexerConfig, configDocsDir, true)
enforce.NilError(err, "failed to generate docs for the chainstate indexer config")
}
24 changes: 24 additions & 0 deletions docs/config/ChainStateIndexer.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading