Skip to content

Commit 894ff0e

Browse files
tac0turtledamiannolantac0turtle
authored
chore: split rust lib (#2445)
<!-- 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced a builder pattern for flexible client configuration, including custom timeouts and TLS options. * Added conditional feature flags for modular gRPC and transport support. * Provided message-only and full gRPC service code generation, selectable via features. * Expanded documentation with usage examples, feature explanations, and concurrency guidance. * **Improvements** * All client service methods are now thread-safe and support concurrent usage without requiring mutable references. * Enhanced test coverage to verify feature-flagged type availability. * **Bug Fixes** * Clarified and updated Store Service API method names and signatures for consistency. * **Documentation** * Updated and expanded READMEs with detailed usage, features, and internal workings. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Damian Nolan <damiannolan@gmail.com> Co-authored-by: tac0turtle <you@example.com>
1 parent bd856b7 commit 894ff0e

File tree

15 files changed

+713
-76
lines changed

15 files changed

+713
-76
lines changed

client/crates/rollkit-client/README.md

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,59 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4040
let client = RollkitClient::connect("http://localhost:50051").await?;
4141

4242
// Check health
43-
let mut health = HealthClient::new(&client);
43+
let health = HealthClient::new(&client);
4444
let is_healthy = health.is_healthy().await?;
4545
println!("Node healthy: {}", is_healthy);
4646

4747
Ok(())
4848
}
4949
```
5050

51-
### Advanced Configuration
51+
### Using the Builder Pattern
5252

5353
```rust
5454
use rollkit_client::RollkitClient;
5555
use std::time::Duration;
5656

