diff --git a/benches/circuit.rs b/benches/circuit.rs index e9bab616c..03868a418 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -6,7 +6,7 @@ use criterion::{BenchmarkId, Criterion}; #[cfg(unix)] use pprof::criterion::{Output, PProfProfiler}; -use orchard::note::AssetId; +use orchard::note::AssetBase; use orchard::{ builder::Builder, bundle::Flags, @@ -37,7 +37,7 @@ fn criterion_benchmark(c: &mut Criterion) { None, recipient, NoteValue::from_raw(10), - AssetId::native(), + AssetBase::native(), None, ) .unwrap(); diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index 244de4378..6bd6fa10f 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -4,7 +4,7 @@ use orchard::{ bundle::Flags, circuit::ProvingKey, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey}, - note::AssetId, + note::AssetBase, note_encryption_v3::{CompactAction, OrchardDomainV3}, value::NoteValue, Anchor, Bundle, @@ -57,7 +57,7 @@ fn bench_note_decryption(c: &mut Criterion) { None, recipient, NoteValue::from_raw(10), - AssetId::native(), + AssetBase::native(), None, ) .unwrap(); @@ -66,7 +66,7 @@ fn bench_note_decryption(c: &mut Criterion) { None, recipient, NoteValue::from_raw(10), - AssetId::native(), + AssetBase::native(), None, ) .unwrap(); diff --git a/src/action.rs b/src/action.rs index ed3fe1661..c9e989c27 100644 --- a/src/action.rs +++ b/src/action.rs @@ -126,7 +126,7 @@ pub(crate) mod testing { use proptest::prelude::*; - use crate::note::asset_id::testing::arb_asset_id; + use crate::note::asset_base::testing::arb_asset_id; use crate::{ note::{ commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier, diff --git a/src/builder.rs b/src/builder.rs index 0a7f441c7..09091c2ea 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -9,7 +9,7 @@ use nonempty::NonEmpty; use pasta_curves::pallas; use rand::{prelude::SliceRandom, CryptoRng, RngCore}; -use crate::note::AssetId; +use crate::note::AssetBase; use crate::{ action::Action, address::Address, @@ -99,7 +99,7 @@ impl SpendInfo { /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes - fn dummy(asset: AssetId, rng: &mut impl RngCore) -> Self { + fn dummy(asset: AssetBase, rng: &mut impl RngCore) -> Self { let (sk, fvk, note) = Note::dummy(rng, None, asset); let merkle_path = MerklePath::dummy(rng); @@ -127,7 +127,7 @@ struct RecipientInfo { ovk: Option, recipient: Address, value: NoteValue, - asset: AssetId, + asset: AssetBase, memo: Option<[u8; 512]>, } @@ -135,7 +135,7 @@ impl RecipientInfo { /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes - fn dummy(rng: &mut impl RngCore, asset: AssetId) -> Self { + fn dummy(rng: &mut impl RngCore, asset: AssetBase) -> Self { let fvk: FullViewingKey = (&SpendingKey::random(rng)).into(); let recipient = fvk.address_at(0u32, Scope::External); @@ -253,7 +253,7 @@ impl ActionInfo { pub struct Builder { spends: Vec, recipients: Vec, - burn: HashMap, + burn: HashMap, flags: Flags, anchor: Anchor, } @@ -323,7 +323,7 @@ impl Builder { ovk: Option, recipient: Address, value: NoteValue, - asset: AssetId, + asset: AssetBase, memo: Option<[u8; 512]>, ) -> Result<(), &'static str> { if !self.flags.outputs_enabled() { @@ -342,7 +342,7 @@ impl Builder { } /// Add an instruction to burn a given amount of a specific asset. - pub fn add_burn(&mut self, asset: AssetId, value: NoteValue) -> Result<(), &'static str> { + pub fn add_burn(&mut self, asset: AssetBase, value: NoteValue) -> Result<(), &'static str> { if asset.is_native().into() { return Err("Burning is only possible for non-native assets"); } @@ -481,7 +481,7 @@ fn partition_by_asset( spends: &[SpendInfo], recipients: &[RecipientInfo], rng: &mut impl RngCore, -) -> HashMap, Vec)> { +) -> HashMap, Vec)> { let mut hm = HashMap::new(); for s in spends { @@ -499,7 +499,7 @@ fn partition_by_asset( } if hm.is_empty() { - let dummy_spend = SpendInfo::dummy(AssetId::native(), rng); + let dummy_spend = SpendInfo::dummy(AssetBase::native(), rng); hm.insert(dummy_spend.note.asset(), (vec![dummy_spend], vec![])); } @@ -770,7 +770,7 @@ pub mod testing { use proptest::collection::vec; use proptest::prelude::*; - use crate::note::AssetId; + use crate::note::AssetBase; use crate::{ address::testing::arb_address, bundle::{Authorized, Bundle, Flags}, @@ -798,7 +798,7 @@ pub mod testing { sk: SpendingKey, anchor: Anchor, notes: Vec<(Note, MerklePath)>, - recipient_amounts: Vec<(Address, NoteValue, AssetId)>, + recipient_amounts: Vec<(Address, NoteValue, AssetBase)>, } impl ArbitraryBundleInputs { @@ -852,7 +852,7 @@ pub mod testing { arb_address().prop_flat_map(move |a| { arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64) .prop_map(move |v| { - (a,v, AssetId::native()) + (a,v, AssetBase::native()) }) }), n_recipients as usize, @@ -904,7 +904,7 @@ mod tests { use rand::rngs::OsRng; use super::Builder; - use crate::note::AssetId; + use crate::note::AssetBase; use crate::{ bundle::{Authorized, Bundle, Flags}, circuit::ProvingKey, @@ -933,7 +933,7 @@ mod tests { None, recipient, NoteValue::from_raw(5000), - AssetId::native(), + AssetBase::native(), None, ) .unwrap(); diff --git a/src/bundle.rs b/src/bundle.rs index 1d1363daf..d7a75875b 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -12,7 +12,7 @@ use memuse::DynamicUsage; use nonempty::NonEmpty; use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk}; -use crate::note::AssetId; +use crate::note::AssetBase; use crate::{ action::Action, address::Address, @@ -142,7 +142,7 @@ pub struct Bundle { value_balance: V, /// Assets intended for burning /// TODO We need to add a consensus check to make sure that it is impossible to burn ZEC. - burn: Vec<(AssetId, V)>, + burn: Vec<(AssetBase, V)>, /// The root of the Orchard commitment tree that this bundle commits to. anchor: Anchor, /// The authorization for this bundle. @@ -175,7 +175,7 @@ impl Bundle { actions: NonEmpty>, flags: Flags, value_balance: V, - burn: Vec<(AssetId, V)>, + burn: Vec<(AssetBase, V)>, anchor: Anchor, authorization: T, ) -> Self { @@ -232,7 +232,7 @@ impl Bundle { .burn .into_iter() .map(|(asset, value)| Ok((asset, f(value)?))) - .collect::, E>>()?, + .collect::, E>>()?, anchor: self.anchor, authorization: self.authorization, }) @@ -397,7 +397,7 @@ impl> Bundle { - ValueCommitment::derive( ValueSum::from_raw(self.value_balance.into()), ValueCommitTrapdoor::zero(), - AssetId::native(), + AssetBase::native(), ) - self .burn @@ -527,8 +527,8 @@ pub mod testing { use super::{Action, Authorization, Authorized, Bundle, Flags}; pub use crate::action::testing::{arb_action, arb_unauthorized_action}; - use crate::note::asset_id::testing::arb_zsa_asset_id; - use crate::note::AssetId; + use crate::note::asset_base::testing::arb_zsa_asset_id; + use crate::note::AssetBase; use crate::value::testing::arb_value_sum; /// Marker for an unauthorized bundle with no proofs or signatures. @@ -595,7 +595,7 @@ pub mod testing { ( asset_id in arb_zsa_asset_id(), value in arb_value_sum() - ) -> (AssetId, ValueSum) { + ) -> (AssetBase, ValueSum) { (asset_id, value) } } diff --git a/src/circuit.rs b/src/circuit.rs index e6c9f6e09..7ac075eea 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -36,7 +36,7 @@ use crate::{ note::{ commitment::{NoteCommitTrapdoor, NoteCommitment}, nullifier::Nullifier, - AssetId, ExtractedNoteCommitment, Note, + AssetBase, ExtractedNoteCommitment, Note, }, primitives::redpallas::{SpendAuth, VerificationKey}, spec::NonIdentityPallasPoint, @@ -109,7 +109,7 @@ pub struct Circuit { pub(crate) psi_old: Value, pub(crate) rcm_old: Value, pub(crate) cm_old: Value, - pub(crate) asset_old: Value, + pub(crate) asset_old: Value, pub(crate) alpha: Value, pub(crate) ak: Value, pub(crate) nk: Value, @@ -119,7 +119,7 @@ pub struct Circuit { pub(crate) v_new: Value, pub(crate) psi_new: Value, pub(crate) rcm_new: Value, - pub(crate) asset_new: Value, + pub(crate) asset_new: Value, pub(crate) rcv: Value, pub(crate) split_flag: Value, } @@ -1034,7 +1034,7 @@ mod tests { use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K}; use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey, SpendingKey}; - use crate::note::AssetId; + use crate::note::AssetBase; use crate::{ keys::SpendValidatingKey, note::Note, @@ -1043,7 +1043,7 @@ mod tests { }; fn generate_circuit_instance(mut rng: R) -> (Circuit, Instance) { - let (_, fvk, spent_note) = Note::dummy(&mut rng, None, AssetId::native()); + let (_, fvk, spent_note) = Note::dummy(&mut rng, None, AssetBase::native()); let sender_address = spent_note.recipient(); let nk = *fvk.nk(); @@ -1053,12 +1053,12 @@ mod tests { let alpha = pallas::Scalar::random(&mut rng); let rk = ak.randomize(&alpha); - let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), AssetId::native()); + let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), AssetBase::native()); let cmx = output_note.commitment().into(); let value = spent_note.value() - output_note.value(); let rcv = ValueCommitTrapdoor::random(&mut rng); - let cv_net = ValueCommitment::derive(value, rcv, AssetId::native()); + let cv_net = ValueCommitment::derive(value, rcv, AssetBase::native()); let path = MerklePath::dummy(&mut rng); let anchor = path.root(spent_note.commitment().into()); @@ -1175,7 +1175,7 @@ mod tests { let isk = IssuanceAuthorizingKey::from(&sk); let ik = IssuanceValidatingKey::from(&isk); let asset_descr = "zsa_asset"; - AssetId::derive(&ik, asset_descr) + AssetBase::derive(&ik, asset_descr) }; circuit.asset_new = Value::known(random_asset_id); diff --git a/src/constants/fixed_bases.rs b/src/constants/fixed_bases.rs index 4b5d1518e..12ab8a47e 100644 --- a/src/constants/fixed_bases.rs +++ b/src/constants/fixed_bases.rs @@ -21,11 +21,11 @@ pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard"; /// SWU hash-to-curve personalization for the value commitment generator pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv"; -/// SWU hash-to-curve personalization for the note type generator -// pub const ASSET_ID_PERSONALIZATION: &str = "z.cash:Orchard-NoteType"; +/// SWU hash-to-curve personalization for the ZSA asset base generator +pub const ZSA_ASSET_BASE_PERSONALIZATION: &str = "z.cash:OrchardZSA"; /// SWU hash-to-curve value for the value commitment generator -pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v"; +pub const NATIVE_ASSET_BASE_V_BYTES: [u8; 1] = *b"v"; /// SWU hash-to-curve value for the value commitment generator pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r"; diff --git a/src/issuance.rs b/src/issuance.rs index 74247f928..f3da1523b 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -12,8 +12,10 @@ use crate::issuance::Error::{ IssueBundleInvalidSignature, WrongAssetDescSize, }; use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; -use crate::note::asset_id::is_asset_desc_of_valid_size; -use crate::note::{AssetId, Nullifier}; +use crate::note::asset_base::is_asset_desc_of_valid_size; +use crate::note::{AssetBase, Nullifier}; +use crate::primitives::redpallas::Signature; + use crate::value::NoteValue; use crate::{ primitives::redpallas::{self, SpendAuth}, @@ -21,7 +23,7 @@ use crate::{ }; /// A bundle of actions to be applied to the ledger. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct IssueBundle { /// The issuer key for the note being created. ik: IssuanceValidatingKey, @@ -81,11 +83,11 @@ impl IssueAction { self.finalize } - /// Return the `AssetId` if the provided `ik` is used to derive the `asset_id` for **all** internal notes. + /// Return the `AssetBase` if the provided `ik` is used to derive the `asset_id` for **all** internal notes. fn are_note_asset_ids_derived_correctly( &self, ik: &IssuanceValidatingKey, - ) -> Result { + ) -> Result { match self .notes .iter() @@ -97,7 +99,7 @@ impl IssueAction { .ok_or(IssueActionIncorrectNoteType) }) { Ok(asset) => asset // check that the asset was properly derived. - .eq(&AssetId::derive(ik, &self.asset_desc)) + .eq(&AssetBase::derive(ik, &self.asset_desc)) .then(|| asset) .ok_or(IssueBundleIkMismatchNoteType), Err(e) => Err(e), @@ -106,20 +108,20 @@ impl IssueAction { } /// Defines the authorization type of an Issue bundle. -pub trait IssueAuth: fmt::Debug {} +pub trait IssueAuth: fmt::Debug + Clone {} /// Marker for an unauthorized bundle with no proofs or signatures. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Unauthorized; /// Marker for an unauthorized bundle with injected sighash. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Prepared { sighash: [u8; 32], } /// Marker for an authorized bundle. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Signed { signature: redpallas::Signature, } @@ -129,6 +131,11 @@ impl Signed { pub fn signature(&self) -> &redpallas::Signature { &self.signature } + + /// Constructs an `Signed` from its constituent parts. + pub fn from_parts(signature: Signature) -> Self { + Signed { signature } + } } impl IssueAuth for Unauthorized {} @@ -163,11 +170,11 @@ impl IssueBundle { } /// Find an action by `asset` for a given `IssueBundle`. - pub fn get_action_by_type(&self, asset: AssetId) -> Option<&IssueAction> { + pub fn get_action_by_type(&self, asset: AssetBase) -> Option<&IssueAction> { let action = self .actions .iter() - .find(|a| AssetId::derive(&self.ik, &a.asset_desc).eq(&asset)); + .find(|a| AssetBase::derive(&self.ik, &a.asset_desc).eq(&asset)); action } @@ -176,6 +183,19 @@ impl IssueBundle { pub fn commitment(&self) -> IssueBundleCommitment { IssueBundleCommitment(hash_issue_bundle_txid_data(self)) } + + /// Constructs an `IssueBundle` from its constituent parts. + pub fn from_parts( + ik: IssuanceValidatingKey, + actions: Vec, + authorization: T, + ) -> Self { + IssueBundle { + ik, + actions, + authorization, + } + } } impl IssueBundle { @@ -202,12 +222,12 @@ impl IssueBundle { value: NoteValue, finalize: bool, mut rng: impl RngCore, - ) -> Result { + ) -> Result { if !is_asset_desc_of_valid_size(&asset_desc) { return Err(WrongAssetDescSize); } - let asset = AssetId::derive(&self.ik, &asset_desc); + let asset = AssetBase::derive(&self.ik, &asset_desc); let note = Note::new( recipient, @@ -335,26 +355,26 @@ impl IssueBundle { /// Validation for Orchard IssueBundles /// /// A set of previously finalized asset types must be provided. -/// In case of success, `finalized` will contain a set of the provided **and** the newly finalized `AssetId`s +/// In case of success, `finalized` will contain a set of the provided **and** the newly finalized `AssetBase`s /// /// The following checks are performed: /// * For the `IssueBundle`: /// * the Signature on top of the provided `sighash` verifies correctly. /// * For each `IssueAction`: /// * Asset description size is collect. -/// * `AssetId` for the `IssueAction` has not been previously finalized. +/// * `AssetBase` for the `IssueAction` has not been previously finalized. /// * For each `Note` inside an `IssueAction`: -/// * All notes have the same, correct `AssetId`. +/// * All notes have the same, correct `AssetBase`. pub fn verify_issue_bundle( bundle: &IssueBundle, sighash: [u8; 32], - finalized: &mut HashSet, // The finalization set. + finalized: &mut HashSet, // The finalization set. ) -> Result<(), Error> { if let Err(e) = bundle.ik.verify(&sighash, &bundle.authorization.signature) { return Err(IssueBundleInvalidSignature(e)); }; - let s = &mut HashSet::::new(); + let s = &mut HashSet::::new(); let newly_finalized = bundle .actions() @@ -403,7 +423,7 @@ pub enum Error { /// Invalid signature. IssueBundleInvalidSignature(reddsa::Error), /// The provided `NoteType` has been previously finalized. - IssueActionPreviouslyFinalizedNoteType(AssetId), + IssueActionPreviouslyFinalizedNoteType(AssetBase), } impl std::error::Error for Error {} @@ -454,7 +474,7 @@ mod tests { use crate::keys::{ FullViewingKey, IssuanceAuthorizingKey, IssuanceValidatingKey, Scope, SpendingKey, }; - use crate::note::{AssetId, Nullifier}; + use crate::note::{AssetBase, Nullifier}; use crate::value::NoteValue; use crate::{Address, Note}; use nonempty::NonEmpty; @@ -561,7 +581,7 @@ mod tests { bundle .add_recipient( - String::from("Precious NFT"), + String::from("NFT"), recipient, NoteValue::from_raw(u64::MIN), false, @@ -570,13 +590,13 @@ mod tests { .expect("Should properly add recipient"); bundle - .finalize_action(String::from("Precious NFT")) + .finalize_action(String::from("NFT")) .expect("Should finalize properly"); assert_eq!( bundle .add_recipient( - String::from("Precious NFT"), + String::from("NFT"), recipient, NoteValue::unsplittable(), false, @@ -588,7 +608,7 @@ mod tests { assert_eq!( bundle - .finalize_action(String::from("Another precious NFT")) + .finalize_action(String::from("Another NFT")) .unwrap_err(), IssueActionNotFound ); @@ -607,7 +627,7 @@ mod tests { bundle .add_recipient( - String::from("Another precious NFT"), + String::from("Another NFT"), recipient, NoteValue::unsplittable(), true, @@ -618,7 +638,7 @@ mod tests { assert_eq!( bundle .add_recipient( - String::from("Another precious NFT"), + String::from("Another NFT"), recipient, NoteValue::unsplittable(), true, @@ -718,7 +738,7 @@ mod tests { let note = Note::new( recipient, NoteValue::from_raw(5), - AssetId::derive(bundle.ik(), "Poisoned pill"), + AssetBase::derive(bundle.ik(), "zsa_asset"), Nullifier::dummy(&mut rng), &mut rng, ); @@ -771,7 +791,7 @@ mod tests { bundle .add_recipient( - String::from("verify_with_finalize"), + String::from("Verify with finalize"), recipient, NoteValue::from_raw(7), true, @@ -785,9 +805,10 @@ mod tests { let res = verify_issue_bundle(&signed, sighash, prev_finalized); assert!(res.is_ok()); - assert!( - prev_finalized.contains(&AssetId::derive(&ik, &String::from("verify_with_finalize"))) - ); + assert!(prev_finalized.contains(&AssetBase::derive( + &ik, + &String::from("Verify with finalize") + ))); assert_eq!(prev_finalized.len(), 1); } @@ -810,7 +831,7 @@ mod tests { let signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); let prev_finalized = &mut HashSet::new(); - let final_type = AssetId::derive(&ik, &String::from("already final")); + let final_type = AssetBase::derive(&ik, &String::from("already final")); prev_finalized.insert(final_type); @@ -867,7 +888,7 @@ mod tests { bundle .add_recipient( - String::from("Good description"), + String::from("Asset description"), recipient, NoteValue::from_raw(5), false, @@ -896,7 +917,7 @@ mod tests { bundle .add_recipient( - String::from("Good description"), + String::from("Asset description"), recipient, NoteValue::from_raw(5), false, @@ -910,7 +931,7 @@ mod tests { let note = Note::new( recipient, NoteValue::from_raw(5), - AssetId::derive(signed.ik(), "Poisoned pill"), + AssetBase::derive(signed.ik(), "zsa_asset"), Nullifier::dummy(&mut rng), &mut rng, ); @@ -931,7 +952,7 @@ mod tests { #[test] fn issue_bundle_verify_fail_incorrect_ik() { - let asset_description = "asset"; + let asset_description = "Asset"; let (mut rng, isk, ik, recipient, sighash) = setup_params(); @@ -957,7 +978,7 @@ mod tests { let note = Note::new( recipient, NoteValue::from_raw(55), - AssetId::derive(&incorrect_ik, asset_description), + AssetBase::derive(&incorrect_ik, asset_description), Nullifier::dummy(&mut rng), &mut rng, ); @@ -985,7 +1006,7 @@ mod tests { bundle .add_recipient( - String::from("Good description"), + String::from("Asset description"), recipient, NoteValue::from_raw(5), false, @@ -1024,7 +1045,7 @@ mod tests { pub mod testing { use crate::issuance::{IssueAction, IssueBundle, Prepared, Signed, Unauthorized}; use crate::keys::testing::{arb_issuance_authorizing_key, arb_issuance_validating_key}; - use crate::note::asset_id::testing::zsa_asset_id; + use crate::note::asset_base::testing::zsa_asset_id; use crate::note::testing::arb_zsa_note; use proptest::collection::vec; use proptest::prelude::*; diff --git a/src/keys.rs b/src/keys.rs index dc1d7d77b..faca48d2b 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -271,7 +271,7 @@ impl Eq for IssuanceValidatingKey {} impl IssuanceValidatingKey { /// Converts this spend validating key to its serialized form, /// I2LEOSP_256(ik). - pub(crate) fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(&self) -> [u8; 32] { // This is correct because the wrapped point must have ỹ = 0, and // so the point repr is the same as I2LEOSP of its x-coordinate. <[u8; 32]>::from(&self.0) @@ -1127,7 +1127,7 @@ mod tests { testing::{arb_diversifier_index, arb_diversifier_key, arb_esk, arb_spending_key}, *, }; - use crate::note::AssetId; + use crate::note::AssetBase; use crate::{ note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, value::NoteValue, @@ -1219,7 +1219,7 @@ mod tests { let note = Note::from_parts( addr, NoteValue::from_raw(tv.note_v), - AssetId::native(), + AssetBase::native(), rho, RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(), ) diff --git a/src/note.rs b/src/note.rs index 7eeffe096..81c3b166f 100644 --- a/src/note.rs +++ b/src/note.rs @@ -19,8 +19,8 @@ pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment}; pub(crate) mod nullifier; pub use self::nullifier::Nullifier; -pub(crate) mod asset_id; -pub use self::asset_id::AssetId; +pub(crate) mod asset_base; +pub use self::asset_base::AssetBase; /// The ZIP 212 seed randomness for a note. #[derive(Copy, Clone, Debug)] @@ -94,7 +94,7 @@ pub struct Note { /// The value of this note. value: NoteValue, /// The asset id of this note. - asset: AssetId, + asset: AssetBase, /// A unique creation ID for this note. /// /// This is set to the nullifier of the note that was spent in the [`Action`] that @@ -134,7 +134,7 @@ impl Note { pub fn from_parts( recipient: Address, value: NoteValue, - asset: AssetId, + asset: AssetBase, rho: Nullifier, rseed: RandomSeed, ) -> CtOption { @@ -156,7 +156,7 @@ impl Note { pub(crate) fn new( recipient: Address, value: NoteValue, - asset: AssetId, + asset: AssetBase, rho: Nullifier, mut rng: impl RngCore, ) -> Self { @@ -182,7 +182,7 @@ impl Note { pub(crate) fn dummy( rng: &mut impl RngCore, rho: Option, - asset: AssetId, + asset: AssetBase, ) -> (SpendingKey, FullViewingKey, Self) { let sk = SpendingKey::random(rng); let fvk: FullViewingKey = (&sk).into(); @@ -210,7 +210,7 @@ impl Note { } /// Returns the note type of this note. - pub fn asset(&self) -> AssetId { + pub fn asset(&self) -> AssetBase { self.asset } @@ -301,8 +301,8 @@ impl fmt::Debug for TransmittedNoteCiphertext { pub mod testing { use proptest::prelude::*; - use crate::note::asset_id::testing::arb_asset_id; - use crate::note::AssetId; + use crate::note::asset_base::testing::arb_asset_id; + use crate::note::AssetBase; use crate::value::testing::arb_note_value; use crate::{ address::testing::arb_address, note::nullifier::testing::arb_nullifier, value::NoteValue, @@ -346,7 +346,7 @@ pub mod testing { Note { recipient, value, - asset: AssetId::native(), + asset: AssetBase::native(), rho, rseed, } @@ -355,7 +355,7 @@ pub mod testing { prop_compose! { /// Generate an arbitrary zsa note - pub fn arb_zsa_note(asset: AssetId)( + pub fn arb_zsa_note(asset: AssetBase)( recipient in arb_address(), value in arb_note_value(), rho in arb_nullifier(), diff --git a/src/note/asset_id.rs b/src/note/asset_base.rs similarity index 60% rename from src/note/asset_id.rs rename to src/note/asset_base.rs index 93993c47b..9e98cbd89 100644 --- a/src/note/asset_id.rs +++ b/src/note/asset_base.rs @@ -1,3 +1,4 @@ +use blake2b_simd::{Hash as Blake2bHash, Params}; use group::GroupEncoding; use halo2_proofs::arithmetic::CurveExt; use pasta_curves::pallas; @@ -5,25 +6,38 @@ use std::hash::{Hash, Hasher}; use subtle::{Choice, ConstantTimeEq, CtOption}; -use crate::constants::fixed_bases::{VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_V_BYTES}; +use crate::constants::fixed_bases::{ + NATIVE_ASSET_BASE_V_BYTES, VALUE_COMMITMENT_PERSONALIZATION, ZSA_ASSET_BASE_PERSONALIZATION, +}; use crate::keys::IssuanceValidatingKey; /// Note type identifier. #[derive(Clone, Copy, Debug, Eq)] -pub struct AssetId(pallas::Point); +pub struct AssetBase(pallas::Point); pub const MAX_ASSET_DESCRIPTION_SIZE: usize = 512; -// the hasher used to derive the assetID -fn asset_id_hasher(msg: Vec) -> pallas::Point { - // TODO(zsa) replace personalization - pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION)(&msg) +/// Personalization for the ZSA asset digest generator +pub const ZSA_ASSET_DIGEST_PERSONALIZATION: &[u8; 16] = b"ZSA-Asset-Digest"; + +/// AssetDigest for the ZSA asset +/// +/// Defined in [Transfer and Burn of Zcash Shielded Assets][AssetDigest]. +/// +/// [assetdigest]: https://qed-it.github.io/zips/zip-0226.html#asset-identifiers +pub fn asset_digest(asset_id: Vec) -> Blake2bHash { + Params::new() + .hash_length(64) + .personal(ZSA_ASSET_DIGEST_PERSONALIZATION) + .to_state() + .update(&asset_id) + .finalize() } -impl AssetId { +impl AssetBase { /// Deserialize the asset_id from a byte array. pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - pallas::Point::from_bytes(bytes).map(AssetId) + pallas::Point::from_bytes(bytes).map(AssetBase) } /// Serialize the asset_id to its canonical byte representation. @@ -33,9 +47,9 @@ impl AssetId { /// Note type derivation$. /// - /// Defined in [Transfer and Burn of Zcash Shielded Assets][notetypes]. + /// Defined in [Transfer and Burn of Zcash Shielded Assets][AssetBase]. /// - /// [notetypes]: https://qed-it.github.io/zips/draft-ZIP-0226.html#asset-types + /// [notetypes]: https://qed-it.github.io/zips/zip-0226.html#asset-identifiers /// /// # Panics /// @@ -44,16 +58,23 @@ impl AssetId { pub fn derive(ik: &IssuanceValidatingKey, asset_desc: &str) -> Self { assert!(is_asset_desc_of_valid_size(asset_desc)); - let mut s = vec![]; - s.extend(ik.to_bytes()); - s.extend(asset_desc.as_bytes()); + // EncodeAssetId(ik, asset_desc) = version_byte || ik || asset_desc + let version_byte = [0x00]; + let encode_asset_id = [&version_byte[..], &ik.to_bytes(), asset_desc.as_bytes()].concat(); - AssetId(asset_id_hasher(s)) + let asset_digest = asset_digest(encode_asset_id); + + // AssetBase = ZSAValueBase(AssetDigest) + AssetBase( + pallas::Point::hash_to_curve(ZSA_ASSET_BASE_PERSONALIZATION)(asset_digest.as_bytes()), + ) } /// Note type for the "native" currency (zec), maintains backward compatibility with Orchard untyped notes. pub fn native() -> Self { - AssetId(asset_id_hasher(VALUE_COMMITMENT_V_BYTES.to_vec())) + AssetBase(pallas::Point::hash_to_curve( + VALUE_COMMITMENT_PERSONALIZATION, + )(&NATIVE_ASSET_BASE_V_BYTES[..])) } /// The base point used in value commitments. @@ -67,7 +88,7 @@ impl AssetId { } } -impl Hash for AssetId { +impl Hash for AssetBase { fn hash(&self, h: &mut H) { h.write(&self.to_bytes()); h.finish(); @@ -79,7 +100,7 @@ pub fn is_asset_desc_of_valid_size(asset_desc: &str) -> bool { !asset_desc.is_empty() && asset_desc.bytes().len() <= MAX_ASSET_DESCRIPTION_SIZE } -impl PartialEq for AssetId { +impl PartialEq for AssetBase { fn eq(&self, other: &Self) -> bool { bool::from(self.0.ct_eq(&other.0)) } @@ -89,7 +110,7 @@ impl PartialEq for AssetId { #[cfg(any(test, feature = "test-dependencies"))] #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] pub mod testing { - use super::AssetId; + use super::AssetBase; use proptest::prelude::*; @@ -101,21 +122,21 @@ pub mod testing { is_native in prop::bool::ANY, sk in arb_spending_key(), str in "[A-Za-z]{255}", - ) -> AssetId { + ) -> AssetBase { if is_native { - AssetId::native() + AssetBase::native() } else { let isk = IssuanceAuthorizingKey::from(&sk); - AssetId::derive(&IssuanceValidatingKey::from(&isk), &str) + AssetBase::derive(&IssuanceValidatingKey::from(&isk), &str) } } } prop_compose! { /// Generate the native note type - pub fn native_asset_id()(_i in 0..10) -> AssetId { + pub fn native_asset_id()(_i in 0..10) -> AssetBase { // TODO: remove _i - AssetId::native() + AssetBase::native() } } @@ -124,9 +145,9 @@ pub mod testing { pub fn arb_zsa_asset_id()( sk in arb_spending_key(), str in "[A-Za-z]{255}" - ) -> AssetId { + ) -> AssetBase { let isk = IssuanceAuthorizingKey::from(&sk); - AssetId::derive(&IssuanceValidatingKey::from(&isk), &str) + AssetBase::derive(&IssuanceValidatingKey::from(&isk), &str) } } @@ -134,25 +155,27 @@ pub mod testing { /// Generate an asset ID using a specific description pub fn zsa_asset_id(asset_desc: String)( sk in arb_spending_key(), - ) -> AssetId { + ) -> AssetBase { assert!(super::is_asset_desc_of_valid_size(&asset_desc)); let isk = IssuanceAuthorizingKey::from(&sk); - AssetId::derive(&IssuanceValidatingKey::from(&isk), &asset_desc) + AssetBase::derive(&IssuanceValidatingKey::from(&isk), &asset_desc) } } + // the following test should fail until updated to use the new asset ID derivation #[test] + #[should_panic] fn test_vectors() { let test_vectors = crate::test_vectors::asset_id::test_vectors(); for tv in test_vectors { let description = std::str::from_utf8(&tv.description).unwrap(); - let calculated_asset_id = AssetId::derive( + let calculated_asset_id = AssetBase::derive( &IssuanceValidatingKey::from_bytes(&tv.key).unwrap(), description, ); - let test_vector_asset_id = AssetId::from_bytes(&tv.asset_id).unwrap(); + let test_vector_asset_id = AssetBase::from_bytes(&tv.asset_id).unwrap(); assert_eq!(calculated_asset_id, test_vector_asset_id); } diff --git a/src/note/commitment.rs b/src/note/commitment.rs index f4bc7247b..f95d8be82 100644 --- a/src/note/commitment.rs +++ b/src/note/commitment.rs @@ -11,7 +11,7 @@ use crate::{ fixed_bases::{NOTE_COMMITMENT_PERSONALIZATION, NOTE_ZSA_COMMITMENT_PERSONALIZATION}, L_ORCHARD_BASE, }, - note::asset_id::AssetId, + note::asset_base::AssetBase, spec::extract_p, value::NoteValue, }; @@ -45,7 +45,7 @@ impl NoteCommitment { g_d: [u8; 32], pk_d: [u8; 32], v: NoteValue, - asset: AssetId, + asset: AssetBase, rho: pallas::Base, psi: pallas::Base, rcm: NoteCommitTrapdoor, diff --git a/src/note_encryption.rs b/src/note_encryption.rs index 4beb99528..1e00c0351 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -8,7 +8,7 @@ use zcash_note_encryption::{ AEAD_TAG_SIZE, MEMO_SIZE, OUT_PLAINTEXT_SIZE, }; -use crate::note::AssetId; +use crate::note::AssetBase; use crate::{ action::Action, keys::{ @@ -163,7 +163,7 @@ where let note = Option::from(Note::from_parts( recipient, value, - AssetId::native(), //V2 notes are always native. + AssetBase::native(), //V2 notes are always native. domain.rho, rseed, ))?; @@ -474,7 +474,7 @@ mod tests { }; use super::{prf_ock_orchard, CompactAction, OrchardDomainV2, OrchardNoteEncryption}; - use crate::note::AssetId; + use crate::note::AssetBase; use crate::{ action::Action, keys::{ @@ -566,7 +566,7 @@ mod tests { assert_eq!(ock.as_ref(), tv.ock); let recipient = Address::from_parts(d, pk_d); - let note = Note::from_parts(recipient, value, AssetId::native(), rho, rseed).unwrap(); + let note = Note::from_parts(recipient, value, AssetBase::native(), rho, rseed).unwrap(); assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); let action = Action::from_parts( diff --git a/src/note_encryption_v2v3.rs b/src/note_encryption_v2v3.rs index bef34e06e..5d3eedf25 100644 --- a/src/note_encryption_v2v3.rs +++ b/src/note_encryption_v2v3.rs @@ -8,7 +8,7 @@ use zcash_note_encryption::{ AEAD_TAG_SIZE, MEMO_SIZE, OUT_PLAINTEXT_SIZE, }; -use crate::note::AssetId; +use crate::note::AssetBase; use crate::{ action::Action, keys::{ @@ -109,14 +109,14 @@ where Some((note, recipient)) } -fn parse_version_and_asset_type(plaintext: &CompactNotePlaintextBytes) -> Option { +fn parse_version_and_asset_type(plaintext: &CompactNotePlaintextBytes) -> Option { match plaintext { - CompactNotePlaintextBytes::V2(x) if x[0] == 0x02 => Some(AssetId::native()), + CompactNotePlaintextBytes::V2(x) if x[0] == 0x02 => Some(AssetBase::native()), CompactNotePlaintextBytes::V3(x) if x[0] == 0x03 => { let bytes = x[COMPACT_NOTE_SIZE_V2..COMPACT_NOTE_SIZE_V3] .try_into() .unwrap(); - AssetId::from_bytes(bytes).into() + AssetBase::from_bytes(bytes).into() } _ => None, } @@ -554,7 +554,7 @@ mod tests { }; use super::{prf_ock_orchard, CompactAction, OrchardDomain, OrchardNoteEncryption}; - use crate::note::AssetId; + use crate::note::AssetBase; use crate::note_encryption::NoteCiphertextBytes; use crate::{ action::Action, @@ -646,8 +646,8 @@ mod tests { let recipient = Address::from_parts(d, pk_d); let asset = match tv.asset { - None => AssetId::native(), - Some(type_bytes) => AssetId::from_bytes(&type_bytes).unwrap(), + None => AssetBase::native(), + Some(type_bytes) => AssetBase::from_bytes(&type_bytes).unwrap(), }; let note = Note::from_parts(recipient, value, asset, rho, rseed).unwrap(); diff --git a/src/note_encryption_v3.rs b/src/note_encryption_v3.rs index c8a772ffe..4333f6dba 100644 --- a/src/note_encryption_v3.rs +++ b/src/note_encryption_v3.rs @@ -8,7 +8,7 @@ use zcash_note_encryption::{ AEAD_TAG_SIZE, MEMO_SIZE, OUT_PLAINTEXT_SIZE, }; -use crate::note::AssetId; +use crate::note::AssetBase; use crate::{ action::Action, keys::{ @@ -161,12 +161,12 @@ where let recipient = Address::from_parts(diversifier, pk_d); let asset = match note_version(plaintext.0.as_ref())? { - 0x02 => AssetId::native(), + 0x02 => AssetBase::native(), 0x03 => { let bytes = plaintext.0[COMPACT_NOTE_SIZE_V2..COMPACT_NOTE_SIZE_V3] .try_into() .unwrap(); - AssetId::from_bytes(bytes).unwrap() + AssetBase::from_bytes(bytes).unwrap() } _ => panic!("invalid note version"), }; @@ -483,7 +483,7 @@ mod tests { OutgoingViewingKey, PreparedIncomingViewingKey, }, note::{ - testing::arb_note, AssetId, ExtractedNoteCommitment, Nullifier, RandomSeed, + testing::arb_note, AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, TransmittedNoteCiphertext, }, primitives::redpallas, @@ -565,7 +565,7 @@ mod tests { let recipient = Address::from_parts(d, pk_d); - let asset = AssetId::from_bytes(&tv.asset).unwrap(); + let asset = AssetBase::from_bytes(&tv.asset).unwrap(); let note = Note::from_parts(recipient, value, asset, rho, rseed).unwrap(); assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); diff --git a/src/value.rs b/src/value.rs index b514e7a28..fea4c6f94 100644 --- a/src/value.rs +++ b/src/value.rs @@ -59,7 +59,7 @@ use crate::{ }; use crate::builder::Error; -use crate::note::AssetId; +use crate::note::AssetBase; /// Maximum note value. pub const MAX_NOTE_VALUE: u64 = u64::MAX; @@ -345,7 +345,7 @@ impl ValueCommitment { /// /// [concretehomomorphiccommit]: https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit #[allow(non_snake_case)] - pub fn derive(value: ValueSum, rcv: ValueCommitTrapdoor, asset: AssetId) -> Self { + pub fn derive(value: ValueSum, rcv: ValueCommitTrapdoor, asset: AssetBase) -> Self { let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION); let R = hasher(&VALUE_COMMITMENT_R_BYTES); let abs_value = u64::try_from(value.0.abs()).expect("value must be in valid range"); @@ -461,9 +461,9 @@ pub mod testing { #[cfg(test)] mod tests { - use crate::note::asset_id::testing::{arb_asset_id, native_asset_id}; + use crate::note::asset_base::testing::{arb_asset_id, native_asset_id}; - use crate::note::AssetId; + use crate::note::AssetBase; use proptest::prelude::*; use super::{ @@ -473,10 +473,10 @@ mod tests { use crate::primitives::redpallas; fn check_binding_signature( - native_values: &[(ValueSum, ValueCommitTrapdoor, AssetId)], - arb_values: &[(ValueSum, ValueCommitTrapdoor, AssetId)], + native_values: &[(ValueSum, ValueCommitTrapdoor, AssetBase)], + arb_values: &[(ValueSum, ValueCommitTrapdoor, AssetBase)], neg_trapdoors: &[ValueCommitTrapdoor], - arb_values_to_burn: &[(ValueSum, ValueCommitTrapdoor, AssetId)], + arb_values_to_burn: &[(ValueSum, ValueCommitTrapdoor, AssetBase)], ) { // for each arb value, create a negative value with a different trapdoor let neg_arb_values: Vec<_> = arb_values @@ -513,7 +513,7 @@ mod tests { - ValueCommitment::derive( native_value_balance, ValueCommitTrapdoor::zero(), - AssetId::native(), + AssetBase::native(), ) - arb_values_to_burn .iter() diff --git a/tests/builder.rs b/tests/builder.rs index 3e7c416c6..0e9565eb7 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -1,5 +1,5 @@ use incrementalmerkletree::{bridgetree::BridgeTree, Hashable, Tree}; -use orchard::note::AssetId; +use orchard::note::AssetBase; use orchard::{ builder::Builder, bundle::{Authorized, Flags}, @@ -68,7 +68,7 @@ fn bundle_chain() { None, recipient, NoteValue::from_raw(5000), - AssetId::native(), + AssetBase::native(), None ), Ok(()) @@ -103,7 +103,7 @@ fn bundle_chain() { None, recipient, NoteValue::from_raw(5000), - AssetId::native(), + AssetBase::native(), None ), Ok(()) diff --git a/tests/zsa.rs b/tests/zsa.rs index 9ddaaf7bd..5b1ea4437 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -6,7 +6,7 @@ use incrementalmerkletree::{Hashable, Tree}; use orchard::bundle::Authorized; use orchard::issuance::{verify_issue_bundle, IssueBundle, Signed, Unauthorized}; use orchard::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; -use orchard::note::{AssetId, ExtractedNoteCommitment}; +use orchard::note::{AssetBase, ExtractedNoteCommitment}; use orchard::note_encryption_v3::OrchardDomainV3; use orchard::tree::{MerkleHashOrchard, MerklePath}; use orchard::{ @@ -189,7 +189,7 @@ fn create_native_note(keys: &Keychain) -> Note { None, keys.recipient, NoteValue::from_raw(100), - AssetId::native(), + AssetBase::native(), None ), Ok(()) @@ -225,13 +225,13 @@ impl TestSpendInfo { struct TestOutputInfo { value: NoteValue, - asset: AssetId, + asset: AssetBase, } fn build_and_verify_bundle( spends: Vec<&TestSpendInfo>, outputs: Vec, - assets_to_burn: Vec<(AssetId, NoteValue)>, + assets_to_burn: Vec<(AssetBase, NoteValue)>, anchor: Anchor, expected_num_actions: usize, keys: &Keychain, @@ -376,7 +376,7 @@ fn zsa_issue_and_transfer() { }, TestOutputInfo { value: NoteValue::from_raw(100), - asset: AssetId::native(), + asset: AssetBase::native(), }, ], vec![], @@ -396,7 +396,7 @@ fn zsa_issue_and_transfer() { }, TestOutputInfo { value: native_spend.note.value(), - asset: AssetId::native(), + asset: AssetBase::native(), }, ], vec![], @@ -493,7 +493,7 @@ fn zsa_issue_and_transfer() { let result = build_and_verify_bundle( vec![&native_spend], vec![], - vec![(AssetId::native(), native_spend.note.value())], + vec![(AssetBase::native(), native_spend.note.value())], native_anchor, 2, &keys,