Skip to main content

mina_node_common/service/
snarks.rs

1use std::sync::Arc;
2
3use ledger::{
4    scan_state::{
5        scan_state::transaction_snark::{SokDigest, Statement},
6        transaction_logic::WithStatus,
7    },
8    transaction_pool::{TransactionError, TransactionPoolErrors},
9};
10use mina_node::{
11    core::{
12        channels::mpsc,
13        snark::{Snark, SnarkJobId},
14        thread,
15    },
16    snark::{
17        block_verify::{SnarkBlockVerifyError, SnarkBlockVerifyId, VerifiableBlockWithHash},
18        work_verify::{SnarkWorkVerifyError, SnarkWorkVerifyId},
19        BlockVerifier, SnarkEvent, TransactionVerifier, VerifierSRS,
20    },
21};
22use mina_p2p_messages::{bigint::InvalidBigInt, v2};
23use rand::prelude::*;
24
25use crate::NodeService;
26
27use super::EventSender;
28
29pub struct SnarkBlockVerifyArgs {
30    pub req_id: SnarkBlockVerifyId,
31    pub verifier_index: BlockVerifier,
32    pub verifier_srs: Arc<VerifierSRS>,
33    pub block: VerifiableBlockWithHash,
34}
35
36impl NodeService {
37    pub fn snark_block_proof_verifier_spawn(
38        event_sender: EventSender,
39        skip_proof_verification: bool,
40    ) -> mpsc::TrackedUnboundedSender<SnarkBlockVerifyArgs> {
41        let (tx, mut rx) = mpsc::tracked_unbounded_channel();
42        thread::Builder::new()
43            .name("block_proof_verifier".to_owned())
44            .spawn(move || {
45                while let Some(msg) = rx.blocking_recv() {
46                    let SnarkBlockVerifyArgs {
47                        req_id,
48                        verifier_index,
49                        verifier_srs,
50                        block,
51                    } = msg.0;
52                    eprintln!("verify({}) - start", block.hash_ref());
53                    let header = block.header_ref();
54
55                    if skip_proof_verification {
56                        eprintln!("verify({}) - skipping proof verification", block.hash_ref());
57                    }
58
59                    let verified = skip_proof_verification
60                        || ledger::proofs::verification::verify_block(
61                            header,
62                            &verifier_index,
63                            &verifier_srs,
64                        );
65
66                    let result = if verified {
67                        Ok(())
68                    } else {
69                        Err(SnarkBlockVerifyError::VerificationFailed)
70                    };
71                    eprintln!("verify({}) - end", block.hash_ref());
72
73                    let _ = event_sender.send(SnarkEvent::BlockVerify(req_id, result).into());
74                }
75            })
76            .expect("failed to spawn block_proof_verifier thread");
77
78        tx
79    }
80}
81
82impl mina_node::service::SnarkBlockVerifyService for NodeService {
83    fn verify_init(
84        &mut self,
85        req_id: SnarkBlockVerifyId,
86        verifier_index: BlockVerifier,
87        verifier_srs: Arc<VerifierSRS>,
88        block: VerifiableBlockWithHash,
89    ) {
90        if self.replayer.is_some() {
91            return;
92        }
93        let args = SnarkBlockVerifyArgs {
94            req_id,
95            verifier_index,
96            verifier_srs,
97            block,
98        };
99        let _ = self.snark_block_proof_verify.tracked_send(args);
100    }
101}
102
103impl mina_node::service::SnarkWorkVerifyService for NodeService {
104    fn verify_init(
105        &mut self,
106        req_id: SnarkWorkVerifyId,
107        verifier_index: TransactionVerifier,
108        verifier_srs: Arc<VerifierSRS>,
109        work: Vec<Snark>,
110    ) {
111        if self.replayer.is_some() {
112            return;
113        }
114        let tx = self.event_sender().clone();
115        rayon::spawn_fifo(move || {
116            let result = (|| {
117                let conv = |proof: &v2::LedgerProofProdStableV2| -> Result<_, InvalidBigInt> {
118                    Ok((
119                        Statement::<SokDigest>::try_from(&proof.0.statement)?,
120                        proof.proof.clone(),
121                    ))
122                };
123                let Ok(works) = work
124                    .into_iter()
125                    .flat_map(|work| match &*work.proofs {
126                        v2::TransactionSnarkWorkTStableV2Proofs::One(v) => {
127                            [conv(v).map(Some), Ok(None)]
128                        }
129                        v2::TransactionSnarkWorkTStableV2Proofs::Two((v1, v2)) => {
130                            [conv(v1).map(Some), conv(v2).map(Some)]
131                        }
132                    })
133                    .collect::<Result<Vec<_>, _>>()
134                else {
135                    return Err(SnarkWorkVerifyError::VerificationFailed);
136                };
137                if !ledger::proofs::verification::verify_transaction(
138                    works.iter().flatten().map(|(v1, v2)| (v1, v2)),
139                    &verifier_index,
140                    &verifier_srs,
141                ) {
142                    Err(SnarkWorkVerifyError::VerificationFailed)
143                } else {
144                    Ok(())
145                }
146            })();
147
148            let _ = tx.send(SnarkEvent::WorkVerify(req_id, result).into());
149        });
150    }
151}
152
153impl mina_node::service::SnarkUserCommandVerifyService for NodeService {
154    fn verify_init(
155        &mut self,
156        req_id: mina_node::snark::user_command_verify::SnarkUserCommandVerifyId,
157        commands: Vec<WithStatus<ledger::scan_state::transaction_logic::verifiable::UserCommand>>,
158    ) {
159        if self.replayer.is_some() {
160            return;
161        }
162
163        let tx = self.event_sender().clone();
164        rayon::spawn_fifo(move || {
165            let result = {
166                let (verified, invalid): (Vec<_>, Vec<_>) = ledger::verifier::Verifier
167                    .verify_commands(commands, None)
168                    .into_iter()
169                    .partition(Result::is_ok);
170
171                let verified: Vec<_> = verified.into_iter().map(Result::unwrap).collect();
172                let invalid: Vec<_> = invalid.into_iter().map(Result::unwrap_err).collect();
173
174                if !invalid.is_empty() {
175                    let transaction_pool_errors = invalid
176                        .into_iter()
177                        .map(TransactionError::Verifier)
178                        .collect();
179                    Err(TransactionPoolErrors::BatchedErrors(
180                        transaction_pool_errors,
181                    ))
182                } else {
183                    Ok(verified)
184                }
185            };
186
187            let result = result.map_err(|err| err.to_string());
188
189            let _ = tx.send(SnarkEvent::UserCommandVerify(req_id, result).into());
190        });
191    }
192}
193
194impl mina_node::service::SnarkPoolService for NodeService {
195    fn random_choose<'a>(
196        &mut self,
197        iter: impl Iterator<Item = &'a SnarkJobId>,
198        n: usize,
199    ) -> Vec<SnarkJobId> {
200        iter.choose_multiple(&mut self.rng, n)
201            .into_iter()
202            .cloned()
203            .collect()
204    }
205}