Skip to content

Commit 629a8fc

Browse files
committed
add factory
1 parent ac5172c commit 629a8fc

File tree

7 files changed

+541
-38
lines changed

7 files changed

+541
-38
lines changed

apps/evm/cmd/run.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/evstack/ev-node/node"
2222
rollcmd "github.com/evstack/ev-node/pkg/cmd"
2323
"github.com/evstack/ev-node/pkg/config"
24-
danode "github.com/evstack/ev-node/pkg/da/node"
24+
"github.com/evstack/ev-node/pkg/da/factory"
2525
da "github.com/evstack/ev-node/pkg/da/types"
2626
"github.com/evstack/ev-node/pkg/genesis"
2727
genesispkg "github.com/evstack/ev-node/pkg/genesis"
@@ -60,7 +60,12 @@ var RunCmd = &cobra.Command{
6060
return err
6161
}
6262

63-
blobClient, err := danode.NewClient(context.Background(), nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
63+
blobClient, err := factory.NewClient(context.Background(), factory.Config{
64+
Address: nodeConfig.DA.Address,
65+
AuthToken: nodeConfig.DA.AuthToken,
66+
Logger: logger,
67+
IsAggregator: nodeConfig.Node.Aggregator,
68+
})
6469
if err != nil {
6570
return fmt.Errorf("failed to create blob client: %w", err)
6671
}

apps/grpc/cmd/run.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/evstack/ev-node/node"
1717
rollcmd "github.com/evstack/ev-node/pkg/cmd"
1818
"github.com/evstack/ev-node/pkg/config"
19-
danode "github.com/evstack/ev-node/pkg/da/node"
19+
"github.com/evstack/ev-node/pkg/da/factory"
2020
da "github.com/evstack/ev-node/pkg/da/types"
2121
"github.com/evstack/ev-node/pkg/genesis"
2222
rollgenesis "github.com/evstack/ev-node/pkg/genesis"
@@ -108,7 +108,12 @@ func createSequencer(
108108
genesis genesis.Genesis,
109109
executor execution.Executor,
110110
) (coresequencer.Sequencer, error) {
111-
blobClient, err := danode.NewClient(ctx, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
111+
blobClient, err := factory.NewClient(ctx, factory.Config{
112+
Address: nodeConfig.DA.Address,
113+
AuthToken: nodeConfig.DA.AuthToken,
114+
Logger: logger,
115+
IsAggregator: nodeConfig.Node.Aggregator,
116+
})
112117
if err != nil {
113118
return nil, fmt.Errorf("failed to create blob client: %w", err)
114119
}

apps/testapp/cmd/run.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/evstack/ev-node/node"
1717
"github.com/evstack/ev-node/pkg/cmd"
1818
"github.com/evstack/ev-node/pkg/config"
19-
danode "github.com/evstack/ev-node/pkg/da/node"
19+
"github.com/evstack/ev-node/pkg/da/factory"
2020
da "github.com/evstack/ev-node/pkg/da/types"
2121
"github.com/evstack/ev-node/pkg/genesis"
2222
"github.com/evstack/ev-node/pkg/p2p/key"
@@ -111,7 +111,12 @@ func createSequencer(
111111
genesis genesis.Genesis,
112112
executor execution.Executor,
113113
) (coresequencer.Sequencer, error) {
114-
blobClient, err := danode.NewClient(ctx, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
114+
blobClient, err := factory.NewClient(ctx, factory.Config{
115+
Address: nodeConfig.DA.Address,
116+
AuthToken: nodeConfig.DA.AuthToken,
117+
Logger: logger,
118+
IsAggregator: nodeConfig.Node.Aggregator,
119+
})
115120
if err != nil {
116121
return nil, fmt.Errorf("failed to create blob client: %w", err)
117122
}

pkg/cmd/run_node.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
coresequencer "github.com/evstack/ev-node/core/sequencer"
2222
"github.com/evstack/ev-node/node"
2323
rollconf "github.com/evstack/ev-node/pkg/config"
24-
danode "github.com/evstack/ev-node/pkg/da/node"
24+
"github.com/evstack/ev-node/pkg/da/factory"
2525
genesispkg "github.com/evstack/ev-node/pkg/genesis"
2626
"github.com/evstack/ev-node/pkg/p2p"
2727
"github.com/evstack/ev-node/pkg/p2p/key"
@@ -107,12 +107,15 @@ func StartNode(
107107
}()
108108
}
109109

110-
blobClient, err := danode.NewClient(ctx, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
110+
blobClient, err := factory.NewClient(ctx, factory.Config{
111+
Address: nodeConfig.DA.Address,
112+
AuthToken: nodeConfig.DA.AuthToken,
113+
Logger: logger,
114+
IsAggregator: nodeConfig.Node.Aggregator,
115+
})
111116
if err != nil {
112117
return fmt.Errorf("failed to create blob client: %w", err)
113118
}
114-
defer blobClient.Close()
115-
// The node client now implements BlobClient interface directly
116119
daClient := block.NewDAClient(blobClient, nodeConfig, logger)
117120

118121
// create a new remote signer

pkg/da/app/client.go

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -350,39 +350,13 @@ func (c *Client) GetLatestDAHeight(ctx context.Context) (uint64, error) {
350350
// Note: celestia-app doesn't provide proofs directly - they need to be computed
351351
// from the block data or obtained from celestia-node.
352352
func (c *Client) GetProofs(ctx context.Context, ids []datypes.ID, namespace []byte) ([]datypes.Proof, error) {
353-
if len(ids) == 0 {
354-
return []datypes.Proof{}, nil
355-
}
356-
357-
// TODO: Implement proof generation
358-
// This would require:
359-
// 1. Fetching the block and ExtendedDataSquare
360-
// 2. Computing the NMT proofs for each blob
361-
// 3. This is complex and typically done by celestia-node
362-
//
363-
// For now, return an error indicating this is not supported
364-
return nil, errors.New("GetProofs not implemented: proof generation requires celestia-node or full EDS computation")
353+
return nil, errors.New("GetProofs not supported: celestia-app client does not support proof generation.")
365354
}
366355

367356
// Validate validates commitments against the corresponding proofs.
368357
// Note: This requires proof generation which is not implemented.
369358
func (c *Client) Validate(ctx context.Context, ids []datypes.ID, proofs []datypes.Proof, namespace []byte) ([]bool, error) {
370-
if len(ids) != len(proofs) {
371-
return nil, errors.New("number of IDs and proofs must match")
372-
}
373-
374-
if len(ids) == 0 {
375-
return []bool{}, nil
376-
}
377-
378-
// TODO: Implement proof validation
379-
// This would require:
380-
// 1. Parsing the proofs
381-
// 2. Verifying NMT inclusion
382-
// 3. This is typically done by celestia-node
383-
//
384-
// For now, return an error indicating this is not supported
385-
return nil, errors.New("Validate not implemented: proof validation requires celestia-node")
359+
return nil, errors.New("Validate not supported: celestia-app client does not support proof validation.")
386360
}
387361

388362
// RPC Types for CometBFT JSON-RPC responses

pkg/da/factory/factory.go

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// Package factory provides a factory for creating DA clients based on configuration.
2+
// It automatically detects whether to use celestia-node or celestia-app client
3+
// based on the address and aggregator mode.
4+
package factory
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net/url"
10+
"strings"
11+
12+
"github.com/rs/zerolog"
13+
14+
daapp "github.com/evstack/ev-node/pkg/da/app"
15+
danode "github.com/evstack/ev-node/pkg/da/node"
16+
datypes "github.com/evstack/ev-node/pkg/da/types"
17+
)
18+
19+
// ClientType indicates which type of DA client to use.
20+
type ClientType int
21+
22+
const (
23+
// ClientTypeAuto automatically detects the client type based on address.
24+
ClientTypeAuto ClientType = iota
25+
// ClientTypeNode forces the use of celestia-node client.
26+
ClientTypeNode
27+
// ClientTypeApp forces the use of celestia-app client.
28+
ClientTypeApp
29+
)
30+
31+
// Config contains configuration for creating a DA client.
32+
type Config struct {
33+
// Address is the DA endpoint address (e.g., "http://localhost:26657" or "http://localhost:26658")
34+
Address string
35+
// AuthToken is the authentication token for celestia-node (optional)
36+
AuthToken string
37+
// AuthHeaderName is the name of the auth header (optional, defaults to "Authorization")
38+
AuthHeaderName string
39+
// Logger for logging
40+
Logger zerolog.Logger
41+
// ForceType forces a specific client type (optional, defaults to auto-detection)
42+
ForceType ClientType
43+
// IsAggregator indicates if the node is running in aggregator mode.
44+
// When true, always uses celestia-node client.
45+
IsAggregator bool
46+
}
47+
48+
// NewClient creates a new DA client based on the configuration.
49+
// It automatically detects whether to use celestia-node or celestia-app client
50+
// based on the address port and aggregator mode.
51+
//
52+
// Detection rules:
53+
// - If IsAggregator is true, always uses celestia-node client
54+
// - Port 26657 (or any port) -> celestia-app client (CometBFT RPC)
55+
// - Port 26658 -> celestia-node client (blob module)
56+
// - Any other port -> celestia-node client (assumed to be custom node endpoint)
57+
//
58+
// Note: celestia-app client (port 26657) does not support proof operations
59+
// (GetProofs/Validate). Use celestia-node client for full functionality.
60+
func NewClient(ctx context.Context, cfg Config) (datypes.BlobClient, error) {
61+
// Always use node client in aggregator mode
62+
if cfg.IsAggregator {
63+
cfg.Logger.Debug().
64+
Str("address", cfg.Address).
65+
Msg("aggregator mode enabled, using celestia-node client")
66+
return createNodeClient(ctx, cfg)
67+
}
68+
69+
// Check if type is forced
70+
switch cfg.ForceType {
71+
case ClientTypeNode:
72+
cfg.Logger.Debug().Msg("forced celestia-node client")
73+
return createNodeClient(ctx, cfg)
74+
case ClientTypeApp:
75+
cfg.Logger.Debug().Msg("forced celestia-app client")
76+
return createAppClient(cfg), nil
77+
}
78+
79+
// Auto-detect based on address
80+
clientType := detectClientType(cfg.Address)
81+
82+
switch clientType {
83+
case ClientTypeApp:
84+
cfg.Logger.Debug().
85+
Str("address", cfg.Address).
86+
Msg("auto-detected celestia-app client (port 26657)")
87+
return createAppClient(cfg), nil
88+
case ClientTypeNode:
89+
cfg.Logger.Debug().
90+
Str("address", cfg.Address).
91+
Msg("auto-detected celestia-node client")
92+
return createNodeClient(ctx, cfg)
93+
default:
94+
return nil, fmt.Errorf("unable to detect client type for address: %s", cfg.Address)
95+
}
96+
}
97+
98+
// detectClientType determines which client to use based on the address.
99+
// Port 26657 is celestia-app (CometBFT RPC).
100+
// Port 26658 or any other port is treated as celestia-node.
101+
func detectClientType(address string) ClientType {
102+
// Parse the URL to extract port
103+
u, err := url.Parse(address)
104+
if err != nil {
105+
// If parsing fails, try adding scheme
106+
u, err = url.Parse("http://" + address)
107+
if err != nil {
108+
// Can't parse, default to node client
109+
return ClientTypeNode
110+
}
111+
}
112+
113+
// If the "scheme" looks like a hostname (localhost or contains digits),
114+
// then url.Parse misinterpreted the address and we need to re-parse with a scheme.
115+
if u.Host == "" && (u.Scheme == "localhost" || containsDigits(u.Scheme)) {
116+
u, _ = url.Parse("http://" + address)
117+
}
118+
119+
// Extract port
120+
port := u.Port()
121+
if port == "" {
122+
// No port specified, check scheme
123+
switch u.Scheme {
124+
case "http":
125+
port = "80"
126+
case "https":
127+
port = "443"
128+
default:
129+
// Default to node client
130+
return ClientTypeNode
131+
}
132+
}
133+
134+
// Port 26657 is celestia-app CometBFT RPC
135+
if port == "26657" {
136+
return ClientTypeApp
137+
}
138+
139+
// All other ports (including 26658) are treated as celestia-node
140+
return ClientTypeNode
141+
}
142+
143+
// containsDigits checks if a string contains any digits.
144+
func containsDigits(s string) bool {
145+
for _, c := range s {
146+
if c >= '0' && c <= '9' {
147+
return true
148+
}
149+
}
150+
return false
151+
}
152+
153+
// createNodeClient creates a celestia-node client.
154+
func createNodeClient(ctx context.Context, cfg Config) (datypes.BlobClient, error) {
155+
client, err := danode.NewClient(ctx, cfg.Address, cfg.AuthToken, cfg.AuthHeaderName)
156+
if err != nil {
157+
return nil, fmt.Errorf("failed to create celestia-node client: %w", err)
158+
}
159+
return client, nil
160+
}
161+
162+
// createAppClient creates a celestia-app client.
163+
func createAppClient(cfg Config) datypes.BlobClient {
164+
return daapp.NewClient(daapp.Config{
165+
RPCAddress: cfg.Address,
166+
Logger: cfg.Logger,
167+
DefaultTimeout: 0, // Use default
168+
})
169+
}
170+
171+
// IsNodeAddress checks if the given address is likely a celestia-node endpoint.
172+
// Returns true for any address that is NOT port 26657.
173+
func IsNodeAddress(address string) bool {
174+
return detectClientType(address) == ClientTypeNode
175+
}
176+
177+
// IsAppAddress checks if the given address is likely a celestia-app endpoint.
178+
// Returns true only for port 26657.
179+
func IsAppAddress(address string) bool {
180+
return detectClientType(address) == ClientTypeApp
181+
}
182+
183+
// ValidateAddress checks if the address is valid and returns the detected client type.
184+
func ValidateAddress(address string) (ClientType, error) {
185+
if strings.TrimSpace(address) == "" {
186+
return ClientTypeAuto, fmt.Errorf("DA address cannot be empty")
187+
}
188+
189+
// Try to parse as URL
190+
u, err := url.Parse(address)
191+
if err != nil {
192+
// Try with http:// prefix
193+
u, err = url.Parse("http://" + address)
194+
if err != nil {
195+
return ClientTypeAuto, fmt.Errorf("invalid DA address format: %w", err)
196+
}
197+
}
198+
199+
if u.Host == "" {
200+
return ClientTypeAuto, fmt.Errorf("DA address must include host")
201+
}
202+
203+
return detectClientType(address), nil
204+
}

0 commit comments

Comments
 (0)