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