Skip to content

Commit 230a6af

Browse files
authored
Merge pull request #985 from openmina/fix/tx-snark-verify
fix(txpool): Don't verify zkapp proofs in the main thread
2 parents 98e1a5b + 95625f1 commit 230a6af

16 files changed

+226
-136
lines changed

ledger/src/transaction_pool.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
currency::{Amount, Balance, BlockTime, Fee, Magnitude, Nonce, Slot},
1616
fee_rate::FeeRate,
1717
transaction_logic::{
18-
valid,
18+
valid, verifiable,
1919
zkapp_command::{
2020
from_unapplied_sequence::{self, FromUnappliedSequence},
2121
MaybeWithStatus, WithHash,
@@ -2095,11 +2095,7 @@ impl TransactionPool {
20952095
}
20962096
}
20972097

2098-
pub fn verify(
2099-
&self,
2100-
diff: diff::Diff,
2101-
accounts: &BTreeMap<AccountId, Account>,
2102-
) -> Result<Vec<valid::UserCommand>, TransactionPoolErrors> {
2098+
pub fn prevalidate(&self, diff: diff::Diff) -> Result<diff::Diff, TransactionPoolErrors> {
21032099
let well_formedness_errors: HashSet<_> = diff
21042100
.list
21052101
.iter()
@@ -2118,6 +2114,14 @@ impl TransactionPool {
21182114
));
21192115
}
21202116

2117+
Ok(diff)
2118+
}
2119+
2120+
pub fn convert_diff_to_verifiable(
2121+
&self,
2122+
diff: diff::Diff,
2123+
accounts: &BTreeMap<AccountId, Account>,
2124+
) -> Result<Vec<WithStatus<verifiable::UserCommand>>, TransactionPoolErrors> {
21212125
let cs = diff
21222126
.list
21232127
.iter()
@@ -2167,10 +2171,18 @@ impl TransactionPool {
21672171
.into_iter()
21682172
.map(|MaybeWithStatus { cmd, status: _ }| WithStatus {
21692173
data: cmd,
2174+
// TODO: is this correct?
21702175
status: Applied,
21712176
})
21722177
.collect::<Vec<_>>();
21732178

2179+
Ok(diff)
2180+
}
2181+
2182+
pub fn verify_proofs(
2183+
&self,
2184+
diff: Vec<WithStatus<verifiable::UserCommand>>,
2185+
) -> Result<Vec<valid::UserCommand>, TransactionPoolErrors> {
21742186
let (verified, invalid): (Vec<_>, Vec<_>) = Verifier
21752187
.verify_commands(diff, None)
21762188
.into_iter()

node/common/src/service/snarks.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
use std::sync::Arc;
22

33
use ark_ff::fields::arithmetic::InvalidBigInt;
4-
use ledger::scan_state::scan_state::transaction_snark::{SokDigest, Statement};
4+
use ledger::{
5+
scan_state::{
6+
scan_state::transaction_snark::{SokDigest, Statement},
7+
transaction_logic::WithStatus,
8+
},
9+
transaction_pool::{TransactionError, TransactionPoolErrors},
10+
};
511
use mina_p2p_messages::v2;
612
use node::{
713
core::{
@@ -104,14 +110,39 @@ impl node::service::SnarkWorkVerifyService for NodeService {
104110
impl node::service::SnarkUserCommandVerifyService for NodeService {
105111
fn verify_init(
106112
&mut self,
107-
_req_id: node::snark::user_command_verify::SnarkUserCommandVerifyId,
108-
_verifier_index: TransactionVerifier,
109-
_verifier_srs: Arc<VerifierSRS>,
110-
_commands: mina_p2p_messages::list::List<
111-
mina_p2p_messages::v2::MinaBaseUserCommandStableV2,
112-
>,
113+
req_id: node::snark::user_command_verify::SnarkUserCommandVerifyId,
114+
commands: Vec<WithStatus<ledger::scan_state::transaction_logic::verifiable::UserCommand>>,
113115
) {
114-
todo!()
116+
if self.replayer.is_some() {
117+
return;
118+
}
119+
120+
let tx = self.event_sender().clone();
121+
let result = {
122+
let (verified, invalid): (Vec<_>, Vec<_>) = ledger::verifier::Verifier
123+
.verify_commands(commands, None)
124+
.into_iter()
125+
.partition(Result::is_ok);
126+
127+
let verified: Vec<_> = verified.into_iter().map(Result::unwrap).collect();
128+
let invalid: Vec<_> = invalid.into_iter().map(Result::unwrap_err).collect();
129+
130+
if !invalid.is_empty() {
131+
let transaction_pool_errors = invalid
132+
.into_iter()
133+
.map(TransactionError::Verifier)
134+
.collect();
135+
Err(TransactionPoolErrors::BatchedErrors(
136+
transaction_pool_errors,
137+
))
138+
} else {
139+
Ok(verified)
140+
}
141+
};
142+
143+
let result = result.map_err(|err| err.to_string());
144+
145+
let _ = tx.send(SnarkEvent::UserCommandVerify(req_id, result).into());
115146
}
116147
}
117148

node/src/action_kind.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ pub enum ActionKind {
603603
TransactionPoolStartVerify,
604604
TransactionPoolStartVerifyWithAccounts,
605605
TransactionPoolVerifyError,
606+
TransactionPoolVerifySuccess,
606607
TransactionPoolCandidateFetchAll,
607608
TransactionPoolCandidateFetchError,
608609
TransactionPoolCandidateFetchInit,
@@ -706,7 +707,7 @@ pub enum ActionKind {
706707
}
707708

708709
impl ActionKind {
709-
pub const COUNT: u16 = 596;
710+
pub const COUNT: u16 = 597;
710711
}
711712

712713
impl std::fmt::Display for ActionKind {
@@ -916,6 +917,7 @@ impl ActionKindGet for TransactionPoolAction {
916917
Self::StartVerifyWithAccounts { .. } => {
917918
ActionKind::TransactionPoolStartVerifyWithAccounts
918919
}
920+
Self::VerifySuccess { .. } => ActionKind::TransactionPoolVerifySuccess,
919921
Self::VerifyError { .. } => ActionKind::TransactionPoolVerifyError,
920922
Self::BestTipChanged { .. } => ActionKind::TransactionPoolBestTipChanged,
921923
Self::BestTipChangedWithAccounts { .. } => {

node/src/event_source/event_source_effects.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,13 @@ pub fn event_source_effects<S: Service>(store: &mut Store<S>, action: EventSourc
290290
}
291291
},
292292
SnarkEvent::UserCommandVerify(req_id, result) => {
293-
if result.iter().any(|res| res.is_err()) {
293+
if let Ok(commands) = result {
294+
store.dispatch(SnarkUserCommandVerifyAction::Success { req_id, commands });
295+
} else {
294296
store.dispatch(SnarkUserCommandVerifyAction::Error {
295297
req_id,
296298
error: SnarkUserCommandVerifyError::VerificationFailed,
297299
});
298-
} else {
299-
store.dispatch(SnarkUserCommandVerifyAction::Success { req_id });
300300
}
301301
}
302302
},

node/src/logger/logger_effects.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ pub fn logger_effects<S: Service>(store: &Store<S>, action: ActionWithMetaRef<'_
117117
Action::ExternalSnarkWorker(action) => action.action_event(&context),
118118
Action::SnarkPool(action) => action.action_event(&context),
119119
Action::Snark(SnarkAction::WorkVerify(a)) => a.action_event(&context),
120+
Action::Snark(SnarkAction::UserCommandVerify(a)) => a.action_event(&context),
120121
Action::Consensus(a) => a.action_event(&context),
121122
Action::TransitionFrontier(a) => match a {
122123
TransitionFrontierAction::Synced { .. } => {

node/src/state.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::time::Duration;
33

44
use mina_p2p_messages::v2;
55
use openmina_core::constants::PROTOCOL_VERSION;
6-
use openmina_core::transaction::{Transaction, TransactionInfo, TransactionWithHash};
6+
use openmina_core::transaction::{TransactionInfo, TransactionWithHash};
77
use rand::prelude::*;
88

99
use openmina_core::block::BlockWithHash;
@@ -534,10 +534,7 @@ impl P2p {
534534
on_p2p_channels_transaction_libp2p_received: Some(redux::callback!(
535535
on_p2p_channels_transaction_libp2p_received(transaction: Box<TransactionWithHash>) -> crate::Action {
536536
TransactionPoolAction::StartVerify {
537-
commands: std::iter::once(*transaction)
538-
// TODO(SEC): remove once proof verification is done outside state machine.
539-
.filter(|v| !matches!(v.body(), Transaction::ZkappCommand(_)))
540-
.collect(),
537+
commands: std::iter::once(*transaction).collect(),
541538
from_rpc: None
542539
}
543540
}

node/src/transaction_pool/transaction_pool_actions.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{BTreeMap, BTreeSet};
22

33
use ledger::{
4+
scan_state::transaction_logic::valid,
45
transaction_pool::{
56
diff::{self, BestTipDiff, DiffVerified},
67
ValidCommandWithHash,
@@ -30,6 +31,10 @@ pub enum TransactionPoolAction {
3031
pending_id: PendingId,
3132
from_rpc: Option<RpcId>,
3233
},
34+
VerifySuccess {
35+
valids: Vec<valid::UserCommand>,
36+
from_rpc: Option<RpcId>,
37+
},
3338
#[action_event(level = warn, fields(debug(errors)))]
3439
VerifyError {
3540
errors: Vec<String>,

node/src/transaction_pool/transaction_pool_reducer.rs

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use ledger::{
2-
scan_state::transaction_logic::{GenericCommand, UserCommand},
2+
scan_state::transaction_logic::{valid, GenericCommand, UserCommand},
33
transaction_pool::{
44
diff::{self, DiffVerified},
55
transaction_hash, ApplyDecision, TransactionPoolErrors,
@@ -14,6 +14,7 @@ use openmina_core::{
1414
};
1515
use p2p::channels::transaction::P2pChannelsTransactionAction;
1616
use redux::callback;
17+
use snark::user_command_verify::{SnarkUserCommandVerifyAction, SnarkUserCommandVerifyId};
1718
use std::collections::{BTreeMap, BTreeSet};
1819

1920
use crate::{BlockProducerAction, RpcAction};
@@ -107,21 +108,38 @@ impl TransactionPoolState {
107108
};
108109
let diff = diff::Diff { list: commands };
109110

110-
match substate.pool.verify(diff, accounts) {
111-
Ok(valids) => {
112-
let valids = valids
113-
.into_iter()
114-
.map(transaction_hash::hash_command)
115-
.collect::<Vec<_>>();
116-
let best_tip_hash = substate.best_tip_hash.clone().unwrap();
117-
let diff = DiffVerified { list: valids };
111+
match substate
112+
.pool
113+
.prevalidate(diff)
114+
.and_then(|diff| substate.pool.convert_diff_to_verifiable(diff, accounts))
115+
{
116+
Ok(verifiable) => {
117+
let (dispatcher, global_state) = state.into_dispatcher_and_state();
118+
let req_id = global_state.snark.user_command_verify.next_req_id();
118119

119-
let dispatcher = state.into_dispatcher();
120-
dispatcher.push(TransactionPoolAction::ApplyVerifiedDiff {
121-
best_tip_hash,
122-
diff,
123-
is_sender_local: from_rpc.is_some(),
120+
dispatcher.push(SnarkUserCommandVerifyAction::Init {
121+
req_id,
122+
commands: verifiable,
124123
from_rpc: *from_rpc,
124+
on_success: callback!(
125+
on_snark_user_command_verify_success(
126+
(req_id: SnarkUserCommandVerifyId, valids: Vec<valid::UserCommand>, from_rpc: Option<RpcId>)
127+
) -> crate::Action {
128+
TransactionPoolAction::VerifySuccess {
129+
valids,
130+
from_rpc,
131+
}
132+
}
133+
),
134+
on_error: callback!(
135+
on_snark_user_command_verify_error(
136+
(req_id: SnarkUserCommandVerifyId, errors: Vec<String>)
137+
) -> crate::Action {
138+
TransactionPoolAction::VerifyError {
139+
errors
140+
}
141+
}
142+
)
125143
});
126144
}
127145
Err(e) => {
@@ -151,6 +169,23 @@ impl TransactionPoolState {
151169
}
152170
}
153171
}
172+
TransactionPoolAction::VerifySuccess { valids, from_rpc } => {
173+
let valids = valids
174+
.iter()
175+
.cloned()
176+
.map(transaction_hash::hash_command)
177+
.collect::<Vec<_>>();
178+
let best_tip_hash = substate.best_tip_hash.clone().unwrap();
179+
let diff = DiffVerified { list: valids };
180+
181+
let dispatcher = state.into_dispatcher();
182+
dispatcher.push(TransactionPoolAction::ApplyVerifiedDiff {
183+
best_tip_hash,
184+
diff,
185+
is_sender_local: from_rpc.is_some(),
186+
from_rpc: *from_rpc,
187+
});
188+
}
154189
TransactionPoolAction::VerifyError { .. } => {
155190
// just logging the errors
156191
}

node/testing/src/service/mod.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use std::{collections::BTreeMap, sync::Arc};
88
use ledger::dummy::dummy_transaction_proof;
99
use ledger::proofs::transaction::ProofError;
1010
use ledger::scan_state::scan_state::transaction_snark::SokMessage;
11+
use ledger::scan_state::transaction_logic::{verifiable, WithStatus};
1112
use ledger::Mask;
12-
use mina_p2p_messages::list::List;
1313
use mina_p2p_messages::string::ByteString;
1414
use mina_p2p_messages::v2::{
15-
self, CurrencyFeeStableV1, LedgerHash, LedgerProofProdStableV2, MinaBaseProofStableV2,
15+
CurrencyFeeStableV1, LedgerHash, LedgerProofProdStableV2, MinaBaseProofStableV2,
1616
MinaStateSnarkedLedgerStateWithSokStableV2, NonZeroCurvePoint,
1717
ProverExtendBlockchainInputStableV2, SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single,
1818
StateHash, TransactionSnarkStableV2, TransactionSnarkWorkTStableV2Proofs,
@@ -451,17 +451,9 @@ impl SnarkUserCommandVerifyService for NodeTestingService {
451451
fn verify_init(
452452
&mut self,
453453
req_id: SnarkUserCommandVerifyId,
454-
verifier_index: TransactionVerifier,
455-
verifier_srs: Arc<VerifierSRS>,
456-
commands: List<v2::MinaBaseUserCommandStableV2>,
454+
commands: Vec<WithStatus<verifiable::UserCommand>>,
457455
) {
458-
SnarkUserCommandVerifyService::verify_init(
459-
&mut self.real,
460-
req_id,
461-
verifier_index,
462-
verifier_srs,
463-
commands,
464-
)
456+
SnarkUserCommandVerifyService::verify_init(&mut self.real, req_id, commands)
465457
}
466458
}
467459

snark/src/snark_event.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub enum SnarkEvent {
1111
WorkVerify(SnarkWorkVerifyId, Result<(), SnarkWorkVerifyError>),
1212
UserCommandVerify(
1313
SnarkUserCommandVerifyId,
14-
Vec<Result<valid::UserCommand, String>>,
14+
Result<Vec<valid::UserCommand>, String>,
1515
),
1616
}
1717

@@ -33,13 +33,9 @@ impl std::fmt::Display for SnarkEvent {
3333
write!(f, "WorkVerify, {id}, {}", res_kind(res))
3434
}
3535
Self::UserCommandVerify(id, res) => {
36-
let n_failed = res.iter().filter(|res| res.is_err()).count();
37-
let n_success = res.len() - n_failed;
38-
write!(
39-
f,
40-
"UserCommandVerify, {id}, n_success={} n_failed={}",
41-
n_success, n_failed
42-
)
36+
//let n_failed = res.iter().filter(|res| res.is_err()).count();
37+
//let n_success = res.len() - n_failed;
38+
write!(f, "UserCommandVerify, {id}, success={}", res.is_err())
4339
}
4440
}
4541
}

0 commit comments

Comments
 (0)