Skip to content

Commit 4de8f43

Browse files
committed
normalize DA endpoint at startup
1 parent febec86 commit 4de8f43

8 files changed

Lines changed: 43 additions & 13 deletions

File tree

apps/evm/cmd/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ var RunCmd = &cobra.Command{
6161

6262
var daClient block.FullDAClient
6363
if nodeConfig.DAEnabled() {
64-
blobClient, err := blobrpc.NewWSClient(cmd.Context(), logger, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
64+
blobClient, err := blobrpc.NewWSClient(cmd.Context(), logger, nodeConfig.GetDAAddress(), nodeConfig.DA.AuthToken, "")
6565
if err != nil {
6666
return fmt.Errorf("failed to create blob client: %w", err)
6767
}

apps/testapp/cmd/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func createSequencer(
125125
return solo.NewSoloSequencer(logger, []byte(genesis.ChainID), executor), nil
126126
}
127127

128-
blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
128+
blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.GetDAAddress(), nodeConfig.DA.AuthToken, "")
129129
if err != nil {
130130
return nil, fmt.Errorf("failed to create blob client: %w", err)
131131
}

docs/learn/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ da:
472472
### DA Service Address
473473

474474
**Description:**
475-
The network address (host:port) of the Data Availability layer service. A full follower with this value unset syncs exclusively through P2P and does not create a DA client. Aggregators must configure this value.
475+
The network address (host:port) of the Data Availability layer service. A full follower with this value unset syncs exclusively through P2P and does not create a DA client. Aggregators must configure this value before startup.
476476

477477
**YAML:**
478478

pkg/cmd/run_node.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ func ParseConfig(cmd *cobra.Command) (rollconf.Config, error) {
3838
if err := nodeConfig.Validate(); err != nil {
3939
return rollconf.Config{}, fmt.Errorf("failed to validate node config: %w", err)
4040
}
41+
if nodeConfig.Node.Aggregator && nodeConfig.GetDAAddress() == "" {
42+
return rollconf.Config{}, fmt.Errorf("DA address is required when aggregator mode is enabled")
43+
}
4144

4245
return nodeConfig, nil
4346
}
@@ -152,7 +155,7 @@ func StartNode(
152155

153156
var daClient block.FullDAClient
154157
if nodeConfig.DAEnabled() {
155-
blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
158+
blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.GetDAAddress(), nodeConfig.DA.AuthToken, "")
156159
if err != nil {
157160
return fmt.Errorf("failed to create blob client: %w", err)
158161
}

pkg/cmd/run_node_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,20 @@ func TestAggregatorFlagInvariants(t *testing.T) {
174174
}
175175
}
176176

177+
func TestParseConfig_AggregatorRequiresDAAddress(t *testing.T) {
178+
executor, sequencer, keyProvider, nodeKey, ds, stopDAHeightTicker := createTestComponents(context.Background(), t)
179+
defer stopDAHeightTicker()
180+
181+
nodeConfig := rollconf.DefaultConfig()
182+
nodeConfig.RootDir = t.TempDir()
183+
184+
cmd := newRunNodeCmd(t.Context(), executor, sequencer, keyProvider, nodeKey, ds, nodeConfig)
185+
require.NoError(t, cmd.ParseFlags([]string{"start", "--rollkit.node.aggregator"}))
186+
187+
_, err := ParseConfig(cmd)
188+
require.EqualError(t, err, "DA address is required when aggregator mode is enabled")
189+
}
190+
177191
func TestPromotableFlagInvariants(t *testing.T) {
178192
flagVariants := [][]string{{
179193
"--rollkit.node.promotable=false",

pkg/config/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,12 @@ type DAConfig struct {
273273
// DAEnabled reports whether this node should initialize a DA client. Full
274274
// followers without a DA address sync exclusively through P2P.
275275
func (c Config) DAEnabled() bool {
276-
return !c.Node.Light && strings.TrimSpace(c.DA.Address) != ""
276+
return !c.Node.Light && c.GetDAAddress() != ""
277+
}
278+
279+
// GetDAAddress returns the configured DA address without surrounding whitespace.
280+
func (c Config) GetDAAddress() string {
281+
return strings.TrimSpace(c.DA.Address)
277282
}
278283

279284
// GetNamespace returns the namespace for header submissions.
@@ -511,10 +516,6 @@ func (c *Config) Validate() error {
511516
return fmt.Errorf("promotable mode cannot be combined with light node mode")
512517
}
513518

514-
if c.Node.Aggregator && strings.TrimSpace(c.DA.Address) == "" {
515-
return fmt.Errorf("DA address is required when aggregator mode is enabled")
516-
}
517-
518519
// Promotable mode and Raft both control leader/follower role switching.
519520
// Combining them needs a composed elector that gates Raft leadership on the execution-selected proposer.
520521
if c.Node.Promotable && c.Raft.Enable {

pkg/config/config_test.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ func TestDAEnabled(t *testing.T) {
6363
expectedEnabled: true,
6464
expectedAddress: "http://da.example:7980",
6565
},
66+
{
67+
name: "follower with whitespace around DA address",
68+
configure: func(cfg *Config) {
69+
cfg.DA.Address = " http://da.example:7980\n"
70+
},
71+
expectedEnabled: true,
72+
expectedAddress: "http://da.example:7980",
73+
},
6674
{
6775
name: "aggregator without DA address does not initialize DA",
6876
configure: func(cfg *Config) {
@@ -90,18 +98,17 @@ func TestDAEnabled(t *testing.T) {
9098
}
9199

92100
assert.Equal(t, tt.expectedEnabled, cfg.DAEnabled())
93-
assert.Equal(t, tt.expectedAddress, cfg.DA.Address)
101+
assert.Equal(t, tt.expectedAddress, cfg.GetDAAddress())
94102
})
95103
}
96104
}
97105

98-
func TestConfigValidate_AggregatorRequiresDAAddress(t *testing.T) {
106+
func TestConfigValidate_AggregatorCanBeInitializedWithoutDAAddress(t *testing.T) {
99107
cfg := DefaultConfig()
100108
cfg.RootDir = t.TempDir()
101109
cfg.Node.Aggregator = true
102110

103-
err := cfg.Validate()
104-
require.EqualError(t, err, "DA address is required when aggregator mode is enabled")
111+
require.NoError(t, cfg.Validate())
105112
}
106113

107114
func TestAddFlags(t *testing.T) {

test/e2e/base_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121

2222
var binaryPath string
2323

24+
const localDAAddress = "http://localhost:7980"
25+
2426
func init() {
2527
flag.StringVar(&binaryPath, "binary", "testapp", "testapp binary")
2628
}
@@ -72,6 +74,7 @@ func TestBasic(t *testing.T) {
7274
"--evnode.signer.passphrase_file="+passphraseFile,
7375
"--evnode.node.block_time=5ms",
7476
"--evnode.da.block_time=15ms",
77+
"--evnode.da.address="+localDAAddress,
7578
fmt.Sprintf("--kv-endpoint=127.0.0.1:%d", kvPort),
7679
)
7780

@@ -174,6 +177,7 @@ func TestNodeRestartPersistence(t *testing.T) {
174177
"--evnode.signer.passphrase_file="+passphraseFile,
175178
"--evnode.node.block_time=5ms",
176179
"--evnode.da.block_time=15ms",
180+
"--evnode.da.address="+localDAAddress,
177181
fmt.Sprintf("--kv-endpoint=127.0.0.1:%d", kvPort),
178182
)
179183
sut.AwaitNodeUp(t, "http://127.0.0.1:7331", 2*time.Second)
@@ -214,6 +218,7 @@ func TestNodeRestartPersistence(t *testing.T) {
214218
"--evnode.signer.passphrase_file="+passphraseFile,
215219
"--evnode.node.block_time=5ms",
216220
"--evnode.da.block_time=15ms",
221+
"--evnode.da.address="+localDAAddress,
217222
fmt.Sprintf("--kv-endpoint=127.0.0.1:%d", kvPort),
218223
)
219224
sut.AwaitNodeUp(t, "http://127.0.0.1:7331", 2*time.Second)

0 commit comments

Comments
 (0)