57+
// Create a client with custom timeouts
58+
let client = RollkitClient::builder()
59+
.endpoint("http://localhost:50051")
60+
.timeout(Duration::from_secs(30))
61+
.connect_timeout(Duration::from_secs(10))
62+
.build()
63+
.await?;
64+
```
65+
66+
### TLS Configuration
67+
68+
```rust
69+
use rollkit_client::{RollkitClient, ClientTlsConfig};
70+
71+
// Enable TLS with default configuration
72+
let client = RollkitClient::builder()
73+
.endpoint("https://secure-node.rollkit.dev")
74+
.tls()
75+
.build()
76+
.await?;
77+
78+
// Or with custom TLS configuration
79+
let tls_config = ClientTlsConfig::new()
80+
.domain_name("secure-node.rollkit.dev");
81+
82+
let client = RollkitClient::builder()
83+
.endpoint("https://secure-node.rollkit.dev")
84+
.tls_config(tls_config)
85+
.build()
86+
.await?;
87+
```
88+
89+
### Legacy Connection Method
90+
91+
```rust
92+
use rollkit_client::RollkitClient;
93+
use std::time::Duration;
94+
95+
// Still supported for backward compatibility
5796
let client = RollkitClient::connect_with_config(
5897
"http://localhost:50051",
5998
|endpoint| {
@@ -67,7 +106,7 @@ let client = RollkitClient::connect_with_config(
67106

68107
## Services
69108

70-
The client provides wrappers for all Rollkit gRPC services:
109+
The client provides wrappers for all Rollkit gRPC services. All service methods are now thread-safe and can be called concurrently:
71110

72111
### Health Service
73112

@@ -86,9 +125,10 @@ The client provides wrappers for all Rollkit gRPC services:
86125

87126
### Store Service
88127

89-
- `get_block(height)` - Get a block by height
90-
- `get_state(height)` - Get state at a specific height
91-
- `get_metadata(initial_height, latest_height)` - Get metadata for a height range
128+
- `get_block_by_height(height)` - Get a block by height
129+
- `get_block_by_hash(hash)` - Get a block by hash
130+
- `get_state()` - Get the current state
131+
- `get_metadata(key)` - Get metadata by key
92132

93133
## Examples
94134

@@ -98,6 +138,38 @@ See the `examples` directory for more detailed usage examples:
98138
cargo run --example basic
99139
```
100140

141+
### Concurrent Usage Example
142+
143+
```rust
144+
use rollkit_client::{RollkitClient, StoreClient};
145+
use tokio::task;
146+
147+
#[tokio::main]
148+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
149+
let client = RollkitClient::connect("http://localhost:50051").await?;
150+
let store = StoreClient::new(&client);
151+
152+
// Service clients can be used concurrently
153+
let mut handles = vec![];
154+
155+
for height in 0..10 {
156+
let store_clone = store.clone();
157+
let handle = task::spawn(async move {
158+
store_clone.get_block_by_height(height).await
159+
});
160+
handles.push(handle);
161+
}
162+
163+
// Wait for all tasks to complete
164+
for handle in handles {
165+
let result = handle.await??;
166+
println!("Got block: {:?}", result);
167+
}
168+
169+
Ok(())
170+
}
171+
```
172+
101173
## Error Handling
102174

103175
All methods return `Result<T, RollkitClientError>` where `RollkitClientError` encompasses:

client/crates/rollkit-client/examples/basic.rs

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rollkit_client::{HealthClient, P2PClient, RollkitClient, SignerClient, StoreClient};
1+
use rollkit_client::{HealthClient, P2PClient, RollkitClient, StoreClient};
22
use std::error::Error;
33

44
#[tokio::main]
@@ -16,7 +16,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
1616

1717
// Check health status
1818
println!("\n=== Health Check ===");
19-
let mut health = HealthClient::new(&client);
19+
let health = HealthClient::new(&client);
2020
match health.get_health().await {
2121
Ok(health_response) => {
2222
println!("Health status: {:?}", health_response.status());
@@ -27,7 +27,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
2727

2828
// Get P2P information
2929
println!("\n=== P2P Information ===");
30-
let mut p2p = P2PClient::new(&client);
30+
let p2p = P2PClient::new(&client);
3131
match p2p.get_net_info().await {
3232
Ok(net_info) => {
3333
println!("Network ID: {}", net_info.id);
@@ -47,29 +47,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
4747
Err(e) => println!("Failed to get peer info: {e}"),
4848
}
4949

50-
// Get signer information
51-
println!("\n=== Signer Information ===");
52-
let mut signer = SignerClient::new(&client);
53-
match signer.get_public_key().await {
54-
Ok(pubkey) => {
55-
println!("Public key (hex): {}", hex::encode(&pubkey));
56-
}
57-
Err(e) => println!("Failed to get public key: {e}"),
58-
}
59-
60-
// Example: Sign a message
61-
let message = b"Hello, Rollkit!";
62-
match signer.sign(message.to_vec()).await {
63-
Ok(signature) => {
64-
println!("Message signed successfully");
65-
println!("Signature (hex): {}", hex::encode(&signature));
66-
}
67-
Err(e) => println!("Failed to sign message: {e}"),
68-
}
69-
7050
// Get store information
7151
println!("\n=== Store Information ===");
72-
let mut store = StoreClient::new(&client);
52+
let store = StoreClient::new(&client);
7353

7454
// Try to get the latest block (height 0 for genesis)
7555
match store.get_block_by_height(0).await {

client/crates/rollkit-client/src/client.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
use crate::error::{Result, RollkitClientError};
22
use std::time::Duration;
3-
use tonic::transport::{Channel, Endpoint};
3+
use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
44

55
#[derive(Clone, Debug)]
66
pub struct RollkitClient {
77
channel: Channel,
88
endpoint: String,
99
}
1010

11+
/// Builder for configuring a RollkitClient
12+
#[derive(Debug)]
13+
pub struct RollkitClientBuilder {
14+
endpoint: String,
15+
timeout: Option<Duration>,
16+
connect_timeout: Option<Duration>,
17+
tls_config: Option<ClientTlsConfig>,
18+
}
19+
1120
impl RollkitClient {
1221
/// Create a new RollkitClient with the given endpoint
1322
pub async fn connect(endpoint: impl Into<String>) -> Result<Self> {
@@ -17,6 +26,16 @@ impl RollkitClient {
1726
Ok(Self { channel, endpoint })
1827
}
1928

29+
/// Create a new RollkitClient builder
30+
pub fn builder() -> RollkitClientBuilder {
31+
RollkitClientBuilder {
32+
endpoint: String::new(),
33+
timeout: None,
34+
connect_timeout: None,
35+
tls_config: None,
36+
}
37+
}
38+
2039
/// Create a new RollkitClient with custom channel configuration
2140
pub async fn connect_with_config<F>(endpoint: impl Into<String>, config: F) -> Result<Self>
2241
where
@@ -62,3 +81,77 @@ impl RollkitClient {
6281
Ok(channel)
6382
}
6483
}
84+
85+
impl RollkitClientBuilder {
86+
/// Set the endpoint URL
87+
pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
88+
self.endpoint = endpoint.into();
89+
self
90+
}
91+
92+
/// Set the request timeout
93+
pub fn timeout(mut self, timeout: Duration) -> Self {
94+
self.timeout = Some(timeout);
95+
self
96+
}
97+
98+
/// Set the connection timeout
99+
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
100+
self.connect_timeout = Some(timeout);
101+
self
102+
}
103+
104+
/// Enable TLS with default configuration
105+
pub fn tls(mut self) -> Self {
106+
self.tls_config = Some(ClientTlsConfig::new());
107+
self
108+
}
109+
110+
/// Set custom TLS configuration
111+
pub fn tls_config(mut self, config: ClientTlsConfig) -> Self {
112+
self.tls_config = Some(config);
113+
self
114+
}
115+
116+
/// Build the RollkitClient
117+
pub async fn build(self) -> Result<RollkitClient> {
118+
if self.endpoint.is_empty() {
119+
return Err(RollkitClientError::InvalidEndpoint(
120+
"Endpoint cannot be empty".to_string(),
121+
));
122+
}
123+
124+
let endpoint = Endpoint::from_shared(self.endpoint.clone())
125+
.map_err(|e| RollkitClientError::InvalidEndpoint(e.to_string()))?;
126+
127+
// Apply timeout configurations
128+
let endpoint = if let Some(timeout) = self.timeout {
129+
endpoint.timeout(timeout)
130+
} else {
131+
endpoint.timeout(Duration::from_secs(10))
132+
};
133+
134+
let endpoint = if let Some(connect_timeout) = self.connect_timeout {
135+
endpoint.connect_timeout(connect_timeout)
136+
} else {
137+
endpoint.connect_timeout(Duration::from_secs(5))
138+
};
139+
140+
// Apply TLS configuration if provided
141+
let endpoint = if let Some(tls_config) = self.tls_config {
142+
endpoint.tls_config(tls_config)?
143+
} else {
144+
endpoint
145+
};
146+
147+
let channel = endpoint
148+
.connect()
149+
.await
150+
.map_err(RollkitClientError::Transport)?;
151+
152+
Ok(RollkitClient {
153+
channel,
154+
endpoint: self.endpoint,
155+
})
156+
}
157+
}

client/crates/rollkit-client/src/health.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,23 @@ impl HealthClient {
1616
}
1717

1818
/// Check if the node is alive and get its health status
19-
pub async fn livez(&mut self) -> Result<HealthStatus> {
19+
pub async fn livez(&self) -> Result<HealthStatus> {
2020
let request = Request::new(());
21-
let response = self.inner.livez(request).await?;
21+
let response = self.inner.clone().livez(request).await?;
2222

2323
Ok(response.into_inner().status())
2424
}
2525

2626
/// Get the full health response
27-
pub async fn get_health(&mut self) -> Result<GetHealthResponse> {
27+
pub async fn get_health(&self) -> Result<GetHealthResponse> {
2828
let request = Request::new(());
29-
let response = self.inner.livez(request).await?;
29+
let response = self.inner.clone().livez(request).await?;
3030

3131
Ok(response.into_inner())
3232
}
3333

3434
/// Check if the node is healthy (status is PASS)
35-
pub async fn is_healthy(&mut self) -> Result<bool> {
35+
pub async fn is_healthy(&self) -> Result<bool> {
3636
let status = self.livez().await?;
3737
Ok(status == HealthStatus::Pass)
3838
}

0 commit comments

Comments
 (0)