use btreemap for zoneid -> ledgerupdate mapping

This commit is contained in:
David Rusu 2025-03-09 00:20:02 +04:00
parent a85b306baa
commit 5e0b2fe174
13 changed files with 91 additions and 98 deletions

View File

@ -50,7 +50,7 @@ impl TxRoot {
pub struct Tx {
pub root: TxRoot,
pub balance: Balance,
pub updates: Vec<LedgerUpdate>,
pub updates: BTreeMap<ZoneId, LedgerUpdate>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
@ -63,16 +63,16 @@ pub struct TxWitness {
pub frontier_paths: Vec<(MMR, MMRProof)>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
// TODO: this LedgerUpdate and LedgerUpdateWitness need to be merged
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct LedgerUpdate {
pub zone_id: ZoneId,
pub frontier_nodes: Vec<Root>,
pub inputs: Vec<Nullifier>,
pub outputs: Vec<NoteCommitment>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LedgerUpdateWitness {
pub zone_id: ZoneId,
pub frontier_nodes: Vec<Root>,
pub inputs: Vec<Nullifier>,
pub outputs: Vec<(NoteCommitment, Vec<u8>)>,
@ -88,15 +88,10 @@ impl LedgerUpdateWitness {
.collect::<Vec<_>>()
},
)));
let root = merkle::root(&merkle::padded_leaves([
input_root,
output_root,
self.zone_id,
]));
let root = merkle::root(&merkle::padded_leaves([input_root, output_root]));
(
LedgerUpdate {
zone_id: self.zone_id,
inputs: self.inputs,
outputs: self.outputs.into_iter().map(|(cm, _)| cm).collect(),
frontier_nodes: self.frontier_nodes,
@ -104,6 +99,19 @@ impl LedgerUpdateWitness {
root,
)
}
pub fn add_input(&mut self, nf: Nullifier, mmr: MMR) -> &mut Self {
self.inputs.push(nf);
self.frontier_nodes.extend(mmr.roots);
self.frontier_nodes.sort();
self.frontier_nodes.dedup();
self
}
pub fn add_output(&mut self, cm: NoteCommitment, data: Vec<u8>) -> &mut Self {
self.outputs.push((cm, data));
self
}
}
impl TxWitness {
@ -118,17 +126,19 @@ impl TxWitness {
self
}
pub fn compute_updates(&self, inputs: &[InputDerivedFields]) -> Vec<LedgerUpdateWitness> {
let mut updates = BTreeMap::new();
pub fn compute_updates(
&self,
inputs: &[InputDerivedFields],
) -> BTreeMap<ZoneId, LedgerUpdateWitness> {
let mut updates: BTreeMap<ZoneId, LedgerUpdateWitness> = Default::default();
assert_eq!(self.inputs.len(), self.frontier_paths.len());
for (input, (mmr, path)) in inputs.iter().zip(&self.frontier_paths) {
let entry = updates.entry(input.zone_id).or_insert(LedgerUpdateWitness {
zone_id: input.zone_id,
inputs: vec![],
outputs: vec![],
frontier_nodes: mmr.roots.clone(),
});
entry.inputs.push(input.nf);
let entry = updates
.entry(input.zone_id)
.or_default()
.add_input(input.nf, mmr.clone());
assert!(mmr.verify_proof(&input.cm.0, path));
// ensure a single MMR per zone per tx
assert_eq!(&mmr.roots, &entry.frontier_nodes);
@ -138,17 +148,11 @@ impl TxWitness {
assert!(output.value > 0);
updates
.entry(output.zone_id)
.or_insert(LedgerUpdateWitness {
zone_id: output.zone_id,
inputs: vec![],
outputs: vec![],
frontier_nodes: vec![],
})
.outputs
.push((output.note_commitment(), data.clone())); // TODO: avoid clone
.or_default()
.add_output(output.note_commitment(), data.clone()); // TODO: avoid clone
}
updates.into_values().collect()
updates
}
pub fn mint_amounts(&self) -> Vec<MintAmount> {
@ -232,11 +236,15 @@ impl TxWitness {
) -> Tx {
let mint_burn_root = Self::mint_burn_root(mints, burns);
let (updates, updates_roots): (Vec<_>, Vec<_>) = self
let (updates, updates_roots): (BTreeMap<_, _>, Vec<_>) = self
.compute_updates(inputs)
.into_iter()
.map(LedgerUpdateWitness::commit)
.map(|(zone_id, update)| {
let (update_cm, update_root) = update.commit();
((zone_id, update_cm), merkle::node(zone_id, update_root))
})
.unzip();
let update_root = merkle::root(&merkle::padded_leaves(updates_roots));
let root = self.root(update_root, mint_burn_root);
let balance = self.balance(mints, burns);
@ -251,7 +259,7 @@ impl TxWitness {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Bundle {
pub updates: Vec<LedgerUpdate>,
pub updates: BTreeMap<ZoneId, LedgerUpdate>,
pub root: BundleRoot,
}
@ -268,37 +276,25 @@ impl BundleWitness {
self.txs.iter().map(|tx| tx.root.0),
)));
let updates = self
let mut updates = self
.txs
.into_iter()
.fold(BTreeMap::new(), |mut updates, tx| {
for update in tx.updates {
let entry = updates.entry(update.zone_id).or_insert(LedgerUpdate {
zone_id: update.zone_id,
inputs: vec![],
outputs: vec![],
frontier_nodes: vec![],
});
for (zone_id, update) in tx.updates {
let entry: &mut LedgerUpdate = updates.entry(zone_id).or_default();
entry.inputs.extend(update.inputs);
entry.outputs.extend(update.outputs);
entry.frontier_nodes.extend(update.frontier_nodes); // TODO: maybe merge?
}
updates
})
.into_values()
.collect::<Vec<_>>();
});
// de-dup frontier nodes
let updates = updates
.into_iter()
.map(|mut update| {
update.frontier_nodes.sort();
update.frontier_nodes.dedup();
update
})
.collect();
updates.iter_mut().for_each(|(_, update)| {
update.frontier_nodes.sort();
update.frontier_nodes.dedup();
});
Bundle { updates, root }
}

View File

@ -1,5 +1,5 @@
use crate::{
crust::{tx::LedgerUpdate, Bundle, NoteCommitment, Nullifier},
crust::{BundleRoot, NoteCommitment, Nullifier},
ds::{
indexed::{BatchUpdateProof, NullifierTree},
mmr::{MMRProof, MMR},
@ -38,8 +38,8 @@ impl LedgerWitness {
self.commitments.push(&cm.0);
}
pub fn add_bundle(&mut self, bundle_root: [u8; 32]) {
self.bundles.push(&bundle_root);
pub fn add_bundle(&mut self, bundle_root: BundleRoot) {
self.bundles.push(&bundle_root.0);
}
pub fn assert_nfs_update(&mut self, nullifiers: &[Nullifier], proof: &BatchUpdateProof) {
@ -77,8 +77,8 @@ impl LedgerState {
self.nullifiers.insert_batch(nfs)
}
pub fn add_bundle(&mut self, bundle_root: [u8; 32]) -> (MMR, MMRProof) {
let proof = self.bundles.push(&bundle_root);
pub fn add_bundle(&mut self, bundle_root: BundleRoot) -> (MMR, MMRProof) {
let proof = self.bundles.push(&bundle_root.0);
(self.bundles.clone(), proof)
}
}

View File

@ -25,8 +25,7 @@ impl ProvedLedgerTransition {
let zone_ledger_update = bundle
.updates
.iter()
.find(|update| update.zone_id == zone_id)
.get(&zone_id)
.expect("why are we proving this bundle for this zone if it's not involved?");
let cm_root_proofs =
@ -45,7 +44,7 @@ impl ProvedLedgerTransition {
cm_root_proofs,
};
w_bundles.push(ledger_bundle)
w_bundles.push(ledger_bundle);
}
let witness = LedgerProofPrivate {
@ -56,13 +55,17 @@ impl ProvedLedgerTransition {
};
for bundle in &witness.bundles {
for update in &bundle.bundle.updates {
if update.zone_id == zone_id {
for cm in &update.outputs {
ledger.add_commitment(cm);
}
}
let update = bundle
.bundle
.updates
.get(&zone_id)
.expect("should have a bundle from the zone we are proofing for");
for cm in &update.outputs {
ledger.add_commitment(cm);
}
ledger.add_bundle(bundle.bundle.root);
}
witness.write(&mut env);

View File

@ -45,15 +45,10 @@ impl ProvedBatchUpdate {
.zip(self.stf_proofs.iter())
.zip(self.ledger_proofs.iter())
{
if ledger_proof.public().old_ledger != update.old.ledger
|| ledger_proof.public().ledger != update.new.ledger
{
return false;
}
if stf_proof.public.old != update.old || stf_proof.public.new != update.new {
return false;
}
assert_eq!(ledger_proof.public().old_ledger, update.old.ledger);
assert_eq!(ledger_proof.public().ledger, update.new.ledger);
assert_eq!(stf_proof.public.old, update.old);
assert_eq!(stf_proof.public.new, update.new);
}
true

View File

@ -29,38 +29,37 @@ fn main() {
)
.unwrap();
let zones = Vec::from_iter(bundle.updates.iter().map(|update| update.zone_id));
if !(zones.len() == 1 && zones[0] == id) {
if bundle.updates.len() > 1 {
// This is a cross zone bundle, add a sync log for it to ensure all zones
// also approve it.
sync_logs.push(SyncLog {
bundle: bundle.root,
zones,
zones: bundle.updates.keys().copied().collect(),
});
}
if let Some(ledger_update) = bundle
let ledger_update = bundle
.updates
.into_iter()
.find(|update| update.zone_id == id)
{
for node in &ledger_update.frontier_nodes {
let past_cm_root_proof = cm_root_proofs
.get(&node.root)
.expect("missing cm root proof");
let expected_current_cm_root = merkle::path_root(node.root, past_cm_root_proof);
assert!(old_ledger.valid_cm_root(expected_current_cm_root))
}
.get(&id)
.expect("attempting to prove a bundle that is not for this zone");
for cm in &ledger_update.outputs {
ledger.add_commitment(cm);
outputs.push(*cm);
}
for node in &ledger_update.frontier_nodes {
let past_cm_root_proof = cm_root_proofs
.get(&node.root)
.expect("missing cm root proof");
nullifiers.extend(ledger_update.inputs);
let expected_current_cm_root = merkle::path_root(node.root, past_cm_root_proof);
assert!(old_ledger.valid_cm_root(expected_current_cm_root))
}
ledger.add_bundle(bundle.root.0);
for cm in &ledger_update.outputs {
ledger.add_commitment(cm);
outputs.push(*cm);
}
nullifiers.extend(ledger_update.inputs.clone());
ledger.add_bundle(bundle.root);
}
// TODO: sort outside and check

View File

@ -1 +1 @@
d6149899df54b0114942f4be1ec773ba771d7f6a9d4201c31a4d75038455eaf3
8ad828fccc168801663aea5e10c8d17019f0bdbb69fa1a169400603535880fcd

View File

@ -1 +1 @@
02ed858ba11b19067319841ec824903318526d6c87c3dd1b0ac2330783d7078e
fef663705ca009bdaa893c842d084848ddfbbf737441a801b6778125a3a5493c

View File

@ -1 +1 @@
400ac4e7c9cc351b84227e090e474c299546cce1db2f6836a8fc22b25aa4a317
29a96bc98531b44e806051daff1be35fe84e00f4cd7f45de0213c1387184c12e

Binary file not shown.

View File

@ -1 +1 @@
ec8a16b24c6ba20fe44b3758bed22da59fe7012d2dd8e5b184c1d17f7f8d2c1f
68a7c458b6ff62901c0176a1ff9433e431d635a3a94b169af7227ac5b1681606