AssetBase spec update (#44)

- Renamed AssetId to AssetBase
- Changed the  AssetBase implementation to support the zip update.
- Updated visibility for various members of issuance.rs
This commit is contained in:
Paul 2023-03-02 14:26:19 +02:00 committed by GitHub
parent cbf0a3a651
commit 43d5e77d38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 202 additions and 158 deletions

View File

@ -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();

View File

@ -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();

View File

@ -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,

View File

@ -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<OutgoingViewingKey>,
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<SpendInfo>,
recipients: Vec<RecipientInfo>,
burn: HashMap<AssetId, ValueSum>,
burn: HashMap<AssetBase, ValueSum>,
flags: Flags,
anchor: Anchor,
}
@ -323,7 +323,7 @@ impl Builder {
ovk: Option<OutgoingViewingKey>,
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<AssetId, (Vec<SpendInfo>, Vec<RecipientInfo>)> {
) -> HashMap<AssetBase, (Vec<SpendInfo>, Vec<RecipientInfo>)> {
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<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
@ -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();

View File

@ -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<T: Authorization, V> {
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<T: Authorization, V> Bundle<T, V> {
actions: NonEmpty<Action<T::SpendAuth>>,
flags: Flags,
value_balance: V,
burn: Vec<(AssetId, V)>,
burn: Vec<(AssetBase, V)>,
anchor: Anchor,
authorization: T,
) -> Self {
@ -232,7 +232,7 @@ impl<T: Authorization, V> Bundle<T, V> {
.burn
.into_iter()
.map(|(asset, value)| Ok((asset, f(value)?)))
.collect::<Result<Vec<(AssetId, V0)>, E>>()?,
.collect::<Result<Vec<(AssetBase, V0)>, E>>()?,
anchor: self.anchor,
authorization: self.authorization,
})
@ -397,7 +397,7 @@ impl<T: Authorization, V: Copy + Into<i64>> Bundle<T, V> {
- 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)
}
}

View File

@ -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<pallas::Base>,
pub(crate) rcm_old: Value<NoteCommitTrapdoor>,
pub(crate) cm_old: Value<NoteCommitment>,
pub(crate) asset_old: Value<AssetId>,
pub(crate) asset_old: Value<AssetBase>,
pub(crate) alpha: Value<pallas::Scalar>,
pub(crate) ak: Value<SpendValidatingKey>,
pub(crate) nk: Value<NullifierDerivingKey>,
@ -119,7 +119,7 @@ pub struct Circuit {
pub(crate) v_new: Value<NoteValue>,
pub(crate) psi_new: Value<pallas::Base>,
pub(crate) rcm_new: Value<NoteCommitTrapdoor>,
pub(crate) asset_new: Value<AssetId>,
pub(crate) asset_new: Value<AssetBase>,
pub(crate) rcv: Value<ValueCommitTrapdoor>,
pub(crate) split_flag: Value<bool>,
}
@ -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<R: RngCore>(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);

View File

@ -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";

View File

@ -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<T: IssueAuth> {
/// 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<AssetId, Error> {
) -> Result<AssetBase, Error> {
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<SpendAuth>,
}
@ -129,6 +131,11 @@ impl Signed {
pub fn signature(&self) -> &redpallas::Signature<SpendAuth> {
&self.signature
}
/// Constructs an `Signed` from its constituent parts.
pub fn from_parts(signature: Signature<SpendAuth>) -> Self {
Signed { signature }
}
}
impl IssueAuth for Unauthorized {}
@ -163,11 +170,11 @@ impl<T: IssueAuth> IssueBundle<T> {
}
/// 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<T: IssueAuth> IssueBundle<T> {
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<IssueAction>,
authorization: T,
) -> Self {
IssueBundle {
ik,
actions,
authorization,
}
}
}
impl IssueBundle<Unauthorized> {
@ -202,12 +222,12 @@ impl IssueBundle<Unauthorized> {
value: NoteValue,
finalize: bool,
mut rng: impl RngCore,
) -> Result<AssetId, Error> {
) -> Result<AssetBase, Error> {
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<Signed> {
/// 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<Signed>,
sighash: [u8; 32],
finalized: &mut HashSet<AssetId>, // The finalization set.
finalized: &mut HashSet<AssetBase>, // The finalization set.
) -> Result<(), Error> {
if let Err(e) = bundle.ik.verify(&sighash, &bundle.authorization.signature) {
return Err(IssueBundleInvalidSignature(e));
};
let s = &mut HashSet::<AssetId>::new();
let s = &mut HashSet::<AssetBase>::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::*;

View File

@ -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(),
)

View File

@ -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<Self> {
@ -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<Nullifier>,
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(),

View File

@ -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<u8>) -> 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<u8>) -> 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<Self> {
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<H: Hasher>(&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);
}

View File

@ -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,

View File

@ -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(

View File

@ -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<AssetId> {
fn parse_version_and_asset_type(plaintext: &CompactNotePlaintextBytes) -> Option<AssetBase> {
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();

View File

@ -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);

View File

@ -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()

View File

@ -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(())

View File

@ -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<TestOutputInfo>,
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,