Skip to content

Commit 03c459c

Browse files
bmuddhaDodecahedr0x
authored andcommitted
refactor: optimize config crate structure and API (#752)
1 parent 77347ee commit 03c459c

31 files changed

+310
-1030
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config.example.toml

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -31,47 +31,31 @@
3131
# Env: MBV_LIFECYCLE
3232
lifecycle = "ephemeral"
3333

34-
# Remote connections for RPC, WebSocket, and gRPC.
35-
# You can define multiple remotes of different kinds.
36-
# The first RPC remote will be used for JSON-RPC calls.
37-
# Each WebSocket/gRPC remote will create a pubsub client.
34+
# Remote endpoints for syncing with the base chain.
35+
# You can specify multiple remotes of different types.
36+
# The first HTTP/HTTPS remote will be used for JSON-RPC calls.
37+
# Each WebSocket/gRPC remote creates a subscription client.
3838
#
39-
# Available kinds:
40-
# - "rpc": JSON-RPC HTTP connection (typically port 8899)
41-
# - "websocket": WebSocket connection for PubSub (typically wss://)
42-
# - "grpc": gRPC connection for streaming (typically port 50051)
39+
# Supported URL schemes:
40+
# - "http", "https": JSON-RPC HTTP connections
41+
# - "ws", "wss": WebSocket connections for PubSub
42+
# - "grpc", "grpcs": gRPC connections for streaming
4343
#
44-
# URL Aliases (automatically resolved based on kind):
45-
# RPC aliases: "mainnet", "devnet", "local"
46-
# WebSocket aliases: "mainnet", "devnet", "local"
44+
# URL Aliases (resolved during parsing):
45+
# - "mainnet": resolves to https://api.mainnet-beta.solana.com/
46+
# - "devnet": resolves to https://api.devnet.solana.com/
47+
# - "testnet": resolves to https://api.testnet.solana.com/
48+
# - "localhost": resolves to http://localhost:8899/ (only for http/https schemes)
4749
#
48-
# Example 1: Using aliases
49-
# [[remote]]
50-
# kind = "rpc"
51-
# url = "devnet"
50+
# Examples:
51+
# remotes = ["devnet"] # Single devnet HTTP endpoint
52+
# remotes = ["mainnet", "wss://mainnet-beta.solana.com"] # Mainnet with explicit WebSocket
53+
# remotes = ["http://localhost:8899", "ws://localhost:8900"] # Local endpoints
5254
#
53-
# [[remote]]
54-
# kind = "websocket"
55-
# url = "devnet"
56-
#
57-
# Example 2: Using full URLs with optional API key
58-
# [[remote]]
59-
# kind = "rpc"
60-
# url = "https://api.devnet.solana.com"
61-
# api-key = "optional-key"
62-
#
63-
# [[remote]]
64-
# kind = "websocket"
65-
# url = "wss://api.devnet.solana.com"
66-
#
67-
# [[remote]]
68-
# kind = "grpc"
69-
# url = "http://grpc.example.com:50051"
70-
# api-key = "optional-key"
71-
72-
[[remote]]
73-
kind = "rpc"
74-
url = "devnet"
55+
# If no remotes are specified, defaults to ["devnet"] with an auto-added WebSocket endpoint.
56+
# Default: ["https://api.devnet.solana.com/"]
57+
# Env: Not supported (must be configured via TOML or CLI)
58+
remotes = ["devnet", "wss://devnet.solana.com", "grpcs://solana.helius.com"]
7559

7660
# Root directory for application storage (ledger, accountsdb, snapshots).
7761
# Default: "magicblock-test-storage" (created in current working directory)

magicblock-api/src/magic_validator.rs

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ use magicblock_config::{
3737
config::{
3838
ChainOperationConfig, LedgerConfig, LifecycleMode, LoadableProgram,
3939
},
40-
consts::DEFAULT_REMOTE,
41-
types::{resolve_url, RemoteKind},
4240
ValidatorParams,
4341
};
4442
use magicblock_core::{
@@ -342,7 +340,7 @@ impl MagicValidator {
342340
config.validator.keypair.insecure_clone(),
343341
committor_persist_path,
344342
ChainConfig {
345-
rpc_uri: config.rpc_url_or_default(),
343+
rpc_uri: config.rpc_url().to_owned(),
346344
commitment: CommitmentConfig::confirmed(),
347345
compute_budget_config: ComputeBudgetConfig::new(
348346
config.commit.compute_unit_price,
@@ -373,22 +371,14 @@ impl MagicValidator {
373371
faucet_pubkey: Pubkey,
374372
) -> ApiResult<ChainlinkImpl> {
375373
use magicblock_chainlink::remote_account_provider::Endpoint;
376-
let rpc_url = config.rpc_url_or_default();
377-
let endpoints = if config.has_subscription_url() {
378-
config
379-
.websocket_urls()
380-
.map(|pubsub_url| Endpoint {
381-
rpc_url: rpc_url.clone(),
382-
pubsub_url: pubsub_url.to_string(),
383-
})
384-
.collect::<Vec<_>>()
385-
} else {
386-
let ws_url = resolve_url(RemoteKind::Websocket, DEFAULT_REMOTE);
387-
vec![Endpoint {
374+
let rpc_url = config.rpc_url().to_owned();
375+
let endpoints = config
376+
.websocket_urls()
377+
.map(|pubsub_url| Endpoint {
388378
rpc_url: rpc_url.clone(),
389-
pubsub_url: ws_url,
390-
}]
391-
};
379+
pubsub_url: pubsub_url.to_string(),
380+
})
381+
.collect::<Vec<_>>();
392382

393383
let cloner = ChainlinkCloner::new(
394384
committor_service,
@@ -540,7 +530,7 @@ impl MagicValidator {
540530
});
541531

542532
DomainRegistryManager::handle_registration_static(
543-
self.config.rpc_url_or_default(),
533+
self.config.rpc_url(),
544534
&validator_keypair,
545535
validator_info,
546536
)
@@ -553,7 +543,7 @@ impl MagicValidator {
553543
let validator_keypair = validator_authority();
554544

555545
DomainRegistryManager::handle_unregistration_static(
556-
self.config.rpc_url_or_default(),
546+
self.config.rpc_url(),
557547
&validator_keypair,
558548
)
559549
.map_err(|err| {
@@ -567,7 +557,7 @@ impl MagicValidator {
567557
const MIN_BALANCE_SOL: u64 = 5;
568558

569559
let lamports = RpcClient::new_with_commitment(
570-
self.config.rpc_url_or_default(),
560+
self.config.rpc_url().to_owned(),
571561
CommitmentConfig::confirmed(),
572562
)
573563
.get_balance(&self.identity)
@@ -614,7 +604,7 @@ impl MagicValidator {
614604
.map(|co| co.claim_fees_frequency)
615605
{
616606
self.claim_fees_task
617-
.start(frequency, self.config.rpc_url_or_default());
607+
.start(frequency, self.config.rpc_url().to_owned());
618608
}
619609

620610
self.slot_ticker = Some(init_slot_ticker(

magicblock-config/src/config/cli.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ use serde::Serialize;
55

66
use crate::{
77
config::LifecycleMode,
8-
types::{
9-
remote::parse_remote_config, BindAddress, RemoteConfig, SerdeKeypair,
10-
},
8+
types::{network::Remote, BindAddress, SerdeKeypair},
119
};
1210

1311
/// CLI Arguments mirroring the structure of ValidatorParams.
@@ -18,13 +16,22 @@ pub struct CliParams {
1816
/// Path to the TOML configuration file.
1917
pub config: Option<PathBuf>,
2018

21-
/// Remote Solana cluster connections. Can be specified multiple times.
22-
/// Format: --remote <kind>:<url> or --remote <kind>:<alias>
23-
/// Examples: --remote rpc:devnet --remote websocket:devnet --remote grpc:http://localhost:50051
24-
/// Aliases: mainnet, devnet, local (resolved based on kind)
25-
#[arg(long, short, value_parser = parse_remote_config)]
26-
#[serde(skip_serializing_if = "Vec::is_empty", default)]
27-
pub remote: Vec<RemoteConfig>,
19+
/// List of remote endpoints for syncing with the base chain.
20+
/// Can be specified multiple times.
21+
///
22+
/// SUPPORTED SCHEMES: http(s), ws(s), grpc(s)
23+
///
24+
/// ALIASES: mainnet, devnet, testnet, localhost
25+
///
26+
/// EXAMPLES:
27+
/// - `--remote devnet`
28+
/// - `--remote wss://devnet.solana.com`
29+
/// - `--remote grpcs://grpc.example.com`
30+
///
31+
/// DEFAULT: devnet (HTTP endpoint with auto-added WS endpoint)
32+
#[arg(long)]
33+
#[serde(skip_serializing_if = "Option::is_none")]
34+
pub remotes: Option<Vec<Remote>>,
2835

2936
/// The application's operational mode.
3037
#[arg(long)]

magicblock-config/src/consts.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,9 @@ pub const DEFAULT_BASE_FEE: u64 = 0;
2020
/// Default compute unit price in microlamports
2121
pub const DEFAULT_COMPUTE_UNIT_PRICE: u64 = 1_000_000;
2222

23-
// Remote URL Aliases - RPC
24-
pub const RPC_MAINNET: &str = "https://api.mainnet-beta.solana.com/";
25-
pub const RPC_DEVNET: &str = "https://api.devnet.solana.com/";
26-
pub const RPC_LOCAL: &str = "http://localhost:8899/";
27-
28-
// Remote URL Aliases - WebSocket
29-
pub const WS_MAINNET: &str = "wss://api.mainnet-beta.solana.com/";
30-
pub const WS_DEVNET: &str = "wss://api.devnet.solana.com/";
31-
pub const WS_LOCAL: &str = "ws://localhost:8899/";
23+
/// Remote URL Aliases - Mainnet, Testnet, Devnet, and Localhost
24+
/// Solana mainnet-beta RPC endpoint
25+
pub const MAINNET_URL: &str = "https://api.mainnet-beta.solana.com/";
3226

3327
/// Solana testnet RPC endpoint
3428
pub const TESTNET_URL: &str = "https://api.testnet.solana.com/";

magicblock-config/src/lib.rs

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::{
2424
CommittorConfig, LedgerConfig, LoadableProgram, TaskSchedulerConfig,
2525
ValidatorConfig,
2626
},
27-
types::{resolve_url, BindAddress, RemoteConfig, RemoteKind},
27+
types::{network::Remote, BindAddress},
2828
};
2929

3030
/// Top-level configuration, assembled from multiple sources.
@@ -34,10 +34,9 @@ pub struct ValidatorParams {
3434
/// Path to the TOML configuration file (overrides CLI args).
3535
pub config: Option<PathBuf>,
3636

37-
/// Array-based remote configurations for RPC, WebSocket, and gRPC.
38-
/// Configured via [[remote]] sections in TOML (array-of-tables syntax).
39-
#[serde(default, rename = "remote")]
40-
pub remotes: Vec<RemoteConfig>,
37+
/// Remote endpoints for syncing with the base chain.
38+
/// Can include HTTP (for JSON-RPC), WebSocket (for PubSub), and gRPC (for streaming) connections.
39+
pub remotes: Vec<Remote>,
4140

4241
/// The application's operational mode.
4342
pub lifecycle: LifecycleMode,
@@ -174,44 +173,6 @@ impl ValidatorParams {
174173
.filter(|r| matches!(r, Remote::Grpc(_)))
175174
.map(|r| r.url_str())
176175
}
177-
178-
/// Returns the first RPC remote URL as an Option.
179-
pub fn rpc_url(&self) -> Option<&str> {
180-
self.remotes
181-
.iter()
182-
.find(|r| r.kind == RemoteKind::Rpc)
183-
.map(|r| r.url.as_str())
184-
}
185-
186-
/// Returns an iterator over all WebSocket remote URLs.
187-
pub fn websocket_urls(&self) -> impl Iterator<Item = &str> + '_ {
188-
self.remotes
189-
.iter()
190-
.filter(|r| r.kind == RemoteKind::Websocket)
191-
.map(|r| r.url.as_str())
192-
}
193-
194-
/// Returns an iterator over all gRPC remote URLs.
195-
pub fn grpc_urls(&self) -> impl Iterator<Item = &str> + '_ {
196-
self.remotes
197-
.iter()
198-
.filter(|r| r.kind == RemoteKind::Grpc)
199-
.map(|r| r.url.as_str())
200-
}
201-
202-
pub fn has_subscription_url(&self) -> bool {
203-
self.remotes.iter().any(|r| {
204-
r.kind == RemoteKind::Websocket || r.kind == RemoteKind::Grpc
205-
})
206-
}
207-
208-
/// Returns the RPC URL, using DEFAULT_REMOTE as fallback if not
209-
/// configured.
210-
pub fn rpc_url_or_default(&self) -> String {
211-
self.rpc_url().map(|s| s.to_string()).unwrap_or_else(|| {
212-
resolve_url(RemoteKind::Rpc, consts::DEFAULT_REMOTE)
213-
})
214-
}
215176
}
216177

217178
impl Display for ValidatorParams {

0 commit comments

Comments
 (0)