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