Skip to content

Commit dd675ea

Browse files
committed
perf(node): load prover indexes in parallel with bootstrap to reduce startup time
closes: #751
1 parent 5c54265 commit dd675ea

File tree

12 files changed

+92
-208
lines changed

12 files changed

+92
-208
lines changed

cli/src/commands/node/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,12 @@ impl Node {
226226

227227
if let Some(producer_key_path) = self.producer_key {
228228
let password = &self.producer_key_password;
229-
node::core::info!(node::core::log::system_time(); summary = "loading provers index");
230-
let provers = BlockProver::make(Some(block_verifier_index), Some(work_verifier_index));
231-
node::core::info!(node::core::log::system_time(); summary = "loaded provers index");
232-
node_builder.block_producer_from_file(provers, producer_key_path, password)?;
229+
openmina_core::thread::spawn(|| {
230+
node::core::info!(node::core::log::system_time(); summary = "loading provers index");
231+
BlockProver::make(Some(block_verifier_index), Some(work_verifier_index));
232+
node::core::info!(node::core::log::system_time(); summary = "loaded provers index");
233+
});
234+
node_builder.block_producer_from_file(producer_key_path, password, None)?;
233235

234236
if let Some(pub_key) = self.coinbase_receiver {
235237
node_builder

core/src/thread.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,24 @@ mod main_thread {
7373
}));
7474
rx.await.ok()
7575
}
76+
77+
pub fn run_async_fn_in_main_thread_blocking<F, FU, T>(f: F) -> Option<T>
78+
where
79+
T: 'static + Send,
80+
FU: Future<Output = T>,
81+
F: 'static + Send + FnOnce() -> FU,
82+
{
83+
let sender = MAIN_THREAD_TASK_SENDER
84+
.get()
85+
.expect("main thread not initialized");
86+
let (tx, rx) = oneshot::channel();
87+
let _ = sender.send(Box::pin(async move {
88+
wasm_bindgen_futures::spawn_local(async move {
89+
let _ = tx.send(f().await);
90+
})
91+
}));
92+
rx.blocking_recv().ok()
93+
}
7694
}
7795
#[cfg(target_family = "wasm")]
7896
pub use main_thread::*;

ledger/src/proofs/circuit_blobs.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ mod http {
3636
_get_bytes(url).await
3737
}
3838
}
39+
40+
pub fn get_bytes_blocking(url: &str) -> std::io::Result<Vec<u8>> {
41+
let url = url.to_owned();
42+
if thread::is_web_worker_thread() {
43+
thread::run_async_fn_in_main_thread_blocking(move || _get_bytes(url)).expect("failed to run task in the main thread! Maybe main thread crashed or not initialized?")
44+
} else {
45+
panic!("can't do blocking requests from main browser thread");
46+
}
47+
}
3948
}
4049

4150
#[cfg(not(target_family = "wasm"))]
@@ -53,7 +62,7 @@ fn git_release_url(filename: &impl AsRef<Path>) -> String {
5362
}
5463

