Skip to content

Commit 42fbe3b

Browse files
avi-starkwareclaude
andcommitted
starknet_transaction_prover: add TLS integration tests
Add 5 tests for the TLS server module: - HTTPS request succeeds with self-signed cert (specVersion) - Plain HTTP to TLS server fails (protocol mismatch) - Missing cert/key file produces error - Invalid PEM content produces error Uses rcgen (new workspace dev-dep) to generate self-signed certs at test time, avoiding committed certificate fixtures that expire. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 49a3322 commit 42fbe3b

File tree

5 files changed

+140
-1
lines changed

5 files changed

+140
-1
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ rand_distr = "0.4.3"
340340
reed-solomon-simd = "3.1.0"
341341
regex = "1.10.4"
342342
replace_with = "0.1.7"
343+
rcgen = "0.13"
343344
reqwest = "0.12"
344345
reqwest-middleware = "0.4.2"
345346
reqwest-retry = "0.7.0"

crates/starknet_transaction_prover/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ blockifier_test_utils.workspace = true
5858
jsonschema.workspace = true
5959
mockito.workspace = true
6060
privacy-circuit-verify.workspace = true
61+
rcgen.workspace = true
6162
reqwest.workspace = true
6263
rstest.workspace = true
6364
serde = { workspace = true, features = ["derive"] }

crates/starknet_transaction_prover/src/server/tls.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
//! TLS helpers for serving JSON-RPC over HTTPS.
22
3+
#[cfg(test)]
4+
#[path = "tls_test.rs"]
5+
mod tls_test;
6+
37
use std::net::SocketAddr;
48
use std::path::Path;
59
use std::sync::Arc;
@@ -115,7 +119,7 @@ pub async fn start_tls_server(
115119
}
116120

