Do not create split notes with native asset (#65)

Due to privacy considerations, we might incorporate dummy or split notes while generating a bundle.
However, to maintain consistency with the previous version, we choose not to include split notes for native asset.

In addition, we use a new dummy/split notes for each extend in order to have different nullifiers.
This commit is contained in:
Constance Beguier 2023-06-06 08:46:52 +02:00 committed by GitHub
parent bedc732d6f
commit 32eee6e083
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 19 deletions

View File

@ -167,9 +167,28 @@ impl SpendInfo {
}
}
/// Return a copy of this note with the split flag set to `true`.
fn create_split_spend(&self) -> Self {
SpendInfo::new(self.fvk.clone(), self.note, self.merkle_path.clone(), true).unwrap()
/// Creates a split spend, which is identical to origin normal spend except that we use a random
/// fvk to generate a different nullifier. In addition, the split_flag is raised.
///
/// Defined in [Transfer and Burn of Zcash Shielded Assets ZIP-0226 § Split Notes (DRAFT PR)][TransferZSA].
///
/// [TransferZSA]: https://qed-it.github.io/zips/zip-0226.html#split-notes
fn create_split_spend(&self, rng: &mut impl RngCore) -> Self {
let note = self.note;
let merkle_path = self.merkle_path.clone();
let sk = SpendingKey::random(rng);
let fvk: FullViewingKey = (&sk).into();
SpendInfo {
dummy_sk: Some(sk),
fvk,
// We use external scope to avoid unnecessary derivations
scope: Scope::External,
note,
merkle_path,
split_flag: true,
}
}
}
@ -467,12 +486,12 @@ impl Builder {
.cloned()
.unwrap();
// use the first spend to create split spend(s) or create a dummy if empty.
let dummy_spend = spends.first().map_or_else(
|| SpendInfo::dummy(asset, &mut rng),
|s| s.create_split_spend(),
let first_spend = spends.first().cloned();
spends.extend(
iter::repeat_with(|| pad_spend(first_spend.as_ref(), asset, &mut rng))
.take(num_actions - num_spends),
);
spends.extend(iter::repeat_with(|| dummy_spend.clone()).take(num_actions - num_spends));
// Extend the recipients with dummy values.
recipients.extend(
@ -574,6 +593,20 @@ fn partition_by_asset(
hm
}
/// Returns a dummy/split notes to extend the spends.
fn pad_spend(spend: Option<&SpendInfo>, asset: AssetBase, mut rng: impl RngCore) -> SpendInfo {
if asset.is_native().into() {
// For native asset, extends with dummy notes
SpendInfo::dummy(asset, &mut rng)
} else {
// For ZSA asset, extends with
// - dummy notes if first spend is empty
// - split notes otherwise.
let dummy = SpendInfo::dummy(asset, &mut rng);
spend.map_or_else(|| dummy, |s| s.create_split_spend(&mut rng))
}
}
/// Marker trait representing bundle signatures in the process of being created.
pub trait InProgressSignatures: fmt::Debug {
/// The authorization type of an Orchard action in the process of being authorized.

View File

@ -261,9 +261,25 @@ fn build_and_verify_bundle(
// Verify the shielded bundle, currently without the proof.
verify_bundle(&shielded_bundle, &keys.vk, true);
assert_eq!(shielded_bundle.actions().len(), expected_num_actions);
assert!(verify_unique_spent_nullifiers(&shielded_bundle));
Ok(())
}
fn verify_unique_spent_nullifiers(bundle: &Bundle<Authorized, i64>) -> bool {
let mut unique_nulifiers = Vec::new();
let spent_nullifiers = bundle
.actions()
.iter()
.map(|action| *action.nullifier())
.collect::<Vec<_>>();
spent_nullifiers.iter().enumerate().all(|(i, item)| {
unique_nulifiers.push(*item);
// Check if the item is already in the unique_nullifiers vector by checking that the first
// position of the item is equal to the current index i.
unique_nulifiers.iter().position(|x| x == item) == Some(i)
})
}
/// Issue several ZSA and native notes and spend them in different combinations, e.g. split and join
#[test]
fn zsa_issue_and_transfer() {
@ -315,23 +331,28 @@ fn zsa_issue_and_transfer() {
)
.unwrap();
// 2. Split single ZSA note into 2 notes
let delta = 2; // arbitrary number for value manipulation
// 2. Split single ZSA note into 3 notes
let delta_1 = 2; // arbitrary number for value manipulation
let delta_2 = 5; // arbitrary number for value manipulation
build_and_verify_bundle(
vec![&zsa_spend_1],
vec![
TestOutputInfo {
value: NoteValue::from_raw(zsa_spend_1.note.value().inner() - delta),
value: NoteValue::from_raw(zsa_spend_1.note.value().inner() - delta_1 - delta_2),
asset: zsa_spend_1.note.asset(),
},
TestOutputInfo {
value: NoteValue::from_raw(delta),
value: NoteValue::from_raw(delta_1),
asset: zsa_spend_1.note.asset(),
},
TestOutputInfo {
value: NoteValue::from_raw(delta_2),
asset: zsa_spend_1.note.asset(),
},
],
vec![],
anchor,
2,
3,
&keys,
)
.unwrap();
@ -357,11 +378,11 @@ fn zsa_issue_and_transfer() {
vec![&zsa_spend_1, &zsa_spend_2],
vec![
TestOutputInfo {
value: NoteValue::from_raw(zsa_spend_1.note.value().inner() - delta),
value: NoteValue::from_raw(zsa_spend_1.note.value().inner() - delta_1),
asset: zsa_spend_1.note.asset(),
},
TestOutputInfo {
value: NoteValue::from_raw(zsa_spend_2.note.value().inner() + delta),
value: NoteValue::from_raw(zsa_spend_2.note.value().inner() + delta_1),
asset: zsa_spend_2.note.asset(),
},
],
@ -401,13 +422,21 @@ fn zsa_issue_and_transfer() {
asset: zsa_spend_1.note.asset(),
},
TestOutputInfo {
value: native_spend.note.value(),
value: NoteValue::from_raw(native_spend.note.value().inner() - delta_1 - delta_2),
asset: AssetBase::native(),
},
TestOutputInfo {
value: NoteValue::from_raw(delta_1),
asset: AssetBase::native(),
},
TestOutputInfo {
value: NoteValue::from_raw(delta_2),
asset: AssetBase::native(),
},
],
vec![],
native_anchor,
4,
5,
&keys,
)
.unwrap();
@ -450,11 +479,11 @@ fn zsa_issue_and_transfer() {
vec![&zsa_spend_t7_1, &zsa_spend_t7_2],
vec![
TestOutputInfo {
value: NoteValue::from_raw(zsa_spend_t7_1.note.value().inner() + delta),
value: NoteValue::from_raw(zsa_spend_t7_1.note.value().inner() + delta_1),
asset: zsa_spend_t7_1.note.asset(),
},
TestOutputInfo {
value: NoteValue::from_raw(zsa_spend_t7_2.note.value().inner() - delta),
value: NoteValue::from_raw(zsa_spend_t7_2.note.value().inner() - delta_1),
asset: zsa_spend_t7_2.note.asset(),
},
],