|
| 1 | +use ledger::proofs::provers::{TransactionProver, ZkappProver}; |
| 2 | +use ledger::proofs::zkapp::ZkappParams; |
| 3 | +use ledger::scan_state::scan_state::transaction_snark::SokMessage; |
1 | 4 | use mina_p2p_messages::v2;
|
2 |
| -use node::external_snark_worker::{ExternalSnarkWorkerError, SnarkWorkSpec}; |
| 5 | +use mina_signer::CompressedPubKey; |
| 6 | +use node::core::channels::mpsc; |
| 7 | +use node::event_source::ExternalSnarkWorkerEvent; |
| 8 | +use node::external_snark_worker::{ |
| 9 | + ExternalSnarkWorkerError, ExternalSnarkWorkerWorkError, SnarkWorkResult, SnarkWorkSpec, |
| 10 | + SnarkWorkSpecError, |
| 11 | +}; |
| 12 | +use node::snark::TransactionVerifier; |
3 | 13 |
|
4 | 14 | use crate::NodeService;
|
5 | 15 |
|
6 |
| -pub struct SnarkWorker {} |
| 16 | +use super::EventSender; |
| 17 | + |
| 18 | +pub struct SnarkWorker { |
| 19 | + cmd_sender: mpsc::UnboundedSender<Cmd>, |
| 20 | +} |
| 21 | + |
| 22 | +enum Cmd { |
| 23 | + Submit(Box<SnarkWorkSpec>), |
| 24 | + Cancel, |
| 25 | + Kill, |
| 26 | +} |
7 | 27 |
|
8 | 28 | impl node::service::ExternalSnarkWorkerService for NodeService {
|
9 | 29 | fn start(
|
10 | 30 | &mut self,
|
11 |
| - _public_key: v2::NonZeroCurvePoint, |
12 |
| - _fee: v2::CurrencyFeeStableV1, |
| 31 | + pub_key: v2::NonZeroCurvePoint, |
| 32 | + fee: v2::CurrencyFeeStableV1, |
| 33 | + work_verifier: TransactionVerifier, |
13 | 34 | ) -> Result<(), ExternalSnarkWorkerError> {
|
14 | 35 | if self.replayer.is_some() {
|
15 | 36 | return Ok(());
|
16 | 37 | }
|
17 |
| - todo!() |
| 38 | + let (cmd_sender, cmd_receiver) = mpsc::unbounded_channel(); |
| 39 | + // TODO(binier): improve pub key conv |
| 40 | + let sok_message = SokMessage::create( |
| 41 | + (&fee).into(), |
| 42 | + CompressedPubKey::from_address(&pub_key.to_string()).unwrap(), |
| 43 | + ); |
| 44 | + self.snark_worker = Some(SnarkWorker { cmd_sender }); |
| 45 | + let event_sender = self.event_sender().clone(); |
| 46 | + |
| 47 | + node::core::thread::Builder::new() |
| 48 | + .name("snark_worker".to_owned()) |
| 49 | + .spawn(move || worker_thread(cmd_receiver, event_sender, sok_message, work_verifier)) |
| 50 | + .map(|_| ()) |
| 51 | + .map_err(|err| ExternalSnarkWorkerError::Error(err.to_string())) |
18 | 52 | }
|
19 | 53 |
|
20 | 54 | fn kill(&mut self) -> Result<(), ExternalSnarkWorkerError> {
|
21 | 55 | if self.replayer.is_some() {
|
22 | 56 | return Ok(());
|
23 | 57 | }
|
24 |
| - todo!() |
| 58 | + |
| 59 | + if self |
| 60 | + .snark_worker |
| 61 | + .as_ref() |
| 62 | + .and_then(|s| s.cmd_sender.send(Cmd::Kill).ok()) |
| 63 | + .is_none() |
| 64 | + { |
| 65 | + return Err(ExternalSnarkWorkerError::NotRunning); |
| 66 | + } |
| 67 | + Ok(()) |
25 | 68 | }
|
26 | 69 |
|
27 |
| - fn submit(&mut self, _spec: SnarkWorkSpec) -> Result<(), ExternalSnarkWorkerError> { |
| 70 | + fn submit(&mut self, spec: SnarkWorkSpec) -> Result<(), ExternalSnarkWorkerError> { |
28 | 71 | if self.replayer.is_some() {
|
29 | 72 | return Ok(());
|
30 | 73 | }
|
31 |
| - todo!() |
| 74 | + |
| 75 | + if self |
| 76 | + .snark_worker |
| 77 | + .as_ref() |
| 78 | + .and_then(|s| s.cmd_sender.send(Cmd::Submit(spec.into())).ok()) |
| 79 | + .is_none() |
| 80 | + { |
| 81 | + return Err(ExternalSnarkWorkerError::NotRunning); |
| 82 | + } |
| 83 | + Ok(()) |
32 | 84 | }
|
33 | 85 |
|
34 | 86 | fn cancel(&mut self) -> Result<(), ExternalSnarkWorkerError> {
|
35 | 87 | if self.replayer.is_some() {
|
36 | 88 | return Ok(());
|
37 | 89 | }
|
38 |
| - todo!() |
| 90 | + |
| 91 | + // TODO(binier): for wasm threads, call terminate: |
| 92 | + // https://developer.mozilla.org/en-US/docs/Web/API/Worker/terminate |
| 93 | + if self |
| 94 | + .snark_worker |
| 95 | + .as_ref() |
| 96 | + .and_then(|s| s.cmd_sender.send(Cmd::Cancel).ok()) |
| 97 | + .is_none() |
| 98 | + { |
| 99 | + return Err(ExternalSnarkWorkerError::NotRunning); |
| 100 | + } |
| 101 | + Ok(()) |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +fn worker_thread( |
| 106 | + mut cmd_receiver: mpsc::UnboundedReceiver<Cmd>, |
| 107 | + event_sender: EventSender, |
| 108 | + sok_message: SokMessage, |
| 109 | + work_verifier: TransactionVerifier, |
| 110 | +) { |
| 111 | + let _ = event_sender.send(ExternalSnarkWorkerEvent::Started.into()); |
| 112 | + let tx_prover = TransactionProver::make(Some(work_verifier.clone())); |
| 113 | + let zkapp_prover = ZkappProver::make(Some(work_verifier)); |
| 114 | + while let Some(cmd) = cmd_receiver.blocking_recv() { |
| 115 | + match cmd { |
| 116 | + Cmd::Kill => { |
| 117 | + let _ = event_sender.send(ExternalSnarkWorkerEvent::Killed.into()); |
| 118 | + return; |
| 119 | + } |
| 120 | + Cmd::Cancel => { |
| 121 | + // can't cancel as it's a blocking thread. Once this |
| 122 | + // is moved to another process, kill it. |
| 123 | + let _ = event_sender.send(ExternalSnarkWorkerEvent::WorkCancelled.into()); |
| 124 | + } |
| 125 | + Cmd::Submit(spec) => { |
| 126 | + let event = match prove_spec(&tx_prover, &zkapp_prover, *spec, &sok_message) { |
| 127 | + Err(err) => ExternalSnarkWorkerEvent::WorkError(err), |
| 128 | + Ok(res) => ExternalSnarkWorkerEvent::WorkResult(res), |
| 129 | + }; |
| 130 | + |
| 131 | + let _ = event_sender.send(event.into()); |
| 132 | + } |
| 133 | + } |
39 | 134 | }
|
40 | 135 | }
|
| 136 | + |
| 137 | +fn prove_spec( |
| 138 | + tx_prover: &TransactionProver, |
| 139 | + zkapp_prover: &ZkappProver, |
| 140 | + spec: SnarkWorkSpec, |
| 141 | + sok_message: &SokMessage, |
| 142 | +) -> Result<SnarkWorkResult, ExternalSnarkWorkerWorkError> { |
| 143 | + match spec { |
| 144 | + SnarkWorkSpec::One(single) => prove_single(tx_prover, zkapp_prover, single, sok_message) |
| 145 | + .map(v2::TransactionSnarkWorkTStableV2Proofs::One), |
| 146 | + SnarkWorkSpec::Two((one, two)) => Ok(v2::TransactionSnarkWorkTStableV2Proofs::Two(( |
| 147 | + prove_single(tx_prover, zkapp_prover, one, sok_message)?, |
| 148 | + prove_single(tx_prover, zkapp_prover, two, sok_message)?, |
| 149 | + ))), |
| 150 | + } |
| 151 | + .map(Into::into) |
| 152 | +} |
| 153 | + |
| 154 | +fn invalid_bigint_err() -> ExternalSnarkWorkerWorkError { |
| 155 | + ExternalSnarkWorkerWorkError::WorkSpecError(SnarkWorkSpecError::InvalidBigInt) |
| 156 | +} |
| 157 | + |
| 158 | +fn prove_single( |
| 159 | + tx_prover: &TransactionProver, |
| 160 | + zkapp_prover: &ZkappProver, |
| 161 | + single: v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single, |
| 162 | + sok_message: &SokMessage, |
| 163 | +) -> Result<v2::LedgerProofProdStableV2, ExternalSnarkWorkerWorkError> { |
| 164 | + use ledger::proofs::{merge::MergeParams, transaction::TransactionParams}; |
| 165 | + |
| 166 | + let (snarked_ledger_state, res) = match single { |
| 167 | + v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single::Transition( |
| 168 | + snarked_ledger_state, |
| 169 | + witness, |
| 170 | + ) => { |
| 171 | + if let v2::MinaTransactionTransactionStableV2::Command(cmd) = &witness.transaction { |
| 172 | + if matches!(&**cmd, v2::MinaBaseUserCommandStableV2::ZkappCommand(_)) { |
| 173 | + return prove_zkapp(zkapp_prover, snarked_ledger_state, witness, sok_message); |
| 174 | + } |
| 175 | + } |
| 176 | + let res = ledger::proofs::generate_tx_proof(TransactionParams { |
| 177 | + statement: &snarked_ledger_state.0, |
| 178 | + tx_witness: &witness, |
| 179 | + message: sok_message, |
| 180 | + tx_step_prover: &tx_prover.tx_step_prover, |
| 181 | + tx_wrap_prover: &tx_prover.tx_wrap_prover, |
| 182 | + only_verify_constraints: false, |
| 183 | + expected_step_proof: None, |
| 184 | + ocaml_wrap_witness: None, |
| 185 | + }); |
| 186 | + (snarked_ledger_state.0, res) |
| 187 | + } |
| 188 | + v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single::Merge(data) => { |
| 189 | + let (snarked_ledger_state, proof_1, proof_2) = *data; |
| 190 | + let res = ledger::proofs::generate_merge_proof(MergeParams { |
| 191 | + statement: (&snarked_ledger_state.0) |
| 192 | + .try_into() |
| 193 | + .map_err(|_| invalid_bigint_err())?, |
| 194 | + proofs: &[proof_1, proof_2], |
| 195 | + message: sok_message, |
| 196 | + step_prover: &tx_prover.merge_step_prover, |
| 197 | + wrap_prover: &tx_prover.tx_wrap_prover, |
| 198 | + only_verify_constraints: false, |
| 199 | + expected_step_proof: None, |
| 200 | + ocaml_wrap_witness: None, |
| 201 | + }); |
| 202 | + (snarked_ledger_state.0, res) |
| 203 | + } |
| 204 | + }; |
| 205 | + res.map_err(|err| ExternalSnarkWorkerWorkError::Error(err.to_string())) |
| 206 | + .map(|proof| { |
| 207 | + v2::LedgerProofProdStableV2(v2::TransactionSnarkStableV2 { |
| 208 | + statement: v2::MinaStateSnarkedLedgerStateWithSokStableV2 { |
| 209 | + source: snarked_ledger_state.source, |
| 210 | + target: snarked_ledger_state.target, |
| 211 | + connecting_ledger_left: snarked_ledger_state.connecting_ledger_left, |
| 212 | + connecting_ledger_right: snarked_ledger_state.connecting_ledger_right, |
| 213 | + supply_increase: snarked_ledger_state.supply_increase, |
| 214 | + fee_excess: snarked_ledger_state.fee_excess, |
| 215 | + sok_digest: (&sok_message.digest()).into(), |
| 216 | + }, |
| 217 | + proof: v2::TransactionSnarkProofStableV2((&proof).into()), |
| 218 | + }) |
| 219 | + }) |
| 220 | +} |
| 221 | + |
| 222 | +fn prove_zkapp( |
| 223 | + zkapp_prover: &ZkappProver, |
| 224 | + snarked_ledger_state: v2::MinaStateSnarkedLedgerStateStableV2, |
| 225 | + witness: v2::TransactionWitnessStableV2, |
| 226 | + sok_message: &SokMessage, |
| 227 | +) -> Result<v2::LedgerProofProdStableV2, ExternalSnarkWorkerWorkError> { |
| 228 | + ledger::proofs::generate_zkapp_proof(ZkappParams { |
| 229 | + statement: &snarked_ledger_state.0, |
| 230 | + tx_witness: &witness, |
| 231 | + message: sok_message, |
| 232 | + step_opt_signed_opt_signed_prover: &zkapp_prover.step_opt_signed_opt_signed_prover, |
| 233 | + step_opt_signed_prover: &zkapp_prover.step_opt_signed_prover, |
| 234 | + step_proof_prover: &zkapp_prover.step_proof_prover, |
| 235 | + merge_step_prover: &zkapp_prover.merge_step_prover, |
| 236 | + tx_wrap_prover: &zkapp_prover.tx_wrap_prover, |
| 237 | + opt_signed_path: None, |
| 238 | + proved_path: None, |
| 239 | + }) |
| 240 | + .map(|proof| (&proof).into()) |
| 241 | + .map_err(|err| ExternalSnarkWorkerWorkError::Error(err.to_string())) |
| 242 | +} |
0 commit comments