Fix indexing error in checkpoint determination.

This commit is contained in:
Kris Nuttycombe 2023-06-29 20:24:33 -06:00
parent d65b129b43
commit 8fa3a08c0b
1 changed files with 35 additions and 30 deletions

View File

@ -235,9 +235,9 @@ pub(crate) fn scan_block_with_runner<
initial_commitment_tree_meta.map(|m| (m.sapling_tree_size() + 1).into()) initial_commitment_tree_meta.map(|m| (m.sapling_tree_size() + 1).into())
}; };
for tx in block.vtx.into_iter() { let block_tx_count = block.vtx.len();
for (tx_idx, tx) in block.vtx.into_iter().enumerate() {
let txid = tx.txid(); let txid = tx.txid();
let index = tx.index as usize;
// Check for spent notes. The only step that is not constant-time is // Check for spent notes. The only step that is not constant-time is
// the filter() at the end. // the filter() at the end.
@ -338,9 +338,20 @@ pub(crate) fn scan_block_with_runner<
.collect() .collect()
}; };
for (index, ((_, output), dec_output)) in decoded.iter().zip(decrypted).enumerate() { for (output_idx, ((_, output), dec_output)) in decoded.iter().zip(decrypted).enumerate()
{
// Collect block note commitments // Collect block note commitments
let node = sapling::Node::from_cmu(&output.cmu); let node = sapling::Node::from_cmu(&output.cmu);
let is_checkpoint = output_idx + 1 == decoded.len() && tx_idx + 1 == block_tx_count;
let retention = match (dec_output.is_some(), is_checkpoint) {
(is_marked, true) => Retention::Checkpoint {
id: block_height,
is_marked,
},
(true, false) => Retention::Marked,
(false, false) => Retention::Ephemeral,
};
if let Some((note, account, nk)) = dec_output { if let Some((note, account, nk)) = dec_output {
// A note is marked as "change" if the account that received it // A note is marked as "change" if the account that received it
// also spent notes in the same transaction. This will catch, // also spent notes in the same transaction. This will catch,
@ -351,11 +362,11 @@ pub(crate) fn scan_block_with_runner<
let is_change = spent_from_accounts.contains(&account); let is_change = spent_from_accounts.contains(&account);
let note_commitment_tree_position = sapling_tree_position let note_commitment_tree_position = sapling_tree_position
.ok_or(SyncError::SaplingTreeSizeUnknown(block_height))? .ok_or(SyncError::SaplingTreeSizeUnknown(block_height))?
+ index.try_into().unwrap(); + output_idx.try_into().unwrap();
let nf = K::sapling_nf(&nk, &note, note_commitment_tree_position); let nf = K::sapling_nf(&nk, &note, note_commitment_tree_position);
shielded_outputs.push(WalletSaplingOutput::from_parts( shielded_outputs.push(WalletSaplingOutput::from_parts(
index, output_idx,
output.cmu, output.cmu,
output.ephemeral_key.clone(), output.ephemeral_key.clone(),
account, account,
@ -364,38 +375,16 @@ pub(crate) fn scan_block_with_runner<
note_commitment_tree_position, note_commitment_tree_position,
nf, nf,
)); ));
sapling_note_commitments.push((
node,
if index == decoded.len() - 1 {
Retention::Checkpoint {
id: block_height,
is_marked: true,
}
} else {
Retention::Marked
},
));
} else {
sapling_note_commitments.push((
node,
if index == decoded.len() - 1 {
Retention::Checkpoint {
id: block_height,
is_marked: false,
}
} else {
Retention::Ephemeral
},
));
} }
sapling_note_commitments.push((node, retention));
} }
} }
if !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { if !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
wtxs.push(WalletTx { wtxs.push(WalletTx {
txid, txid,
index, index: tx.index as usize,
sapling_spends: shielded_spends, sapling_spends: shielded_spends,
sapling_outputs: shielded_outputs, sapling_outputs: shielded_outputs,
}); });
@ -423,6 +412,7 @@ mod tests {
ff::{Field, PrimeField}, ff::{Field, PrimeField},
GroupEncoding, GroupEncoding,
}; };
use incrementalmerkletree::Retention;
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
use zcash_note_encryption::Domain; use zcash_note_encryption::Domain;
use zcash_primitives::{ use zcash_primitives::{
@ -613,6 +603,21 @@ mod tests {
assert_eq!(tx.sapling_outputs[0].index(), 0); assert_eq!(tx.sapling_outputs[0].index(), 0);
assert_eq!(tx.sapling_outputs[0].account(), account); assert_eq!(tx.sapling_outputs[0].account(), account);
assert_eq!(tx.sapling_outputs[0].note().value().inner(), 5); assert_eq!(tx.sapling_outputs[0].note().value().inner(), 5);
assert_eq!(
pruned_block
.sapling_commitments
.iter()
.map(|(_, retention)| *retention)
.collect::<Vec<_>>(),
vec![
Retention::Ephemeral,
Retention::Checkpoint {
id: pruned_block.block_height,
is_marked: true
}
]
);
} }
go(false); go(false);