diff --git a/config.go b/config.go index 118fbf5d..839b42f0 100644 --- a/config.go +++ b/config.go @@ -19,6 +19,7 @@ import ( "fmt" "io" "log/slog" + "time" "github.com/blinklabs-io/dingo/config/cardano" "github.com/blinklabs-io/dingo/connmanager" @@ -31,26 +32,27 @@ import ( type ListenerConfig = connmanager.ListenerConfig type Config struct { - badgerCacheSize int64 - mempoolCapacity int64 - cardanoNodeConfig *cardano.CardanoNodeConfig - dataDir string - intersectPoints []ocommon.Point - intersectTip bool - logger *slog.Logger - listeners []ListenerConfig - network string - networkMagic uint32 - outboundSourcePort uint - utxorpcPort uint - tlsCertFilePath string - tlsKeyFilePath string - peerSharing bool - promRegistry prometheus.Registerer - topologyConfig *topology.TopologyConfig - tracing bool - tracingStdout bool - devMode bool + badgerCacheSize int64 + mempoolCapacity int64 + cardanoNodeConfig *cardano.CardanoNodeConfig + dataDir string + intersectPoints []ocommon.Point + intersectTip bool + logger *slog.Logger + listeners []ListenerConfig + network string + networkMagic uint32 + outboundSourcePort uint + utxorpcPort uint + tlsCertFilePath string + tlsKeyFilePath string + peerSharing bool + promRegistry prometheus.Registerer + topologyConfig *topology.TopologyConfig + tracing bool + tracingStdout bool + devMode bool + ledgerValidateHistorical time.Duration } // configPopulateNetworkMagic uses the named network (if specified) to determine the network magic value (if not specified) @@ -268,3 +270,10 @@ func WithDevMode(devMode bool) ConfigOptionFunc { c.devMode = devMode } } + +// WithLedgerValidateHistorical specifies how far back in history to validate all transactions +func WithLedgerValidateHistorical(duration time.Duration) ConfigOptionFunc { + return func(c *Config) { + c.ledgerValidateHistorical = duration + } +} diff --git a/dingo.yaml.example b/dingo.yaml.example index 02228f1b..14e5ce3d 100644 --- a/dingo.yaml.example +++ b/dingo.yaml.example @@ -64,3 +64,7 @@ mempoolCapacity: 1048576 # Enable development mode which prevents outbound connections # Default: false devMode: false + +# How far back in history to validate all transactions +# Default: "14d" +ledgerValidateHistorical: "14d" diff --git a/internal/config/config.go b/internal/config/config.go index a7d82d07..f91a7f70 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -43,43 +43,45 @@ func FromContext(ctx context.Context) *Config { } type Config struct { - BadgerCacheSize int64 `split_words:"true" yaml:"badgerCacheSize"` - MempoolCapacity int64 `split_words:"true" yaml:"mempoolCapacity"` - BindAddr string `split_words:"true" yaml:"bindAddr"` - CardanoConfig string ` yaml:"cardanoConfig" envconfig:"config"` - DatabasePath string `split_words:"true" yaml:"databasePath"` - SocketPath string `split_words:"true" yaml:"socketPath"` - Network string ` yaml:"network"` - TlsCertFilePath string ` yaml:"tlsCertFilePath" envconfig:"TLS_CERT_FILE_PATH"` - TlsKeyFilePath string ` yaml:"tlsKeyFilePath" envconfig:"TLS_KEY_FILE_PATH"` - Topology string ` yaml:"topology"` - MetricsPort uint `split_words:"true" yaml:"metricsPort"` - PrivateBindAddr string `split_words:"true" yaml:"privateBindAddr"` - PrivatePort uint `split_words:"true" yaml:"privatePort"` - RelayPort uint ` yaml:"relayPort" envconfig:"port"` - UtxorpcPort uint `split_words:"true" yaml:"utxorpcPort"` - IntersectTip bool `split_words:"true" yaml:"intersectTip"` - DevMode bool `split_words:"true" yaml:"devMode"` + BadgerCacheSize int64 `split_words:"true" yaml:"badgerCacheSize"` + MempoolCapacity int64 `split_words:"true" yaml:"mempoolCapacity"` + BindAddr string `split_words:"true" yaml:"bindAddr"` + CardanoConfig string ` yaml:"cardanoConfig" envconfig:"config"` + DatabasePath string `split_words:"true" yaml:"databasePath"` + SocketPath string `split_words:"true" yaml:"socketPath"` + Network string ` yaml:"network"` + TlsCertFilePath string ` yaml:"tlsCertFilePath" envconfig:"TLS_CERT_FILE_PATH"` + TlsKeyFilePath string ` yaml:"tlsKeyFilePath" envconfig:"TLS_KEY_FILE_PATH"` + Topology string ` yaml:"topology"` + MetricsPort uint `split_words:"true" yaml:"metricsPort"` + PrivateBindAddr string `split_words:"true" yaml:"privateBindAddr"` + PrivatePort uint `split_words:"true" yaml:"privatePort"` + RelayPort uint ` yaml:"relayPort" envconfig:"port"` + UtxorpcPort uint `split_words:"true" yaml:"utxorpcPort"` + IntersectTip bool `split_words:"true" yaml:"intersectTip"` + DevMode bool `split_words:"true" yaml:"devMode"` + LedgerValidateHistorical string `split_words:"true" yaml:"ledgerValidateHistorical"` } var globalConfig = &Config{ - BadgerCacheSize: 1073741824, - MempoolCapacity: 1048576, - BindAddr: "0.0.0.0", - CardanoConfig: "./config/cardano/preview/config.json", - DatabasePath: ".dingo", - SocketPath: "dingo.socket", - IntersectTip: false, - Network: "preview", - MetricsPort: 12798, - PrivateBindAddr: "127.0.0.1", - PrivatePort: 3002, - RelayPort: 3001, - UtxorpcPort: 9090, - Topology: "", - TlsCertFilePath: "", - TlsKeyFilePath: "", - DevMode: false, + BadgerCacheSize: 1073741824, + MempoolCapacity: 1048576, + BindAddr: "0.0.0.0", + CardanoConfig: "./config/cardano/preview/config.json", + DatabasePath: ".dingo", + SocketPath: "dingo.socket", + IntersectTip: false, + Network: "preview", + MetricsPort: 12798, + PrivateBindAddr: "127.0.0.1", + PrivatePort: 3002, + RelayPort: 3001, + UtxorpcPort: 9090, + Topology: "", + TlsCertFilePath: "", + TlsKeyFilePath: "", + DevMode: false, + LedgerValidateHistorical: "14d", } func LoadConfig(configFile string) (*Config, error) { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a18ad525..7f396eba 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -9,23 +9,24 @@ import ( func resetGlobalConfig() { globalConfig = &Config{ - BadgerCacheSize: 1073741824, - MempoolCapacity: 1048576, - BindAddr: "0.0.0.0", - CardanoConfig: "./config/cardano/preview/config.json", - DatabasePath: ".dingo", - SocketPath: "dingo.socket", - IntersectTip: false, - Network: "preview", - MetricsPort: 12798, - PrivateBindAddr: "127.0.0.1", - PrivatePort: 3002, - RelayPort: 3001, - UtxorpcPort: 9090, - Topology: "", - TlsCertFilePath: "", - TlsKeyFilePath: "", - DevMode: false, + BadgerCacheSize: 1073741824, + MempoolCapacity: 1048576, + BindAddr: "0.0.0.0", + CardanoConfig: "./config/cardano/preview/config.json", + DatabasePath: ".dingo", + SocketPath: "dingo.socket", + IntersectTip: false, + Network: "preview", + MetricsPort: 12798, + PrivateBindAddr: "127.0.0.1", + PrivatePort: 3002, + RelayPort: 3001, + UtxorpcPort: 9090, + Topology: "", + TlsCertFilePath: "", + TlsKeyFilePath: "", + DevMode: false, + LedgerValidateHistorical: "14d", } } @@ -60,23 +61,24 @@ tlsKeyFilePath: "key1.pem" defer os.Remove(tmpFile) expected := &Config{ - BadgerCacheSize: 8388608, - MempoolCapacity: 2097152, - BindAddr: "127.0.0.1", - CardanoConfig: "./cardano/preview/config.json", - DatabasePath: ".dingo", - SocketPath: "env.socket", - IntersectTip: true, - Network: "preview", - MetricsPort: 8088, - PrivateBindAddr: "127.0.0.1", - PrivatePort: 8000, - RelayPort: 4000, - UtxorpcPort: 9940, - Topology: "", - TlsCertFilePath: "cert1.pem", - TlsKeyFilePath: "key1.pem", - DevMode: false, + BadgerCacheSize: 8388608, + MempoolCapacity: 2097152, + BindAddr: "127.0.0.1", + CardanoConfig: "./cardano/preview/config.json", + DatabasePath: ".dingo", + SocketPath: "env.socket", + IntersectTip: true, + Network: "preview", + MetricsPort: 8088, + PrivateBindAddr: "127.0.0.1", + PrivatePort: 8000, + RelayPort: 4000, + UtxorpcPort: 9940, + Topology: "", + TlsCertFilePath: "cert1.pem", + TlsKeyFilePath: "key1.pem", + DevMode: false, + LedgerValidateHistorical: "14d", } actual, err := LoadConfig(tmpFile) @@ -103,23 +105,24 @@ func TestLoad_WithoutConfigFile_UsesDefaults(t *testing.T) { // Expected is the original default values from globalConfig expected := &Config{ - BadgerCacheSize: 1073741824, - MempoolCapacity: 1048576, - BindAddr: "0.0.0.0", - CardanoConfig: "./config/cardano/preview/config.json", - DatabasePath: ".dingo", - SocketPath: "dingo.socket", - IntersectTip: false, - Network: "preview", - MetricsPort: 12798, - PrivateBindAddr: "127.0.0.1", - PrivatePort: 3002, - RelayPort: 3001, - UtxorpcPort: 9090, - Topology: "", - TlsCertFilePath: "", - TlsKeyFilePath: "", - DevMode: false, + BadgerCacheSize: 1073741824, + MempoolCapacity: 1048576, + BindAddr: "0.0.0.0", + CardanoConfig: "./config/cardano/preview/config.json", + DatabasePath: ".dingo", + SocketPath: "dingo.socket", + IntersectTip: false, + Network: "preview", + MetricsPort: 12798, + PrivateBindAddr: "127.0.0.1", + PrivatePort: 3002, + RelayPort: 3001, + UtxorpcPort: 9090, + Topology: "", + TlsCertFilePath: "", + TlsKeyFilePath: "", + DevMode: false, + LedgerValidateHistorical: "14d", } if !reflect.DeepEqual(cfg, expected) { @@ -157,3 +160,30 @@ network: "preview" t.Errorf("expected DevMode to be true, got: %v", cfg.DevMode) } } + +func TestLoad_WithLedgerValidateHistorical(t *testing.T) { + resetGlobalConfig() + + // Test with ledger validate historical in config file + yamlContent := ` +ledgerValidateHistorical: "7d" +` + + tmpDir := t.TempDir() + tmpFile := filepath.Join(tmpDir, "test-ledger-validate-historical.yaml") + + err := os.WriteFile(tmpFile, []byte(yamlContent), 0644) + if err != nil { + t.Fatalf("failed to write config file: %v", err) + } + + cfg, err := LoadConfig(tmpFile) + if err != nil { + t.Fatalf("expected no error, got: %v", err) + } + + expected := "7d" + if cfg.LedgerValidateHistorical != expected { + t.Errorf("expected LedgerValidateHistorical to be %s, got: %s", expected, cfg.LedgerValidateHistorical) + } +} diff --git a/internal/node/node.go b/internal/node/node.go index 033083db..535d5048 100644 --- a/internal/node/node.go +++ b/internal/node/node.go @@ -57,6 +57,10 @@ func Run(cfg *config.Config, logger *slog.Logger) error { "component", "node", ) } + ledgerValidateHistorical, err := time.ParseDuration(cfg.LedgerValidateHistorical) + if err != nil { + return err + } listeners := []dingo.ListenerConfig{} if cfg.RelayPort > 0 { // Public "relay" port (node-to-node) @@ -114,6 +118,7 @@ func Run(cfg *config.Config, logger *slog.Logger) error { dingo.WithUtxorpcTlsCertFilePath(cfg.TlsCertFilePath), dingo.WithUtxorpcTlsKeyFilePath(cfg.TlsKeyFilePath), dingo.WithDevMode(cfg.DevMode), + dingo.WithLedgerValidateHistorical(ledgerValidateHistorical), // Enable metrics with default prometheus registry dingo.WithPrometheusRegistry(prometheus.DefaultRegisterer), // TODO: make this configurable (#387) diff --git a/ledger/state.go b/ledger/state.go index 7e5f9b1d..3406e19c 100644 --- a/ledger/state.go +++ b/ledger/state.go @@ -44,8 +44,6 @@ import ( const ( cleanupConsumedUtxosInterval = 5 * time.Minute cleanupConsumedUtxosSlotWindow = 50000 // TODO: calculate this from params (#395) - - validateHistoricalThreshold = 14 * (24 * time.Hour) // 2 weeks ) type ChainsyncState string @@ -57,14 +55,15 @@ const ( ) type LedgerStateConfig struct { - Logger *slog.Logger - Database *database.Database - ChainManager *chain.ChainManager - EventBus *event.EventBus - CardanoNodeConfig *cardano.CardanoNodeConfig - PromRegistry prometheus.Registerer - ValidateHistorical bool - ForgeBlocks bool + Logger *slog.Logger + Database *database.Database + ChainManager *chain.ChainManager + EventBus *event.EventBus + CardanoNodeConfig *cardano.CardanoNodeConfig + PromRegistry prometheus.Registerer + ValidateHistorical bool + ForgeBlocks bool + ValidateHistoricalPeriod time.Duration // Callback(s) BlockfetchRequestRangeFunc BlockfetchRequestRangeFunc } @@ -599,7 +598,7 @@ func (ls *LedgerState) ledgerProcessBlocks() { } // Check difference from current time timeDiff := time.Since(slotTime) - if timeDiff < validateHistoricalThreshold { + if timeDiff < ls.config.ValidateHistoricalPeriod { shouldValidate = true ls.config.Logger.Debug( "enabling validation as we approach tip", diff --git a/node.go b/node.go index 476abf06..e10e83c9 100644 --- a/node.go +++ b/node.go @@ -124,6 +124,7 @@ func (n *Node) Run() error { CardanoNodeConfig: n.config.cardanoNodeConfig, PromRegistry: n.config.promRegistry, ForgeBlocks: n.config.devMode, + ValidateHistoricalPeriod: n.config.ledgerValidateHistorical, BlockfetchRequestRangeFunc: n.blockfetchClientRequestRange, }, )