5564
#[cfg(not(target_family = "wasm"))]
56-
pub fn fetch(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
65+
pub fn fetch_blocking(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
5766
use std::path::PathBuf;
5867

5968
fn try_base_dir<P: Into<PathBuf>>(base_dir: P, filename: &impl AsRef<Path>) -> Option<PathBuf> {
@@ -124,3 +133,11 @@ pub async fn fetch(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
124133
http::get_bytes(&url).await
125134
// http::get_bytes(&git_release_url(filename)).await
126135
}
136+
137+
#[cfg(target_family = "wasm")]
138+
pub fn fetch_blocking(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
139+
let prefix =
140+
option_env!("CIRCUIT_BLOBS_HTTP_PREFIX").unwrap_or("/assets/webnode/circuit-blobs");
141+
let url = format!("{prefix}/{}", filename.as_ref().to_str().unwrap());
142+
http::get_bytes_blocking(&url)
143+
}

ledger/src/proofs/provers.rs

Lines changed: 16 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,13 @@ fn decode_gates_file<F: FieldWitness>(
5050
Ok(data.gates)
5151
}
5252

53-
#[cfg(not(target_family = "wasm"))]
5453
fn read_gates_file<F: FieldWitness>(
5554
filename: &impl AsRef<Path>,
5655
) -> std::io::Result<Vec<CircuitGate<F>>> {
57-
let bytes = circuit_blobs::fetch(filename)?;
56+
let bytes = circuit_blobs::fetch_blocking(filename)?;
5857
decode_gates_file(bytes.as_slice())
5958
}
6059

61-
#[cfg(target_family = "wasm")]
62-
async fn read_gates_file<F: FieldWitness>(
63-
filepath: &impl AsRef<Path>,
64-
) -> std::io::Result<Vec<CircuitGate<F>>> {
65-
let resp = circuit_blobs::fetch(filepath).await?;
66-
decode_gates_file(&mut resp.as_slice())
67-
}
68-
69-
#[cfg(not(target_family = "wasm"))]
7060
fn make_gates<F: FieldWitness>(
7161
filename: &str,
7262
) -> (
@@ -88,30 +78,6 @@ fn make_gates<F: FieldWitness>(
8878
(internal_vars_path, rows_rev_path, gates)
8979
}
9080

91-
#[cfg(target_family = "wasm")]
92-
async fn make_gates<F: FieldWitness>(
93-
filename: &str,
94-
) -> (
95-
HashMap<usize, (Vec<(F, V)>, Option<F>)>,
96-
Vec<Vec<Option<V>>>,
97-
Vec<CircuitGate<F>>,
98-
) {
99-
let circuits_config = openmina_core::NetworkConfig::global().circuits_config;
100-
let base_dir = Path::new(circuits_config.directory_name);
101-
102-
let internal_vars_path = base_dir.join(format!("{}_internal_vars.bin", filename));
103-
let rows_rev_path = base_dir.join(format!("{}_rows_rev.bin", filename));
104-
let gates_path = base_dir.join(format!("{}_gates.json", filename));
105-
106-
let gates: Vec<CircuitGate<F>> = read_gates_file(&gates_path).await.unwrap();
107-
let (internal_vars_path, rows_rev_path) =
108-
read_constraints_data::<F>(&internal_vars_path, &rows_rev_path)
109-
.await
110-
.unwrap();
111-
112-
(internal_vars_path, rows_rev_path, gates)
113-
}
114-
11581
macro_rules! get_or_make {
11682
($constant: ident, $type: ty, $filename: expr) => {{
11783
get_or_make!($constant, $type, None, $filename)
@@ -121,13 +87,7 @@ macro_rules! get_or_make {
12187
return prover.clone();
12288
}
12389

124-
let (internal_vars, rows_rev, gates) = {
125-
#[cfg(not(target_family = "wasm"))]
126-
let res = make_gates($filename);
127-
#[cfg(target_family = "wasm")]
128-
let res = make_gates($filename).await;
129-
res
130-
};
90+
let (internal_vars, rows_rev, gates) = make_gates($filename);
13191

13292
let index = make_prover_index::<$type, _>(gates, $verifier_index);
13393
let prover = Prover {
@@ -153,7 +113,6 @@ fn default_circuits_config() -> &'static CircuitsConfig {
153113
openmina_core::NetworkConfig::global().circuits_config
154114
}
155115

156-
#[cfg(not(target_family = "wasm"))]
157116
mod prover_makers {
158117
use super::*;
159118

@@ -274,128 +233,6 @@ mod prover_makers {
274233
}
275234
}
276235

277-
#[cfg(target_family = "wasm")]
278-
mod prover_makers {
279-
use super::*;
280-
281-
async fn get_or_make_tx_step_prover(config: &CircuitsConfig) -> Arc<Prover<Fp>> {
282-
get_or_make!(
283-
TX_STEP_PROVER,
284-
StepTransactionProof,
285-
config.step_transaction_gates
286-
)
287-
}
288-
async fn get_or_make_tx_wrap_prover(
289-
config: &CircuitsConfig,
290-
verifier_index: Option<TransactionVerifier>,
291-
) -> Arc<Prover<Fq>> {
292-
get_or_make!(
293-
TX_WRAP_PROVER,
294-
WrapTransactionProof,
295-
verifier_index.map(Into::into),
296-
config.wrap_transaction_gates
297-
)
298-
}
299-
async fn get_or_make_merge_step_prover(config: &CircuitsConfig) -> Arc<Prover<Fp>> {
300-
get_or_make!(MERGE_STEP_PROVER, StepMergeProof, config.step_merge_gates)
301-
}
302-
async fn get_or_make_block_step_prover(config: &CircuitsConfig) -> Arc<Prover<Fp>> {
303-
get_or_make!(
304-
BLOCK_STEP_PROVER,
305-
StepBlockProof,
306-
config.step_blockchain_gates
307-
)
308-
}
309-
async fn get_or_make_block_wrap_prover(
310-
config: &CircuitsConfig,
311-
verifier_index: Option<BlockVerifier>,
312-
) -> Arc<Prover<Fq>> {
313-
get_or_make!(
314-
BLOCK_WRAP_PROVER,
315-
WrapBlockProof,
316-
verifier_index.map(Into::into),
317-
config.wrap_blockchain_gates
318-
)
319-
}
320-
async fn get_or_make_zkapp_step_opt_signed_opt_signed_prover(
321-
config: &CircuitsConfig,
322-
) -> Arc<Prover<Fp>> {
323-
get_or_make!(
324-
ZKAPP_STEP_OPT_SIGNED_OPT_SIGNED_PROVER,
325-
StepZkappOptSignedOptSignedProof,
326-
config.step_transaction_opt_signed_opt_signed_gates
327-
)
328-
}
329-
async fn get_or_make_zkapp_step_opt_signed_prover(config: &CircuitsConfig) -> Arc<Prover<Fp>> {
330-
get_or_make!(
331-
ZKAPP_STEP_OPT_SIGNED_PROVER,
332-
StepZkappOptSignedProof,
333-
config.step_transaction_opt_signed_gates
334-
)
335-
}
336-
async fn get_or_make_zkapp_step_proof_prover(config: &CircuitsConfig) -> Arc<Prover<Fp>> {
337-
get_or_make!(
338-
ZKAPP_STEP_PROOF_PROVER,
339-
StepZkappProvedProof,
340-
config.step_transaction_proved_gates
341-
)
342-
}
343-
344-
impl BlockProver {
345-
pub async fn make(
346-
block_verifier_index: Option<BlockVerifier>,
347-
tx_verifier_index: Option<TransactionVerifier>,
348-
) -> Self {
349-
let config = default_circuits_config();
350-
let block_step_prover = get_or_make_block_step_prover(config).await;
351-
let block_wrap_prover =
352-
get_or_make_block_wrap_prover(config, block_verifier_index).await;
353-
let tx_wrap_prover = get_or_make_tx_wrap_prover(config, tx_verifier_index).await;
354-
355-
Self {
356-
block_step_prover,
357-
block_wrap_prover,
358-
tx_wrap_prover,
359-
}
360-
}
361-
}
362-
363-
impl TransactionProver {
364-
pub async fn make(tx_verifier_index: Option<TransactionVerifier>) -> Self {
365-
let config = default_circuits_config();
366-
let tx_step_prover = get_or_make_tx_step_prover(config).await;
367-
let tx_wrap_prover = get_or_make_tx_wrap_prover(config, tx_verifier_index).await;
368-
let merge_step_prover = get_or_make_merge_step_prover(config).await;
369-
370-
Self {
371-
tx_step_prover,
372-
tx_wrap_prover,
373-
merge_step_prover,
374-
}
375-
}
376-
}
377-
378-
impl ZkappProver {
379-
pub async fn make(tx_verifier_index: Option<TransactionVerifier>) -> Self {
380-
let config = default_circuits_config();
381-
let tx_wrap_prover = get_or_make_tx_wrap_prover(config, tx_verifier_index).await;
382-
let merge_step_prover = get_or_make_merge_step_prover(config).await;
383-
let step_opt_signed_opt_signed_prover =
384-
get_or_make_zkapp_step_opt_signed_opt_signed_prover(config).await;
385-
let step_opt_signed_prover = get_or_make_zkapp_step_opt_signed_prover(config).await;
386-
let step_proof_prover = get_or_make_zkapp_step_proof_prover(config).await;
387-
388-
Self {
389-
tx_wrap_prover,
390-
merge_step_prover,
391-
step_opt_signed_opt_signed_prover,
392-
step_opt_signed_prover,
393-
step_proof_prover,
394-
}
395-
}
396-
}
397-
}
398-
399236
#[derive(Clone)]
400237
pub struct BlockProver {
401238
pub block_step_prover: Arc<Prover<Fp>>,
@@ -419,6 +256,18 @@ pub struct ZkappProver {
419256
pub step_proof_prover: Arc<Prover<Fp>>,
420257
}
421258

259+
impl BlockProver {
260+
/// Called must make sure that before calling this,
261+
/// `BlockProver::make` call was made.
262+
pub fn get_once_made() -> Self {
263+
Self {
264+
block_step_prover: BLOCK_STEP_PROVER.wait().clone(),
265+
block_wrap_prover: BLOCK_WRAP_PROVER.wait().clone(),
266+
tx_wrap_prover: TX_WRAP_PROVER.wait().clone(),
267+
}
268+
}
269+
}
270+
422271
fn decode_constraints_data<F: FieldWitness>(
423272
mut internal_vars: &[u8],
424273
mut rows_rev: &[u8],
@@ -471,31 +320,15 @@ fn decode_constraints_data<F: FieldWitness>(
471320
Some((internal_vars, rows_rev))
472321
}
473322

474-
#[cfg(not(target_family = "wasm"))]
475323
fn read_constraints_data<F: FieldWitness>(
476324
internal_vars_path: &Path,
477325
rows_rev_path: &Path,
478326
) -> Option<(InternalVars<F>, Vec<Vec<Option<V>>>)> {
479327
// ((Fp.t * V.t) list * Fp.t option)
480-
let internal_vars = circuit_blobs::fetch(&internal_vars_path).ok()?;
328+
let internal_vars = circuit_blobs::fetch_blocking(&internal_vars_path).ok()?;
481329

482330
// V.t option array list
483-
let rows_rev = circuit_blobs::fetch(&rows_rev_path).ok()?;
331+
let rows_rev = circuit_blobs::fetch_blocking(&rows_rev_path).ok()?;
484332

485333
decode_constraints_data(internal_vars.as_slice(), rows_rev.as_slice())
486334
}
487-
488-
#[cfg(target_family = "wasm")]
489-
async fn read_constraints_data<F: FieldWitness>(
490-
internal_vars_path: &Path,
491-
rows_rev_path: &Path,
492-
) -> Option<(InternalVars<F>, Vec<Vec<Option<V>>>)> {
493-
// ((Fp.t * V.t) list * Fp.t option)
494-
let internal_vars = circuit_blobs::fetch(&internal_vars_path).await.ok()?;
495-
496-
// V.t option array list
497-
let rows_rev = circuit_blobs::fetch(&rows_rev_path).await.ok()?;
498-
// std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("rows_rev.bin")).unwrap();
499-
500-
decode_constraints_data(internal_vars.as_ref(), rows_rev.as_ref())
501-
}

ledger/src/proofs/verifiers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ fn cache_path(kind: Kind) -> Option<PathBuf> {
7171
macro_rules! read_cache {
7272
($kind: expr, $digest: expr) => {{
7373
#[cfg(not(target_family = "wasm"))]
74-
let data = super::circuit_blobs::fetch(&cache_filename($kind))
74+
let data = super::circuit_blobs::fetch_blocking(&cache_filename($kind))
7575
.context("fetching verifier index failed")?;
7676
#[cfg(target_family = "wasm")]
7777
let data = super::circuit_blobs::fetch(&cache_filename($kind))

node/common/src/service/block_producer/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ use node::{
1616
use crate::EventSender;
1717

1818
pub struct BlockProducerService {
19-
provers: BlockProver,
19+
provers: Option<BlockProver>,
2020
keypair: AccountSecretKey,
2121
vrf_evaluation_sender: mpsc::UnboundedSender<VrfEvaluatorInput>,
2222
}
2323

2424
impl BlockProducerService {
2525
pub fn new(
26-
provers: BlockProver,
2726
keypair: AccountSecretKey,
2827
vrf_evaluation_sender: mpsc::UnboundedSender<VrfEvaluatorInput>,
28+
provers: Option<BlockProver>,
2929
) -> Self {
3030
Self {
3131
provers,
@@ -35,9 +35,9 @@ impl BlockProducerService {
3535
}
3636

3737
pub fn start(
38-
provers: BlockProver,
3938
event_sender: EventSender,
4039
keypair: AccountSecretKey,
40+
provers: Option<BlockProver>,
4141
) -> Self {
4242
let (vrf_evaluation_sender, vrf_evaluation_receiver) =
4343
mpsc::unbounded_channel::<VrfEvaluatorInput>();
@@ -54,7 +54,7 @@ impl BlockProducerService {
5454
})
5555
.unwrap();
5656

57-
BlockProducerService::new(provers, keypair, vrf_evaluation_sender)
57+
BlockProducerService::new(keypair, vrf_evaluation_sender, provers)
5858
}
5959

6060
pub fn keypair(&self) -> AccountSecretKey {
@@ -103,6 +103,7 @@ impl node::service::BlockProducerService for crate::NodeService {
103103
.expect("provers shouldn't be needed if block producer isn't initialized")
104104
.provers
105105
.clone()
106+
.unwrap_or_else(|| BlockProver::get_once_made())
106107
}
107108

108109
fn prove(&mut self, block_hash: StateHash, input: Box<ProverExtendBlockchainInputStableV2>) {

0 commit comments

Comments
 (0)