diff --git a/config.go b/config.go index 81607437..118fbf5d 100644 --- a/config.go +++ b/config.go @@ -50,6 +50,7 @@ type Config struct { topologyConfig *topology.TopologyConfig tracing bool tracingStdout bool + devMode bool } // configPopulateNetworkMagic uses the named network (if specified) to determine the network magic value (if not specified) @@ -260,3 +261,10 @@ func WithMempoolCapacity(capacity int64) ConfigOptionFunc { c.mempoolCapacity = capacity } } + +// WithDevMode enables development mode which prevents outbound connections. +func WithDevMode(devMode bool) ConfigOptionFunc { + return func(c *Config) { + c.devMode = devMode + } +} diff --git a/dingo.yaml.example b/dingo.yaml.example index fb3256be..02228f1b 100644 --- a/dingo.yaml.example +++ b/dingo.yaml.example @@ -60,3 +60,7 @@ badgerCacheSize: 1073741824 # Transactions exceeding this limit will be rejected. # Default: 1048576 (1 MB) mempoolCapacity: 1048576 + +# Enable development mode which prevents outbound connections +# Default: false +devMode: false diff --git a/internal/config/config.go b/internal/config/config.go index 6b7f41a5..97b8333b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -59,6 +59,7 @@ type Config struct { 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"` } var globalConfig = &Config{ @@ -78,6 +79,7 @@ var globalConfig = &Config{ Topology: "", TlsCertFilePath: "", TlsKeyFilePath: "", + DevMode: false, } func LoadConfig(configFile string) (*Config, error) { @@ -113,6 +115,7 @@ func LoadConfig(configFile string) (*Config, error) { if err != nil { return nil, fmt.Errorf("error processing environment: %+w", err) } + _, err = LoadTopologyConfig() if err != nil { return nil, fmt.Errorf("error loading topology: %+w", err) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index be0d8e21..a18ad525 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -2,6 +2,7 @@ package config import ( "os" + "path/filepath" "reflect" "testing" ) @@ -24,6 +25,7 @@ func resetGlobalConfig() { Topology: "", TlsCertFilePath: "", TlsKeyFilePath: "", + DevMode: false, } } @@ -48,7 +50,9 @@ tlsCertFilePath: "cert1.pem" tlsKeyFilePath: "key1.pem" ` - tmpFile := "test-dingo.yaml" + tmpDir := t.TempDir() + tmpFile := filepath.Join(tmpDir, "test-dingo.yaml") + err := os.WriteFile(tmpFile, []byte(yamlContent), 0644) if err != nil { t.Fatalf("failed to write config file: %v", err) @@ -72,6 +76,7 @@ tlsKeyFilePath: "key1.pem" Topology: "", TlsCertFilePath: "cert1.pem", TlsKeyFilePath: "key1.pem", + DevMode: false, } actual, err := LoadConfig(tmpFile) @@ -114,6 +119,7 @@ func TestLoad_WithoutConfigFile_UsesDefaults(t *testing.T) { Topology: "", TlsCertFilePath: "", TlsKeyFilePath: "", + DevMode: false, } if !reflect.DeepEqual(cfg, expected) { @@ -124,3 +130,30 @@ func TestLoad_WithoutConfigFile_UsesDefaults(t *testing.T) { ) } } + +func TestLoad_WithDevModeConfig(t *testing.T) { + resetGlobalConfig() + + // Test with dev mode in config file + yamlContent := ` +devMode: true +network: "preview" +` + + tmpDir := t.TempDir() + tmpFile := filepath.Join(tmpDir, "test-dev-mode.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) + } + + if !cfg.DevMode { + t.Errorf("expected DevMode to be true, got: %v", cfg.DevMode) + } +} diff --git a/internal/node/node.go b/internal/node/node.go index 08eb723d..033083db 100644 --- a/internal/node/node.go +++ b/internal/node/node.go @@ -113,6 +113,7 @@ func Run(cfg *config.Config, logger *slog.Logger) error { dingo.WithUtxorpcPort(cfg.UtxorpcPort), dingo.WithUtxorpcTlsCertFilePath(cfg.TlsCertFilePath), dingo.WithUtxorpcTlsKeyFilePath(cfg.TlsKeyFilePath), + dingo.WithDevMode(cfg.DevMode), // Enable metrics with default prometheus registry dingo.WithPrometheusRegistry(prometheus.DefaultRegisterer), // TODO: make this configurable (#387) diff --git a/node.go b/node.go index dd93170c..280bd9e2 100644 --- a/node.go +++ b/node.go @@ -168,9 +168,10 @@ func (n *Node) Run() error { // Configure peer governor n.peerGov = peergov.NewPeerGovernor( peergov.PeerGovernorConfig{ - Logger: n.config.logger, - EventBus: n.eventBus, - ConnManager: n.connManager, + Logger: n.config.logger, + EventBus: n.eventBus, + ConnManager: n.connManager, + DisableOutbound: n.config.devMode, }, ) n.eventBus.SubscribeFunc( diff --git a/peergov/peergov.go b/peergov/peergov.go index 06e9dac8..c7928555 100644 --- a/peergov/peergov.go +++ b/peergov/peergov.go @@ -42,9 +42,10 @@ type PeerGovernor struct { } type PeerGovernorConfig struct { - Logger *slog.Logger - EventBus *event.EventBus - ConnManager *connmanager.ConnectionManager + Logger *slog.Logger + EventBus *event.EventBus + ConnManager *connmanager.ConnectionManager + DisableOutbound bool } func NewPeerGovernor(cfg PeerGovernorConfig) *PeerGovernor { @@ -182,10 +183,20 @@ func (p *PeerGovernor) peerIndexByConnId(connId ouroboros.ConnectionId) int { } func (p *PeerGovernor) startOutboundConnections() { + // Skip outbound connections if disabled + if p.config.DisableOutbound { + p.config.Logger.Info( + "outbound connections disabled, skipping outbound connections", + "role", "client", + ) + return + } + p.config.Logger.Debug( "starting connections", "role", "client", ) + for _, tmpPeer := range p.peers { go p.createOutboundConnection(tmpPeer) }