Skip to content

Commit

Permalink
Merge branch 'next' into santiagopittella-use-remote-batch-prover
Browse files Browse the repository at this point in the history
  • Loading branch information
SantiagoPittella committed Mar 5, 2025
2 parents 0d4e24b + 3c5f411 commit f0d8d4d
Show file tree
Hide file tree
Showing 14 changed files with 972 additions and 90 deletions.
552 changes: 529 additions & 23 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ version = "0.8.0"

[workspace.dependencies]
assert_matches = { version = "1.5" }
http = { version = "1.2" }
itertools = { version = "0.14" }
miden-air = { version = "0.12" }
miden-lib = { git = "https://github.com/0xPolygonMiden/miden-base", branch = "next" }
Expand All @@ -44,6 +45,8 @@ thiserror = { version = "2.0", default-features = false }
tokio = { version = "1.40", features = ["rt-multi-thread"] }
tokio-stream = { version = "0.1" }
tonic = { version = "0.12" }
tower = { version = "0.5" }
tower-http = { version = "0.6", features = ["trace"] }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] }
url = { version = "2.5", features = ["serde"] }
Expand Down
10 changes: 8 additions & 2 deletions bin/faucet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs"] }
toml = { version = "0.8" }
tonic = { workspace = true }
tower = "0.5"
tower-http = { version = "0.6", features = ["cors", "set-header", "trace"] }
tower = { workspace = true }
tower-http = { workspace = true, features = ["cors", "set-header", "trace"] }
tracing = { workspace = true }
url = { workspace = true }

[build-dependencies]
# Required to inject build metadata.
miden-node-utils = { workspace = true, features = ["vergen"] }
static-files = "0.2"

[dev-dependencies]
fantoccini = { version = "0.21" }
serde_json = { version = "1.0" }
tokio-stream = { workspace = true, features = ["net"] }
tonic-web = { version = "0.12" }
135 changes: 131 additions & 4 deletions bin/faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ mod handlers;
mod state;
mod store;

#[cfg(test)]
mod stub_rpc_api;

use std::path::PathBuf;

use anyhow::Context;
Expand Down Expand Up @@ -96,11 +99,14 @@ async fn main() -> anyhow::Result<()> {

let cli = Cli::parse();

run_faucet_command(cli).await
}

