Skip to content

Commit

Permalink
Add verify_rho_computation_for_issuance_notes tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ConstanceBeguier committed Jan 13, 2025
1 parent 195a058 commit bf101d1
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 27 deletions.
163 changes: 150 additions & 13 deletions src/issuance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,20 +731,26 @@ impl fmt::Display for Error {
#[cfg(test)]
mod tests {
use super::{AssetSupply, IssueBundle, IssueInfo};
use crate::issuance::Error::{
AssetBaseCannotBeIdentityPoint, IssueActionNotFound,
IssueActionPreviouslyFinalizedAssetBase, IssueBundleIkMismatchAssetBase,
IssueBundleInvalidSignature, WrongAssetDescSize,
use crate::{
builder::{Builder, BundleType},
circuit::ProvingKey,
issuance::Error::{
AssetBaseCannotBeIdentityPoint, IssueActionNotFound,
IssueActionPreviouslyFinalizedAssetBase, IssueBundleIkMismatchAssetBase,
IssueBundleInvalidSignature, WrongAssetDescSize,
},
issuance::{is_reference_note, verify_issue_bundle, IssueAction, Signed, Unauthorized},
keys::{
FullViewingKey, IssuanceAuthorizingKey, IssuanceValidatingKey, Scope,
SpendAuthorizingKey, SpendingKey,
},
note::{rho_for_issuance_note, AssetBase, ExtractedNoteCommitment, Nullifier, Rho},
orchard_flavor::OrchardZSA,
tree::{MerkleHashOrchard, MerklePath},
value::NoteValue,
Address, Bundle, Note,
};
use crate::issuance::{
is_reference_note, verify_issue_bundle, IssueAction, Signed, Unauthorized,
};
use crate::keys::{
FullViewingKey, IssuanceAuthorizingKey, IssuanceValidatingKey, Scope, SpendingKey,
};
use crate::note::{AssetBase, Nullifier, Rho};
use crate::value::NoteValue;
use crate::{Address, Note};
use bridgetree::BridgeTree;
use group::{Group, GroupEncoding};
use nonempty::NonEmpty;
use pasta_curves::pallas::{Point, Scalar};
Expand Down Expand Up @@ -1670,6 +1676,137 @@ mod tests {
assert_eq!(action2.notes.get(1).unwrap().value().inner(), 10);
assert_eq!(bundle.get_action_by_desc(&asset_desc_2).unwrap(), action2);
}

#[test]
fn verify_rho_computation_for_issuance_notes() {
// Setup keys
let pk = ProvingKey::build::<OrchardZSA>();
let sk = SpendingKey::from_bytes([1; 32]).unwrap();
let fvk = FullViewingKey::from(&sk);
let recipient = fvk.address_at(0u32, Scope::External);
let isk = IssuanceAuthorizingKey::from_bytes([2; 32]).unwrap();
let ik = IssuanceValidatingKey::from(&isk);

// Setup note and merkle tree
let mut rng = OsRng;
let asset1 = AssetBase::derive(&ik, b"zsa_asset1");
let note1 = Note::new(
recipient,
NoteValue::from_raw(10),
asset1,
Rho::from_nf_old(Nullifier::dummy(&mut rng)),
&mut rng,
);
// Build the merkle tree with only note1
let mut tree = BridgeTree::<MerkleHashOrchard, u32, 32>::new(100);
let cmx: ExtractedNoteCommitment = note1.commitment().into();
let leaf = MerkleHashOrchard::from_cmx(&cmx);
tree.append(leaf);
let position = tree.mark().unwrap();
let root = tree.root(0).unwrap();
let anchor = root.into();
let auth_path = tree.witness(position, 0).unwrap();
let merkle_path = MerklePath::from_parts(
u64::from(position).try_into().unwrap(),
auth_path[..].try_into().unwrap(),
);

// Create a transfer bundle
let mut builder = Builder::new(BundleType::DEFAULT_ZSA, anchor);
builder.add_spend(fvk, note1, merkle_path).unwrap();
builder
.add_output(None, recipient, NoteValue::from_raw(5), asset1, None)
.unwrap();
builder
.add_output(None, recipient, NoteValue::from_raw(5), asset1, None)
.unwrap();
let unauthorized = builder.build(&mut rng).unwrap().0;
let sighash = unauthorized.commitment().into();
let proven = unauthorized.create_proof(&pk, &mut rng).unwrap();
let authorized: Bundle<_, i64, OrchardZSA> = proven
.apply_signatures(rng, sighash, &[SpendAuthorizingKey::from(&sk)])
.unwrap();

// Create an issue bundle
let asset2 = "asset2".to_string();
let asset3 = "asset3".to_string();
let (mut bundle, asset) = IssueBundle::new(
ik,
asset2.clone().into_bytes(),
Some(IssueInfo {
recipient,
value: NoteValue::from_raw(5),
}),
true,
rng,
)
.unwrap();

let another_asset = bundle
.add_recipient(
asset2.as_bytes(),
recipient,
NoteValue::from_raw(10),
false,
rng,
)
.unwrap();
assert_eq!(asset, another_asset);

let third_asset = bundle
.add_recipient(
asset3.as_bytes(),
recipient,
NoteValue::from_raw(10),
true,
rng,
)
.unwrap();
assert_ne!(asset, third_asset);

// Check that all rho values are zero.
bundle.actions().iter().for_each(|action| {
action
.notes()
.iter()
.for_each(|note| assert_eq!(note.rho(), Rho::zero()))
});

let partially_prepared_bundle =
bundle.update_rho(*authorized.actions().first().nullifier());

assert_eq!(partially_prepared_bundle.actions().len(), 2);
assert_eq!(
partially_prepared_bundle
.actions()
.get(0)
.unwrap()
.notes()
.len(),
3
);
assert_eq!(
partially_prepared_bundle
.actions()
.get(1)
.unwrap()
.notes()
.len(),
2
);

// Check the rho value for each issuance note in the issue bundle
for (index_action, action) in partially_prepared_bundle.actions.iter().enumerate() {
for (index_note, note) in action.notes.iter().enumerate() {
let expected_rho = rho_for_issuance_note(
*authorized.actions().first().nullifier(),
index_action.try_into().unwrap(),
index_note.try_into().unwrap(),
);
assert_eq!(note.rho(), expected_rho);
}
}
}
}

/// Generators for property testing.
Expand Down
40 changes: 26 additions & 14 deletions src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,23 +365,35 @@ impl Note {
index_action: u32,
index_note: u32,
) {
self.rho = Rho(to_base(
Params::new()
.hash_length(64)
.personal(ZSA_ISSUE_NOTE_RHO_PERSONALIZATION)
.to_state()
.update(&nullifier.to_bytes())
.update(&[0x84])
.update(index_action.to_le_bytes().as_ref())
.update(index_note.to_le_bytes().as_ref())
.finalize()
.as_bytes()
.try_into()
.unwrap(),
));
self.rho = rho_for_issuance_note(nullifier, index_action, index_note);
}
}

/// Evaluate the rho value of the issuance note (see
/// [ZIP-227: Issuance of Zcash Shielded Assets][zip227]).
///
/// [zip227]: https://zips.z.cash/zip-0227
pub(crate) fn rho_for_issuance_note(
nullifier: Nullifier,
index_action: u32,
index_note: u32,
) -> Rho {
Rho(to_base(
Params::new()
.hash_length(64)
.personal(ZSA_ISSUE_NOTE_RHO_PERSONALIZATION)
.to_state()
.update(&nullifier.to_bytes())
.update(&[0x84])
.update(index_action.to_le_bytes().as_ref())
.update(index_note.to_le_bytes().as_ref())
.finalize()
.as_bytes()
.try_into()
.unwrap(),
))
}

/// An encrypted note.
#[derive(Clone)]
pub struct TransmittedNoteCiphertext<D: OrchardDomainCommon> {
Expand Down

0 comments on commit bf101d1

Please sign in to comment.