Skip to content

Commit 5a8693e

Browse files
authored
feat: enable mock servers with intercept command (#1911)
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> --> This PR does two key things: 1. Handles already running sequencer 2. Enables using mock services with the intercept command. 1 is pretty straight forward as we did this for the DA server as well. The one thing to note though with this PR is that because we are checking for already running servers, I just remove the redundant check of if the flag was set. I add a comment to the code to explain the reasoning and to document the context for the future. For 2, this will help us clean up the rollkit docs. Right now, even for simple ignite apps, you have to run the local da, local sequencer, and in the future some local execution. This makes the docs and rollkit feel clunky. By adding the mock server commands to the intercept command, we can spin up mock da and mock sequencer for these tutorials to reduce the steps required by the users. Enables: evstack/docs#485 Closes #1910 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes - **New Features** - Added command-line option `--rollkit.sequencer_rollup_id` for specifying the sequencer middleware rollup ID, defaulting to "mock-rollup." - Introduced functionality to start mock services for improved testing and development. - **Bug Fixes** - Enhanced error handling for starting mock servers, including checks for already running services. - **Documentation** - Updated documentation to reflect new command-line options and improved configurations. - **Refactor** - Streamlined control flow and error handling in the `InterceptCommand` and mock server initialization processes. - Improved code structure and readability for mock server startup logic. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent accd48f commit 5a8693e

File tree

6 files changed

+126
-51
lines changed

6 files changed

+126
-51
lines changed

cmd/rollkit/commands/intercept.go

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package commands
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"os/exec"
@@ -9,6 +10,7 @@ import (
910
cometos "github.com/cometbft/cometbft/libs/os"
1011
"github.com/spf13/cobra"
1112

13+
proxy "github.com/rollkit/go-da/proxy/jsonrpc"
1214
rollconf "github.com/rollkit/rollkit/config"
1315
)
1416

@@ -22,47 +24,85 @@ func InterceptCommand(
2224
rollkitCommand *cobra.Command,
2325
readToml func() (rollconf.TomlConfig, error),
2426
runEntrypoint func(*rollconf.TomlConfig, []string) error,
25-
) (bool, error) {
27+
) (shouldExecute bool, err error) {
2628
// Grab flags and verify command
2729
flags := []string{}
30+
isStartCommand := false
2831
if len(os.Args) >= 2 {
2932
flags = os.Args[1:]
3033

3134
// Handle specific cases first for help, version, and start
3235
switch os.Args[1] {
3336
case "help", "--help", "h", "-h",
3437
"version", "--version", "v", "-v":
35-
return false, nil
38+
return
3639
case "start":
37-
goto readTOML
38-
}
39-
40-
// Check if user attempted to run a rollkit command
41-
for _, cmd := range rollkitCommand.Commands() {
42-
if os.Args[1] == cmd.Use {
43-
return false, nil
40+
isStartCommand = true
41+
default:
42+
// Check if user attempted to run a rollkit command
43+
for _, cmd := range rollkitCommand.Commands() {
44+
if os.Args[1] == cmd.Use {
45+
return
46+
}
4447
}
4548
}
4649
}
4750

48-
readTOML:
49-
var err error
5051
rollkitConfig, err = readToml()
5152
if err != nil {
52-
return false, err
53+
return
5354
}
5455

5556
// To avoid recursive calls, we check if the root directory is the rollkit repository itself
5657
if filepath.Base(rollkitConfig.RootDir) == "rollkit" {
57-
return false, nil
58+
return
5859
}
5960

61+
// At this point we expect to execute the command against the entrypoint
62+
shouldExecute = true
63+
6064
// After successfully reading the TOML file, we expect to be able to use the entrypoint
6165
if rollkitConfig.Entrypoint == "" {
62-
return true, fmt.Errorf("no entrypoint specified in %s", rollconf.RollkitToml)
66+
err = fmt.Errorf("no entrypoint specified in %s", rollconf.RollkitToml)
67+
return
6368
}
6469

65-
return true, runEntrypoint(&rollkitConfig, flags)
70+
if isStartCommand {
71+
// Try and launch mock services or connect to default addresses
72+
daAddress := parseFlag(flags, rollconf.FlagDAAddress)
73+
if daAddress == "" {
74+
daAddress = rollconf.DefaultDAAddress
75+
}
76+
daSrv, err := tryStartMockDAServJSONRPC(rollkitCommand.Context(), daAddress, proxy.NewServer)
77+
if err != nil && !errors.Is(err, errDAServerAlreadyRunning) {
78+
return shouldExecute, fmt.Errorf("failed to launch mock da server: %w", err)
79+
}
80+
// nolint:errcheck,gosec
81+
defer func() {
82+
if daSrv != nil {
83+
daSrv.Stop(rollkitCommand.Context())
84+
}
85+
}()
86+
sequencerAddress := parseFlag(flags, rollconf.FlagSequencerAddress)
87+
if sequencerAddress == "" {
88+
sequencerAddress = rollconf.DefaultSequencerAddress
89+
}
90+
rollupID := parseFlag(flags, rollconf.FlagSequencerRollupID)
91+
if rollupID == "" {
92+
rollupID = rollconf.DefaultSequencerRollupID
93+
}
94+
seqSrv, err := tryStartMockSequencerServerGRPC(sequencerAddress, rollupID)
95+
if err != nil && !errors.Is(err, errSequencerAlreadyRunning) {
96+
return shouldExecute, fmt.Errorf("failed to launch mock sequencing server: %w", err)
97+
}
98+
// nolint:errcheck,gosec
99+
defer func() {
100+
if seqSrv != nil {
101+
seqSrv.Stop()
102+
}
103+
}()
104+
}
105+
return shouldExecute, runEntrypoint(&rollkitConfig, flags)
66106
}
67107

68108
func buildEntrypoint(rootDir, entrypointSourceFile string, forceRebuild bool) (string, error) {
@@ -125,3 +165,14 @@ func RunRollupEntrypoint(rollkitConfig *rollconf.TomlConfig, args []string) erro
125165

126166
return nil
127167
}
168+
169+
func parseFlag(args []string, flag string) string {
170+
for i, arg := range args {
171+
if arg == fmt.Sprintf("--%s", flag) {
172+
if len(args) > i+1 {
173+
return args[i+1]
174+
}
175+
}
176+
}
177+
return ""
178+
}

cmd/rollkit/commands/run_node.go

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,10 @@ var (
5151
// initialize the logger with the cometBFT defaults
5252
logger = cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout))
5353

54-
errDAServerAlreadyRunning = errors.New("DA server already running")
54+
errDAServerAlreadyRunning = errors.New("DA server already running")
55+
errSequencerAlreadyRunning = errors.New("sequencer already running")
5556
)
5657

