mina_node_common/service/
snarks.rs1use 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}