Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync: use TCT block acceleration #1716

Merged
merged 5 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/twelve-schools-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/wasm': minor
---

TCT block acceleration
232 changes: 118 additions & 114 deletions packages/wasm/crate/src/view_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,140 +124,144 @@ impl ViewServer {

let mut found_new_data: bool = false;

for state_payload in block.state_payloads {
let clone_payload = state_payload.clone();
let mut note_advice = BTreeMap::new();
let mut swap_advice = BTreeMap::new();

// This structure allows all of the trial decryption to be done in one pass,
// before processing any of the decrypted data. This makes it easier to skip
// over an entire block with no known data.
for state_payload in &block.state_payloads {
match state_payload {
StatePayload::Note { note: payload, .. } => {
let note_opt = (!skip_trial_decrypt)
.then(|| payload.trial_decrypt(&self.fvk))
.flatten();
match note_opt {
Some(note) => {
let note_position = self.sct.insert(Keep, payload.note_commitment)?;

let source = clone_payload.source().clone();
let nullifier = Nullifier::derive(
self.fvk.nullifier_key(),
note_position,
clone_payload.commitment(),
);
let address_index = self
.fvk
.incoming()
.index_for_diversifier(note.diversifier());

let note_record = SpendableNoteRecord {
note_commitment: *clone_payload.commitment(),
height_spent: None,
height_created: block.height,
note: note.clone(),
address_index,
nullifier,
position: note_position,
source,
return_address: None,
};
self.notes
.insert(payload.note_commitment, note_record.clone());
found_new_data = true;
}
None => {
self.sct.insert(Forget, payload.note_commitment)?;
}
if let Some(note) = note_opt {
// It's safe to avoid recomputing the note commitment here because
// trial_decrypt checks that the decrypted data is consistent
note_advice.insert(payload.note_commitment, note);
}
}
StatePayload::Swap { swap: payload, .. } => {
let note_opt = (!skip_trial_decrypt)
let swap_opt = (!skip_trial_decrypt)
.then(|| payload.trial_decrypt(&self.fvk))
.flatten();
match note_opt {
Some(swap) => {
let swap_position = self.sct.insert(Keep, payload.commitment)?;
let batch_data =
block.swap_outputs.get(&swap.trading_pair).ok_or_else(|| {
anyhow::anyhow!("server gave invalid compact block")
})?;

let source = clone_payload.source().clone();
let nullifier = Nullifier::derive(
self.fvk.nullifier_key(),
swap_position,
clone_payload.commitment(),
);

let swap_record = SwapRecord {
swap_commitment: *clone_payload.commitment(),
swap: swap.clone(),
position: swap_position,
nullifier,
source,
output_data: *batch_data,
height_claimed: None,
};
self.swaps.insert(payload.commitment, swap_record);

let batch_data =
block.swap_outputs.get(&swap.trading_pair).ok_or_else(|| {
anyhow::anyhow!("server gave invalid compact block")
})?;

let (output_1, output_2) = swap.output_notes(batch_data);

self.storage.store_advice(output_1).await?;
self.storage.store_advice(output_2).await?;
found_new_data = true;
}
None => {
self.sct.insert(Forget, payload.commitment)?;
}
if let Some(swap) = swap_opt {
// It's safe to avoid recomputing the note commitment here because
// trial_decrypt checks that the decrypted data is consistent
swap_advice.insert(payload.commitment, swap);
}
}
StatePayload::RolledUp { commitment, .. } => {
// This is a note we anticipated, so retain its auth path.

let advice_result = self.storage.read_advice(commitment).await?;

match advice_result {
None => {
self.sct.insert(Forget, commitment)?;
}
Some(note) => {
let position = self.sct.insert(Keep, commitment)?;

let address_index_1 = self
.fvk
.incoming()
.index_for_diversifier(note.diversifier());

let nullifier =
Nullifier::derive(self.fvk.nullifier_key(), position, &commitment);

let source = clone_payload.source().clone();

let spendable_note = SpendableNoteRecord {
note_commitment: note.commit(),
height_spent: None,
height_created: block.height,
note: note.clone(),
address_index: address_index_1,
nullifier,
position,
source,
return_address: None,
};
self.notes
.insert(spendable_note.note_commitment, spendable_note.clone());
found_new_data = true;
}
// Query the storage to find out if we have stored advice for this note commitment.
if let Some(note) = self.storage.read_advice(*commitment).await? {
note_advice.insert(*commitment, note);
}
}
}
}

self.sct.end_block()?;
if note_advice.is_empty() && swap_advice.is_empty() {
// If there are no notes we care about in this block, just insert the block root into the
// tree instead of processing each commitment individually
self.sct
.insert_block(block.block_root)
.expect("inserting a block root must succeed");
} else {
// If we found at least one note for us in this block, we have to explicitly construct the
// whole block in the SCT by inserting each commitment one at a time
for payload in block.state_payloads.into_iter() {
// We proceed commitment by commitment, querying our in-memory advice
// to see if we have any data for the commitment and act accordingly.
// We need to insert each commitment, so use a match statement to ensure we
// exhaustively cover all possible cases.
match (
note_advice.get(payload.commitment()),
swap_advice.get(payload.commitment()),
) {
(Some(note), None) => {
let position = self.sct.insert(Keep, *payload.commitment())?;

let source = payload.source().clone();
let nullifier = Nullifier::derive(
self.fvk.nullifier_key(),
position,
payload.commitment(),
);
let address_index = self
.fvk
.incoming()
.index_for_diversifier(note.diversifier());

let note_record = SpendableNoteRecord {
note_commitment: *payload.commitment(),
height_spent: None,
height_created: block.height,
note: note.clone(),
address_index,
nullifier,
position,
source,
return_address: None,
};
self.notes
.insert(*payload.commitment(), note_record.clone());

found_new_data = true;
}
(None, Some(swap)) => {
let position = self.sct.insert(Keep, *payload.commitment())?;
let output_data = *block
.swap_outputs
.get(&swap.trading_pair)
.ok_or_else(|| anyhow::anyhow!("server gave invalid compact block"))?;

let source = payload.source().clone();
let nullifier = Nullifier::derive(
self.fvk.nullifier_key(),
position,
payload.commitment(),
);

// Save the output notes that will be created by this swap as advice
// so that we can correctly detect the rolled-up output notes when they
// are claimed in the future.
let (output_1, output_2) = swap.output_notes(&output_data);
self.storage.store_advice(output_1).await?;
self.storage.store_advice(output_2).await?;

let swap_record = SwapRecord {
swap_commitment: *payload.commitment(),
swap: swap.clone(),
position,
nullifier,
source,
output_data,
height_claimed: None,
};
self.swaps.insert(*payload.commitment(), swap_record);

found_new_data = true;
}
(None, None) => {
// Don't remember this commitment; it wasn't ours, and
// it doesn't matter what kind of payload it was either.
// Just insert and forget
self.sct
.insert(tct::Witness::Forget, *payload.commitment())
.expect("inserting a commitment must succeed");
}
(Some(_), Some(_)) => unreachable!("swap and note commitments are distinct"),
}
}

// End the block in the commitment tree
self.sct.end_block().expect("ending the block must succed");
}

// If we've also reached the end of the epoch, end the epoch in the commitment tree
if block.epoch_root.is_some() {
self.sct.end_epoch()?;
self.sct.end_epoch().expect("ending the epoch must succeed");
}

self.latest_height = block.height;
Expand Down
Loading