57-
// MockSequencerAddress is a sample address used by the mock sequencer
58-
const MockSequencerAddress = "localhost:50051"
59-
6058
// NewRunNodeCmd returns the command that allows the CLI to start a node.
6159
func NewRunNodeCmd() *cobra.Command {
6260
cmd := &cobra.Command{
@@ -130,29 +128,36 @@ func NewRunNodeCmd() *cobra.Command {
130128
// initialize the metrics
131129
metrics := rollnode.DefaultMetricsProvider(cometconf.DefaultInstrumentationConfig())
132130

133-
// use mock jsonrpc da server by default
134-
if !cmd.Flags().Lookup("rollkit.da_address").Changed {
135-
srv, err := startMockDAServJSONRPC(cmd.Context(), nodeConfig.DAAddress, proxy.NewServer)
136-
if err != nil && !errors.Is(err, errDAServerAlreadyRunning) {
137-
return fmt.Errorf("failed to launch mock da server: %w", err)
138-
}
139-
// nolint:errcheck,gosec
140-
defer func() {
141-
if srv != nil {
142-
srv.Stop(cmd.Context())
143-
}
144-
}()
131+
// Try and launch a mock JSON RPC DA server if there is no DA server running.
132+
// NOTE: if the user supplied an address for a running DA server, and the address doesn't match, this will launch a mock DA server. This is ok because the logs will tell the user that a mock DA server is being used.
133+
daSrv, err := tryStartMockDAServJSONRPC(cmd.Context(), nodeConfig.DAAddress, proxy.NewServer)
134+
if err != nil && !errors.Is(err, errDAServerAlreadyRunning) {
135+
return fmt.Errorf("failed to launch mock da server: %w", err)
145136
}
146-
147-
// use mock grpc sequencer server by default
148-
if !cmd.Flags().Lookup("rollkit.sequencer_address").Changed {
149-
srv, err := startMockSequencerServerGRPC(MockSequencerAddress, genDoc.ChainID)
150-
if err != nil {
151-
return fmt.Errorf("failed to launch mock sequencing server: %w", err)
137+
// nolint:errcheck,gosec
138+
defer func() {
139+
if daSrv != nil {
140+
daSrv.Stop(cmd.Context())
152141
}
153-
// nolint:errcheck,gosec
154-
defer func() { srv.Stop() }()
142+
}()
143+
144+
// Determine which rollupID to use. If the flag has been set we want to use that value and ensure that the chainID in the genesis doc matches.
145+
if cmd.Flags().Lookup(rollconf.FlagSequencerRollupID).Changed {
146+
genDoc.ChainID = nodeConfig.SequencerRollupID
155147
}
148+
sequencerRollupID := genDoc.ChainID
149+
// Try and launch a mock gRPC sequencer if there is no sequencer running.
150+
// NOTE: if the user supplied an address for a running sequencer, and the address doesn't match, this will launch a mock sequencer. This is ok because the logs will tell the user that a mock sequencer is being used.
151+
seqSrv, err := tryStartMockSequencerServerGRPC(nodeConfig.SequencerAddress, sequencerRollupID)
152+
if err != nil && !errors.Is(err, errSequencerAlreadyRunning) {
153+
return fmt.Errorf("failed to launch mock sequencing server: %w", err)
154+
}
155+
// nolint:errcheck,gosec
156+
defer func() {
157+
if seqSrv != nil {
158+
seqSrv.Stop()
159+
}
160+
}()
156161

157162
// use noop proxy app by default
158163
if !cmd.Flags().Lookup("proxy_app").Changed {
@@ -244,8 +249,8 @@ func addNodeFlags(cmd *cobra.Command) {
244249
rollconf.AddFlags(cmd)
245250
}
246251

247-
// startMockDAServJSONRPC starts a mock JSONRPC server
248-
func startMockDAServJSONRPC(
252+
// tryStartMockDAServJSONRPC will try and start a mock JSONRPC server
253+
func tryStartMockDAServJSONRPC(
249254
ctx context.Context,
250255
daAddress string,
251256
newServer func(string, string, da.DA) *proxy.Server,
@@ -258,28 +263,34 @@ func startMockDAServJSONRPC(
258263
srv := newServer(addr.Hostname(), addr.Port(), goDATest.NewDummyDA())
259264
if err := srv.Start(ctx); err != nil {
260265
if errors.Is(err, syscall.EADDRINUSE) {
261-
logger.Info("DA server is already running", "address", nodeConfig.DAAddress)
266+
logger.Info("DA server is already running", "address", daAddress)
262267
return nil, errDAServerAlreadyRunning
263268
}
264269
return nil, err
265270
}
266271

267-
logger.Info("Starting mock DA server", "address", nodeConfig.DAAddress)
272+
logger.Info("Starting mock DA server", "address", daAddress)
268273

269274
return srv, nil
270275
}
271276

272-
// startMockSequencerServerGRPC starts a mock gRPC server with the given listenAddress.
273-
func startMockSequencerServerGRPC(listenAddress string, rollupId string) (*grpc.Server, error) {
277+
// tryStartMockSequencerServerGRPC will try and start a mock gRPC server with the given listenAddress.
278+
func tryStartMockSequencerServerGRPC(listenAddress string, rollupId string) (*grpc.Server, error) {
274279
dummySeq := seqTest.NewDummySequencer([]byte(rollupId))
275280
server := seqGRPC.NewServer(dummySeq, dummySeq, dummySeq)
276281
lis, err := net.Listen("tcp", listenAddress)
277282
if err != nil {
283+
if errors.Is(err, syscall.EADDRINUSE) {
284+
logger.Info(errSequencerAlreadyRunning.Error(), "address", listenAddress)
285+
logger.Info("make sure your rollupID matches your sequencer", "rollupID", rollupId)
286+
return nil, errSequencerAlreadyRunning
287+
}
278288
return nil, err
279289
}
280290
go func() {
281291
_ = server.Serve(lis)
282292
}()
293+
logger.Info("Starting mock sequencer", "address", listenAddress, "rollupID", rollupId)
283294
return server, nil
284295
}
285296

cmd/rollkit/commands/run_node_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ func TestStartMockDAServJSONRPC(t *testing.T) {
209209
return mockServer.Server
210210
}
211211

212-
srv, err := startMockDAServJSONRPC(context.Background(), tt.daAddress, newServerFunc)
212+
srv, err := tryStartMockDAServJSONRPC(context.Background(), tt.daAddress, newServerFunc)
213213

214214
if tt.expectedErr != nil {
215215
assert.Error(t, err)

cmd/rollkit/docs/rollkit_start.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ rollkit start [flags]
4545
--rollkit.light run light client
4646
--rollkit.max_pending_blocks uint limit of blocks pending DA submission (0 for no limit)
4747
--rollkit.sequencer_address string sequencer middleware address (host:port) (default "localhost:50051")
48+
--rollkit.sequencer_rollup_id string sequencer middleware rollup ID (default: mock-rollup) (default "mock-rollup")
4849
--rollkit.trusted_hash string initial trusted hash to start the header exchange service
4950
--rpc.grpc_laddr string GRPC listen address (BroadcastTx only). Port required
5051
--rpc.laddr string RPC listen address. Port required (default "tcp://127.0.0.1:26657")

config/config.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const (
4444
FlagLazyBlockTime = "rollkit.lazy_block_time"
4545
// FlagSequencerAddress is a flag for specifying the sequencer middleware address
4646
FlagSequencerAddress = "rollkit.sequencer_address"
47+
// FlagSequencerRollupID is a flag for specifying the sequencer middleware rollup ID
48+
FlagSequencerRollupID = "rollkit.sequencer_rollup_id"
4749
)
4850

4951
// NodeConfig stores Rollkit node configuration.
@@ -66,8 +68,9 @@ type NodeConfig struct {
6668
DASubmitOptions string `mapstructure:"da_submit_options"`
6769

6870
// CLI flags
69-
DANamespace string `mapstructure:"da_namespace"`
70-
SequencerAddress string `mapstructure:"sequencer_address"`
71+
DANamespace string `mapstructure:"da_namespace"`
72+
SequencerAddress string `mapstructure:"sequencer_address"`
73+
SequencerRollupID string `mapstructure:"sequencer_rollup_id"`
7174
}
7275

7376
// HeaderConfig allows node to pass the initial trusted header hash to start the header exchange service
@@ -143,6 +146,7 @@ func (nc *NodeConfig) GetViperConfig(v *viper.Viper) error {
143146
nc.DAMempoolTTL = v.GetUint64(FlagDAMempoolTTL)
144147
nc.LazyBlockTime = v.GetDuration(FlagLazyBlockTime)
145148
nc.SequencerAddress = v.GetString(FlagSequencerAddress)
149+
nc.SequencerRollupID = v.GetString(FlagSequencerRollupID)
146150

147151
return nil
148152
}
@@ -170,4 +174,5 @@ func AddFlags(cmd *cobra.Command) {
170174
cmd.Flags().Uint64(FlagDAMempoolTTL, def.DAMempoolTTL, "number of DA blocks until transaction is dropped from the mempool")
171175
cmd.Flags().Duration(FlagLazyBlockTime, def.LazyBlockTime, "block time (for lazy mode)")
172176
cmd.Flags().String(FlagSequencerAddress, def.SequencerAddress, "sequencer middleware address (host:port)")
177+
cmd.Flags().String(FlagSequencerRollupID, def.SequencerRollupID, "sequencer middleware rollup ID (default: mock-rollup)")
173178
}

config/defaults.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ const (
1212
// Version is the current rollkit version
1313
// Please keep updated with each new release
1414
Version = "0.38.5"
15+
// DefaultDAAddress is the default address for the DA middleware
16+
DefaultDAAddress = "http://localhost:26658"
17+
// DefaultSequencerAddress is the default address for the sequencer middleware
18+
DefaultSequencerAddress = "localhost:50051"
19+
// DefaultSequencerRollupID is the default rollup ID for the sequencer middleware
20+
DefaultSequencerRollupID = "mock-rollup"
1521
)
1622

1723
// DefaultNodeConfig keeps default values of NodeConfig
@@ -27,13 +33,14 @@ var DefaultNodeConfig = NodeConfig{
2733
LazyAggregator: false,
2834
LazyBlockTime: 60 * time.Second,
2935
},
30-
DAAddress: "http://localhost:26658",
36+
DAAddress: DefaultDAAddress,
3137
DAGasPrice: -1,
3238
DAGasMultiplier: 0,
3339
Light: false,
3440
HeaderConfig: HeaderConfig{
3541
TrustedHash: "",
3642
},
37-
Instrumentation: config.DefaultInstrumentationConfig(),
38-
SequencerAddress: "localhost:50051",
43+
Instrumentation: config.DefaultInstrumentationConfig(),
44+
SequencerAddress: DefaultSequencerAddress,
45+
SequencerRollupID: DefaultSequencerRollupID,
3946
}

0 commit comments

Comments
 (0)