Skip to content

Commit b1950f3

Browse files
feat(block-producer): arc transaction data (#530)
This makes `AuthenticatedTransaction` much cheaper to clone and move around.
1 parent e43f8d0 commit b1950f3

File tree

3 files changed

+45
-31
lines changed

3 files changed

+45
-31
lines changed

crates/block-producer/src/batch_builder/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{
99

1010
use miden_objects::{
1111
accounts::AccountId,
12+
assembly::SourceManager,
1213
notes::NoteId,
1314
transaction::{OutputNote, TransactionId},
1415
Digest,
@@ -278,8 +279,13 @@ impl WorkerPool {
278279
async move {
279280
tracing::debug!("Begin proving batch.");
280281

281-
let transactions =
282-
transactions.into_iter().map(AuthenticatedTransaction::into_raw).collect();
282+
// TODO: This is a deep clone which can be avoided by change batch building to using
283+
// refs or arcs.
284+
let transactions = transactions
285+
.iter()
286+
.map(AuthenticatedTransaction::raw_proven_transaction)
287+
.cloned()
288+
.collect();
283289

284290
tokio::time::sleep(simulated_proof_time).await;
285291
if failed {

crates/block-producer/src/domain/transaction.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::BTreeSet;
1+
use std::{collections::BTreeSet, sync::Arc};
22

33
use miden_objects::{
44
accounts::AccountId,
@@ -14,10 +14,12 @@ use crate::{errors::VerifyTxError, mempool::BlockNumber, store::TransactionInput
1414
/// Authentication ensures that all nullifiers are unspent, and additionally authenticates some
1515
/// previously unauthenticated input notes.
1616
///
17+
/// This struct is cheap to clone as it uses an Arc for the heavy data.
18+
///
1719
/// Note that this is of course only valid for the chain height of the authentication.
1820
#[derive(Clone, Debug, PartialEq)]
1921
pub struct AuthenticatedTransaction {
20-
inner: ProvenTransaction,
22+
inner: Arc<ProvenTransaction>,
2123
/// The account state provided by the store [inputs](TransactionInputs).
2224
///
2325
/// This does not necessarily have to match the transaction's initial state
@@ -62,7 +64,7 @@ impl AuthenticatedTransaction {
6264
.collect();
6365

6466
Ok(AuthenticatedTransaction {
65-
inner: tx,
67+
inner: Arc::new(tx),
6668
notes_authenticated_by_store: authenticated_notes,
6769
authentication_height: BlockNumber::new(inputs.current_block_height),
6870
store_account_state: inputs.account_hash,
@@ -107,8 +109,8 @@ impl AuthenticatedTransaction {
107109
.filter(|note_id| !self.notes_authenticated_by_store.contains(note_id))
108110
}
109111

110-
pub fn into_raw(self) -> ProvenTransaction {
111-
self.inner
112+
pub fn raw_proven_transaction(&self) -> &ProvenTransaction {
113+
&self.inner
112114
}
113115
}
114116

@@ -117,18 +119,24 @@ impl AuthenticatedTransaction {
117119
//! Builder methods intended for easier test setup.
118120
119121
/// Short-hand for `Self::new` where the input's are setup to match the transaction's initial
120-
/// account state.
122+
/// account state. This covers the account's initial state and nullifiers being set to unspent.
121123
pub fn from_inner(inner: ProvenTransaction) -> Self {
122124
let store_account_state = match inner.account_update().init_state_hash() {
123125
zero if zero == Digest::default() => None,
124126
non_zero => Some(non_zero),
125127
};
126-
Self {
127-
inner,
128-
store_account_state,
129-
notes_authenticated_by_store: Default::default(),
130-
authentication_height: Default::default(),
131-
}
128+
let inputs = TransactionInputs {
129+
account_id: inner.account_id(),
130+
account_hash: store_account_state,
131+
nullifiers: inner.get_nullifiers().map(|nullifier| (nullifier, None)).collect(),
132+
missing_unauthenticated_notes: inner
133+
.get_unauthenticated_notes()
134+
.map(|header| header.id())
135+
.collect(),
136+
current_block_height: Default::default(),
137+
};
138+
// SAFETY: nullifiers were set to None aka are definitely unspent.
139+
Self::new(inner, inputs).unwrap()
132140
}
133141

134142
/// Overrides the authentication height with the given value.

crates/block-producer/src/mempool/dependency_graph.rs

+17-17
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<K, V> Default for DependencyGraph<K, V> {
6666
}
6767
}
6868

69-
impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
69+
impl<K: Ord + Copy, V: Clone> DependencyGraph<K, V> {
7070
/// Inserts a new node into the graph.
7171
///
7272
/// # Errors
@@ -82,19 +82,19 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
8282
let missing_parents = parents
8383
.iter()
8484
.filter(|parent| !self.vertices.contains_key(parent))
85-
.cloned()
85+
.copied()
8686
.collect::<BTreeSet<_>>();
8787
if !missing_parents.is_empty() {
8888
return Err(GraphError::MissingParents(missing_parents));
8989
}
9090

9191
// Inform parents of their new child.
9292
for parent in &parents {
93-
self.children.entry(parent.clone()).or_default().insert(key.clone());
93+
self.children.entry(*parent).or_default().insert(key);
9494
}
95-
self.vertices.insert(key.clone(), value);
96-
self.parents.insert(key.clone(), parents);
97-
self.children.insert(key.clone(), Default::default());
95+
self.vertices.insert(key, value);
96+
self.parents.insert(key, parents);
97+
self.children.insert(key, Default::default());
9898

9999
self.try_make_root(key);
100100

@@ -115,12 +115,12 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
115115
let missing_nodes = keys
116116
.iter()
117117
.filter(|key| !self.vertices.contains_key(key))
118-
.cloned()
118+
.copied()
119119
.collect::<BTreeSet<_>>();
120120
if !missing_nodes.is_empty() {
121121
return Err(GraphError::UnknownNodes(missing_nodes));
122122
}
123-
let unprocessed = keys.difference(&self.processed).cloned().collect::<BTreeSet<_>>();
123+
let unprocessed = keys.difference(&self.processed).copied().collect::<BTreeSet<_>>();
124124
if !unprocessed.is_empty() {
125125
return Err(GraphError::UnprocessedNodes(unprocessed));
126126
}
@@ -137,7 +137,7 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
137137
.map(|children| children.difference(&reverted))
138138
.into_iter()
139139
.flatten()
140-
.cloned();
140+
.copied();
141141

142142
to_revert.extend(unprocessed_children);
143143

@@ -176,13 +176,13 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
176176
let missing_nodes = keys
177177
.iter()
178178
.filter(|key| !self.vertices.contains_key(key))
179-
.cloned()
179+
.copied()
180180
.collect::<BTreeSet<_>>();
181181
if !missing_nodes.is_empty() {
182182
return Err(GraphError::UnknownNodes(missing_nodes));
183183
}
184184

185-
let unprocessed = keys.difference(&self.processed).cloned().collect::<BTreeSet<_>>();
185+
let unprocessed = keys.difference(&self.processed).copied().collect::<BTreeSet<_>>();
186186
if !unprocessed.is_empty() {
187187
return Err(GraphError::UnprocessedNodes(unprocessed));
188188
}
@@ -193,7 +193,7 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
193193
.flat_map(|key| self.parents.get(key))
194194
.flatten()
195195
.filter(|parent| !keys.contains(parent))
196-
.cloned()
196+
.copied()
197197
.collect::<BTreeSet<_>>();
198198
if !dangling.is_empty() {
199199
return Err(GraphError::DanglingNodes(dangling));
@@ -236,7 +236,7 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
236236
let missing_nodes = keys
237237
.iter()
238238
.filter(|key| !self.vertices.contains_key(key))
239-
.cloned()
239+
.copied()
240240
.collect::<BTreeSet<_>>();
241241
if !missing_nodes.is_empty() {
242242
return Err(GraphError::UnknownNodes(missing_nodes));
@@ -251,15 +251,15 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
251251
.vertices
252252
.remove(&key)
253253
.expect("Node was checked in precondition and must therefore exist");
254-
removed.insert(key.clone(), value);
254+
removed.insert(key, value);
255255

256256
self.processed.remove(&key);
257257
self.roots.remove(&key);
258258

259259
// Children must also be purged. Take care not to visit them twice which is
260260
// possible since children can have multiple purged parents.
261261
let unvisited_children = self.children.remove(&key).unwrap_or_default();
262-
let unvisited_children = unvisited_children.difference(&visited).cloned();
262+
let unvisited_children = unvisited_children.difference(&visited);
263263
to_remove.extend(unvisited_children);
264264

265265
// Inform parents that this child no longer exists.
@@ -315,7 +315,7 @@ impl<K: Ord + Clone, V: Clone> DependencyGraph<K, V> {
315315
return Err(GraphError::NotARootNode(key));
316316
}
317317

318-
self.processed.insert(key.clone());
318+
self.processed.insert(key);
319319

320320
self.children
321321
.get(&key)
@@ -374,7 +374,7 @@ mod tests {
374374

375375
/// Calls process_root until all nodes have been processed.
376376
fn process_all(&mut self) {
377-
while let Some(root) = self.roots().first().cloned() {
377+
while let Some(root) = self.roots().first().copied() {
378378
/// SAFETY: this is definitely a root since we just took it from there :)
379379
self.process_root(root);
380380
}

0 commit comments

Comments
 (0)