async fn run_faucet_command(cli: Cli) -> anyhow::Result<()> {
match &cli.command {
Command::Start { config } => {
let config: FaucetConfig =
load_config(config).context("failed to load configuration file")?;

let faucet_state = FaucetState::new(config.clone()).await?;

info!(target: COMPONENT, %config, "Initializing server");
Expand Down Expand Up @@ -128,9 +134,12 @@ async fn main() -> anyhow::Result<()> {
)
.with_state(faucet_state);

let socket_addr = config.endpoint.socket_addrs(|| None)?.into_iter().next().ok_or(
anyhow::anyhow!("Couldn't get any socket addrs for endpoint: {}", config.endpoint),
)?;
let socket_addr = config
.endpoint
.socket_addrs(|| None)?
.into_iter()
.next()
.with_context(|| format!("no sockets available on {}", config.endpoint))?;
let listener =
TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?;

Expand Down Expand Up @@ -229,3 +238,121 @@ fn long_version() -> LongVersion {
debug: option_env!("VERGEN_CARGO_DEBUG").unwrap_or_default(),
}
}

#[cfg(test)]
mod test {
use std::{
env::temp_dir,
io::{BufRead, BufReader},
process::{Command, Stdio},
str::FromStr,
};

use fantoccini::ClientBuilder;
use serde_json::{json, Map};
use url::Url;

use crate::{config::FaucetConfig, run_faucet_command, stub_rpc_api::serve_stub, Cli};

/// This test starts a stub node, a faucet connected to the stub node, and a chromedriver
/// to test the faucet website. It then loads the website and checks that all the requests
/// made return status 200.
#[tokio::test]
async fn test_website() {
let stub_node_url = Url::from_str("http://localhost:50051").unwrap();

// Start the stub node
tokio::spawn({
let stub_node_url = stub_node_url.clone();
async move { serve_stub(&stub_node_url).await.unwrap() }
});

let config_path = temp_dir().join("faucet.toml");
let faucet_account_path = temp_dir().join("account.mac");

// Create config
let config = FaucetConfig {
node_url: stub_node_url,
faucet_account_path: faucet_account_path.clone(),
..FaucetConfig::default()
};
let config_as_toml_string = toml::to_string(&config).unwrap();
std::fs::write(&config_path, config_as_toml_string).unwrap();

// Create faucet account
run_faucet_command(Cli {
command: crate::Command::CreateFaucetAccount {
config_path: config_path.clone(),
output_path: faucet_account_path.clone(),
token_symbol: "TEST".to_string(),
decimals: 2,
max_supply: 1000,
},
})
.await
.unwrap();

// Start the faucet connected to the stub
let website_url = config.endpoint.clone();
tokio::spawn(async move {
run_faucet_command(Cli {
command: crate::Command::Start { config: config_path },
})
.await
.unwrap();
});

// Start chromedriver. This requires having chromedriver and chrome installed
let chromedriver_port = "57709";
#[expect(clippy::zombie_processes)]
let mut chromedriver = Command::new("chromedriver")
.arg(format!("--port={chromedriver_port}"))
.stdout(Stdio::piped())
.spawn()
.expect("failed to start chromedriver");
// Wait for chromedriver to be running
let stdout = chromedriver.stdout.take().unwrap();
for line in BufReader::new(stdout).lines() {
if line.unwrap().contains("ChromeDriver was started successfully") {
break;
}
}

// Start fantoccini client
let client = ClientBuilder::native()
.capabilities(
[(
"goog:chromeOptions".to_string(),
json!({"args": ["--headless", "--disable-gpu", "--no-sandbox"]}),
)]
.into_iter()
.collect::<Map<_, _>>(),
)
.connect(&format!("http://localhost:{chromedriver_port}"))
.await
.expect("failed to connect to WebDriver");

// Open the website
client.goto(website_url.as_str()).await.unwrap();

let title = client.title().await.unwrap();
assert_eq!(title, "Miden Faucet");

// Execute a script to get all the failed requests
let script = r"
let errors = [];
performance.getEntriesByType('resource').forEach(entry => {
if (entry.responseStatus && entry.responseStatus >= 400) {
errors.push({url: entry.name, status: entry.responseStatus});
}
});
return errors;
";
let failed_requests = client.execute(script, vec![]).await.unwrap();
assert!(failed_requests.as_array().unwrap().is_empty());

// Close the client and kill chromedriver
client.close().await.unwrap();
chromedriver.kill().unwrap();
}
}
166 changes: 166 additions & 0 deletions bin/faucet/src/stub_rpc_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use miden_node_proto::generated::{
block::BlockHeader,
digest::Digest,
requests::{
CheckNullifiersByPrefixRequest, CheckNullifiersRequest, GetAccountDetailsRequest,
GetAccountProofsRequest, GetAccountStateDeltaRequest, GetBlockByNumberRequest,
GetBlockHeaderByNumberRequest, GetNotesByIdRequest, SubmitProvenTransactionRequest,
SyncNoteRequest, SyncStateRequest,
},
responses::{
CheckNullifiersByPrefixResponse, CheckNullifiersResponse, GetAccountDetailsResponse,
GetAccountProofsResponse, GetAccountStateDeltaResponse, GetBlockByNumberResponse,
GetBlockHeaderByNumberResponse, GetNotesByIdResponse, SubmitProvenTransactionResponse,
SyncNoteResponse, SyncStateResponse,
},
rpc::api_server,
};
use miden_node_utils::errors::ApiError;
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
use tonic::{Request, Response, Status};
use url::Url;

#[derive(Clone)]
pub struct StubRpcApi;

#[tonic::async_trait]
impl api_server::Api for StubRpcApi {
async fn check_nullifiers(
&self,
_request: Request<CheckNullifiersRequest>,
) -> Result<Response<CheckNullifiersResponse>, Status> {
unimplemented!();
}

async fn check_nullifiers_by_prefix(
&self,
_request: Request<CheckNullifiersByPrefixRequest>,
) -> Result<Response<CheckNullifiersByPrefixResponse>, Status> {
unimplemented!();
}

async fn get_block_header_by_number(
&self,
_request: Request<GetBlockHeaderByNumberRequest>,
) -> Result<Response<GetBlockHeaderByNumberResponse>, Status> {
// Values are taken from the default genesis block as at v0.7
Ok(Response::new(GetBlockHeaderByNumberResponse {
block_header: Some(BlockHeader {
version: 1,
prev_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }),
block_num: 0,
chain_root: Some(Digest {
d0: 0x9729_9D39_2DA8_DC69,
d1: 0x674_44AF_6294_0719,
d2: 0x7B97_0BC7_07A0_F7D6,
d3: 0xE423_8D7C_78F3_9D8B,
}),
account_root: Some(Digest {
d0: 0x9666_5D75_8487_401A,
d1: 0xB7BF_DF8B_379F_ED71,
d2: 0xFCA7_82CB_2406_2222,
d3: 0x8D0C_B80F_6377_4E9A,
}),
nullifier_root: Some(Digest {
d0: 0xD4A0_CFF6_578C_123E,
d1: 0xF11A_1794_8930_B14A,
d2: 0xD128_DD2A_4213_B53C,
d3: 0x2DF8_FE54_F23F_6B91,
}),
note_root: Some(Digest {
d0: 0x93CE_DDC8_A187_24FE,
d1: 0x4E32_9917_2E91_30ED,
d2: 0x8022_9E0E_1808_C860,
d3: 0x13F4_7934_7EB7_FD78,
}),
tx_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }),
kernel_root: Some(Digest {
d0: 0x7B6F_43E5_2910_C8C3,
d1: 0x99B3_2868_577E_5779,
d2: 0xAF9E_6424_57CD_B8C1,
d3: 0xB1DD_E61B_F983_2DBD,
}),
proof_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }),
timestamp: 0x63B0_CD00,
}),
mmr_path: None,
chain_length: None,
}))
}