117121
/// Loads a certificate chain and private key from PEM files and builds a TLS acceptor.
118-
fn load_tls_acceptor(cert_path: &Path, key_path: &Path) -> anyhow::Result<TlsAcceptor> {
122+
pub(crate) fn load_tls_acceptor(cert_path: &Path, key_path: &Path) -> anyhow::Result<TlsAcceptor> {
119123
let cert_pem = std::fs::read(cert_path)
120124
.with_context(|| format!("Failed to read TLS certificate file: {}", cert_path.display()))?;
121125
let cert_chain: Vec<CertificateDer<'static>> =
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use std::io::Write;
2+
use std::net::SocketAddr;
3+
4+
use serde_json::Value;
5+
use tempfile::NamedTempFile;
6+
7+
use crate::server::mock_rpc::MockProvingRpc;
8+
use crate::server::rpc_api::ProvingRpcServer;
9+
use crate::server::rpc_impl::SPEC_VERSION;
10+
use crate::server::tls::{load_tls_acceptor, start_tls_server};
11+
12+
/// Installs the default rustls crypto provider (aws-lc-rs) if not already installed.
13+
/// Required by reqwest when using rustls-based TLS.
14+
fn ensure_crypto_provider() {
15+
let _ = tokio_rustls::rustls::crypto::aws_lc_rs::default_provider().install_default();
16+
}
17+
18+
/// Generates a self-signed certificate and private key using rcgen.
19+
/// Returns (cert_pem, key_pem) as byte vectors.
20+
fn generate_self_signed_cert() -> (Vec<u8>, Vec<u8>) {
21+
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])
22+
.expect("Failed to generate self-signed certificate");
23+
let cert_pem = cert.cert.pem().into_bytes();
24+
let key_pem = cert.key_pair.serialize_pem().into_bytes();
25+
(cert_pem, key_pem)
26+
}
27+
28+
/// Writes PEM bytes to a temporary file and returns the handle.
29+
fn write_pem_to_tempfile(pem_bytes: &[u8]) -> NamedTempFile {
30+
let mut file = NamedTempFile::new().expect("Failed to create temp file");
31+
file.write_all(pem_bytes).expect("Failed to write PEM");
32+
file.flush().expect("Failed to flush PEM file");
33+
file
34+
}
35+
36+
/// Starts a TLS server with mock RPC methods, returns (addr, server_handle, cert_pem).
37+
async fn start_test_tls_server() -> (SocketAddr, jsonrpsee::server::ServerHandle, Vec<u8>) {
38+
let (cert_pem, key_pem) = generate_self_signed_cert();
39+
let cert_file = write_pem_to_tempfile(&cert_pem);
40+
let key_file = write_pem_to_tempfile(&key_pem);
41+
42+
let methods = MockProvingRpc::from_expected_json().into_rpc();
43+
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
44+
45+
let (local_addr, handle) = start_tls_server(
46+
addr,
47+
cert_file.path(),
48+
key_file.path(),
49+
methods,
50+
10, // max_connections
51+
None,
52+
)
53+
.await
54+
.expect("Failed to start TLS server");
55+
56+
(local_addr, handle, cert_pem)
57+
}
58+
59+
#[tokio::test]
60+
async fn test_https_spec_version_succeeds() {
61+
ensure_crypto_provider();
62+
let (addr, handle, cert_pem) = start_test_tls_server().await;
63+
64+
let cert = reqwest::tls::Certificate::from_pem(&cert_pem)
65+
.expect("Failed to parse certificate for reqwest");
66+
let client = reqwest::Client::builder()
67+
.add_root_certificate(cert)
68+
.build()
69+
.expect("Failed to build HTTPS client");
70+
71+
let body = serde_json::json!({
72+
"jsonrpc": "2.0",
73+
"id": "1",
74+
"method": "starknet_specVersion"
75+
});
76+
77+
let response = client
78+
.post(format!("https://localhost:{}", addr.port()))
79+
.json(&body)
80+
.send()
81+
.await
82+
.expect("HTTPS request failed");
83+
84+
assert_eq!(response.status(), 200);
85+
86+
let json: Value = response.json().await.expect("Failed to parse response JSON");
87+
assert_eq!(json["result"].as_str().unwrap(), SPEC_VERSION);
88+
89+
handle.stop().expect("Failed to stop server");
90+
}
91+
92+
#[tokio::test]
93+
async fn test_http_to_tls_server_fails() {
94+
ensure_crypto_provider();
95+
let (addr, handle, _cert_pem) = start_test_tls_server().await;
96+
97+
let client = reqwest::Client::new();
98+
let body = serde_json::json!({
99+
"jsonrpc": "2.0",
100+
"id": "1",
101+
"method": "starknet_specVersion"
102+
});
103+
104+
// Plain HTTP to a TLS server should fail (connection or protocol error).
105+
let result = client.post(format!("http://localhost:{}", addr.port())).json(&body).send().await;
106+
107+
assert!(result.is_err(), "Expected HTTP to TLS server to fail, but got: {result:?}");
108+
109+
handle.stop().expect("Failed to stop server");
110+
}
111+
112+
#[test]
113+
fn test_load_tls_acceptor_missing_cert_file() {
114+
let key_file = write_pem_to_tempfile(b"dummy key content");
115+
let result = load_tls_acceptor("/nonexistent/cert.pem".as_ref(), key_file.path());
116+
assert!(result.is_err(), "Expected error for missing cert file");
117+
}
118+
119+
#[test]
120+
fn test_load_tls_acceptor_missing_key_file() {
121+
let cert_file = write_pem_to_tempfile(b"dummy cert content");
122+
let result = load_tls_acceptor(cert_file.path(), "/nonexistent/key.pem".as_ref());
123+
assert!(result.is_err(), "Expected error for missing key file");
124+
}
125+
126+
#[test]
127+
fn test_load_tls_acceptor_invalid_pem() {
128+
let cert_file = write_pem_to_tempfile(b"not a valid PEM certificate");
129+
let key_file = write_pem_to_tempfile(b"not a valid PEM key");
130+
let result = load_tls_acceptor(cert_file.path(), key_file.path());
131+
assert!(result.is_err(), "Expected error for invalid PEM content");
132+
}

0 commit comments

Comments
 (0)