openmina_node_common/service/
snarks.rs

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