async fn sync_state(
&self,
_request: Request<SyncStateRequest>,
) -> Result<Response<SyncStateResponse>, Status> {
unimplemented!();
}

async fn sync_notes(
&self,
_request: Request<SyncNoteRequest>,
) -> Result<Response<SyncNoteResponse>, Status> {
unimplemented!();
}

async fn get_notes_by_id(
&self,
_request: Request<GetNotesByIdRequest>,
) -> Result<Response<GetNotesByIdResponse>, Status> {
unimplemented!();
}

async fn submit_proven_transaction(
&self,
_request: Request<SubmitProvenTransactionRequest>,
) -> Result<Response<SubmitProvenTransactionResponse>, Status> {
Ok(Response::new(SubmitProvenTransactionResponse { block_height: 0 }))
}

async fn get_account_details(
&self,
_request: Request<GetAccountDetailsRequest>,
) -> Result<Response<GetAccountDetailsResponse>, Status> {
Err(Status::not_found("account not found"))
}

async fn get_block_by_number(
&self,
_request: Request<GetBlockByNumberRequest>,
) -> Result<Response<GetBlockByNumberResponse>, Status> {
unimplemented!()
}

async fn get_account_state_delta(
&self,
_request: Request<GetAccountStateDeltaRequest>,
) -> Result<Response<GetAccountStateDeltaResponse>, Status> {
unimplemented!()
}

async fn get_account_proofs(
&self,
_request: Request<GetAccountProofsRequest>,
) -> Result<Response<GetAccountProofsResponse>, Status> {
unimplemented!()
}
}

pub async fn serve_stub(endpoint: &Url) -> Result<(), ApiError> {
let addr = endpoint
.socket_addrs(|| None)
.map_err(ApiError::EndpointToSocketFailed)?
.into_iter()
.next()
.unwrap();

let listener = TcpListener::bind(addr).await?;
let api_service = api_server::ApiServer::new(StubRpcApi);

tonic::transport::Server::builder()
.accept_http1(true)
.add_service(tonic_web::enable(api_service))
.serve_with_incoming(TcpListenerStream::new(listener))
.await
.map_err(ApiError::ApiServeFailed)
}
17 changes: 9 additions & 8 deletions crates/block-producer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ miden-proving-service-client = { git = "https://github.com/0xPolygonMiden/miden-
] }
miden-tx = { workspace = true }
miden-tx-batch-prover = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "next" }
rand = { version = "0.8" }
serde = { version = "1.0", features = ["derive"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "net", "rt-multi-thread", "sync", "time"] }
tokio-stream = { workspace = true, features = ["net"] }
tonic = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }
rand = { version = "0.8" }
serde = { version = "1.0", features = ["derive"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "net", "rt-multi-thread", "sync", "time"] }
tokio-stream = { workspace = true, features = ["net"] }
tonic = { workspace = true, features = ["transport"] }
tower-http = { workspace = true, features = ["util"] }
tracing = { workspace = true }
url = { workspace = true }

[dev-dependencies]
assert_matches = { workspace = true }
Expand Down
Loading

0 comments on commit f0d8d4d

Please sign in to comment.