Skip to content

Commit 382f319

Browse files
PR471 review: Add AssetId enum (#223)
Address the following review comments: r2550373804 r2550386724 r2550403709 Introduce the AssetId enum holding an ik and an asset_desc_hash, and use it as input for encode_asset_id, asset_digest, and AssetBase::custom (renamed from AssetBase::derive). AssetId uses lifetimes to borrow ik and avoid cloning it.
1 parent 00e76f9 commit 382f319

File tree

5 files changed

+103
-76
lines changed

5 files changed

+103
-76
lines changed

src/issuance.rs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rand::RngCore;
2323
use crate::{
2424
bundle::commitments::{hash_issue_bundle_auth_data, hash_issue_bundle_txid_data},
2525
constants::reference_keys::ReferenceKeys,
26-
note::{rho_for_issuance_note, AssetBase, Nullifier, Rho},
26+
note::{rho_for_issuance_note, AssetBase, AssetId, Nullifier, Rho},
2727
value::NoteValue,
2828
Address, Note,
2929
};
@@ -226,7 +226,7 @@ impl IssueAction {
226226
return Err(IssueActionWithoutNoteNotFinalized);
227227
}
228228

229-
let issue_asset = AssetBase::derive(ik, &self.asset_desc_hash);
229+
let issue_asset = AssetBase::custom(&AssetId::new_v0(ik, &self.asset_desc_hash));
230230

231231
// The new asset should not be the identity point of the Pallas curve.
232232
if bool::from(issue_asset.cv_base().is_identity()) {
@@ -367,7 +367,7 @@ impl<T: IssueAuth> IssueBundle<T> {
367367
let issue_actions: Vec<&IssueAction> = self
368368
.actions
369369
.iter()
370-
.filter(|a| AssetBase::derive(&self.ik, &a.asset_desc_hash).eq(asset))
370+
.filter(|a| AssetBase::custom(&AssetId::new_v0(&self.ik, &a.asset_desc_hash)).eq(asset))
371371
.collect();
372372
match issue_actions.len() {
373373
0 => None,
@@ -427,7 +427,7 @@ impl IssueBundle<AwaitingNullifier> {
427427
first_issuance: bool,
428428
mut rng: impl RngCore,
429429
) -> (IssueBundle<AwaitingNullifier>, AssetBase) {
430-
let asset = AssetBase::derive(&ik, &asset_desc_hash);
430+
let asset = AssetBase::custom(&AssetId::new_v0(&ik, &asset_desc_hash));
431431

432432
let mut notes = vec![];
433433
if first_issuance {
@@ -482,7 +482,7 @@ impl IssueBundle<AwaitingNullifier> {
482482
first_issuance: bool,
483483
mut rng: impl RngCore,
484484
) -> Result<AssetBase, Error> {
485-
let asset = AssetBase::derive(&self.ik, &asset_desc_hash);
485+
let asset = AssetBase::custom(&AssetId::new_v0(&self.ik, &asset_desc_hash));
486486

487487
let note = Note::new(recipient, value, asset, Rho::zero(), &mut rng);
488488

@@ -898,7 +898,7 @@ mod tests {
898898
Signed,
899899
},
900900
keys::{FullViewingKey, Scope, SpendingKey},
901-
note::{rho_for_issuance_note, AssetBase, Nullifier, Rho},
901+
note::{rho_for_issuance_note, AssetBase, AssetId, Nullifier, Rho},
902902
value::NoteValue,
903903
Address, Note,
904904
};
@@ -991,12 +991,12 @@ mod tests {
991991

992992
let note1_asset_desc_hash =
993993
compute_asset_desc_hash(&NonEmpty::from_slice(note1_asset_desc).unwrap());
994-
let asset = AssetBase::derive(&ik, &note1_asset_desc_hash);
994+
let asset = AssetBase::custom(&AssetId::new_v0(&ik, &note1_asset_desc_hash));
995995
let note2_asset = note2_asset_desc.map_or(asset, |desc| {
996-
AssetBase::derive(
996+
AssetBase::custom(&AssetId::new_v0(
997997
&ik,
998998
&compute_asset_desc_hash(&NonEmpty::from_slice(desc).unwrap()),
999-
)
999+
))
10001000
});
10011001

10021002
let note1 = Note::new(
@@ -1156,7 +1156,10 @@ mod tests {
11561156
.unwrap();
11571157
assert_eq!(action2.notes.len(), 2);
11581158
let reference_note = action2.notes.first().unwrap();
1159-
verify_reference_note(reference_note, AssetBase::derive(&ik, &asset_desc_hash_2));
1159+
verify_reference_note(
1160+
reference_note,
1161+
AssetBase::custom(&AssetId::new_v0(&ik, &asset_desc_hash_2)),
1162+
);
11601163
let first_note = action2.notes().get(1).unwrap();
11611164
assert_eq!(first_note.value().inner(), 15);
11621165
assert_eq!(first_note.asset(), third_asset);
@@ -1322,10 +1325,10 @@ mod tests {
13221325
let note = Note::new(
13231326
recipient,
13241327
NoteValue::from_raw(5),
1325-
AssetBase::derive(
1328+
AssetBase::custom(&AssetId::new_v0(
13261329
bundle.ik(),
13271330
&compute_asset_desc_hash(&NonEmpty::from_slice(b"zsa_asset").unwrap()),
1328-
),
1331+
)),
13291332
Rho::zero(),
13301333
&mut rng,
13311334
);
@@ -1377,7 +1380,7 @@ mod tests {
13771380
assert_eq!(
13781381
issued_assets,
13791382
BTreeMap::from([(
1380-
AssetBase::derive(&ik, &asset_desc_hash),
1383+
AssetBase::custom(&AssetId::new_v0(&ik, &asset_desc_hash)),
13811384
AssetRecord::new(NoteValue::from_raw(5), false, first_note)
13821385
)])
13831386
);
@@ -1423,7 +1426,7 @@ mod tests {
14231426
assert_eq!(
14241427
issued_assets,
14251428
BTreeMap::from([(
1426-
AssetBase::derive(&ik, &asset_desc_hash),
1429+
AssetBase::custom(&AssetId::new_v0(&ik, &asset_desc_hash)),
14271430
AssetRecord::new(NoteValue::from_raw(7), true, first_note)
14281431
)])
14291432
);
@@ -1447,9 +1450,9 @@ mod tests {
14471450
let asset3_desc_hash =
14481451
compute_asset_desc_hash(&NonEmpty::from_slice(b"Verify with issued assets 3").unwrap());
14491452

1450-
let asset1_base = AssetBase::derive(&ik, &asset1_desc_hash);
1451-
let asset2_base = AssetBase::derive(&ik, &asset2_desc_hash);
1452-
let asset3_base = AssetBase::derive(&ik, &asset3_desc_hash);
1453+
let asset1_base = AssetBase::custom(&AssetId::new_v0(&ik, &asset1_desc_hash));
1454+
let asset2_base = AssetBase::custom(&AssetId::new_v0(&ik, &asset2_desc_hash));
1455+
let asset3_base = AssetBase::custom(&AssetId::new_v0(&ik, &asset3_desc_hash));
14531456

14541457
let (mut bundle, _) = IssueBundle::new(
14551458
ik,
@@ -1606,7 +1609,7 @@ mod tests {
16061609
.sign(&isk)
16071610
.unwrap();
16081611

1609-
let final_type = AssetBase::derive(&ik, &asset_desc_hash);
1612+
let final_type = AssetBase::custom(&AssetId::new_v0(&ik, &asset_desc_hash));
16101613

16111614
let issued_assets = [(
16121615
final_type,
@@ -1754,10 +1757,10 @@ mod tests {
17541757
let note = Note::new(
17551758
recipient,
17561759
NoteValue::from_raw(5),
1757-
AssetBase::derive(
1760+
AssetBase::custom(&AssetId::new_v0(
17581761
signed.ik(),
17591762
&compute_asset_desc_hash(&NonEmpty::from_slice(b"zsa_asset").unwrap()),
1760-
),
1763+
)),
17611764
rho_for_issuance_note(&first_nullifier, 0, 2),
17621765
&mut rng,
17631766
);
@@ -1807,7 +1810,7 @@ mod tests {
18071810
let note = Note::new(
18081811
recipient,
18091812
NoteValue::from_raw(55),
1810-
AssetBase::derive(&incorrect_ik, &asset_desc_hash),
1813+
AssetBase::custom(&AssetId::new_v0(&incorrect_ik, &asset_desc_hash)),
18111814
rho_for_issuance_note(&first_nullifier, 0, 0),
18121815
&mut rng,
18131816
);
@@ -1913,10 +1916,10 @@ mod tests {
19131916

19141917
// Setup note and merkle tree
19151918
let mut rng = OsRng;
1916-
let asset1 = AssetBase::derive(
1919+
let asset1 = AssetBase::custom(&AssetId::new_v0(
19171920
&ik,
19181921
&compute_asset_desc_hash(&NonEmpty::from_slice(b"zsa_asset1").unwrap()),
1919-
);
1922+
));
19201923
let note1 = Note::new(
19211924
recipient,
19221925
NoteValue::from_raw(10),

src/note.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use crate::{
2020

2121
pub(crate) mod asset_base;
2222
pub use self::asset_base::AssetBase;
23+
#[cfg(feature = "zsa-issuance")]
24+
pub use self::asset_base::AssetId;
2325

2426
pub(crate) mod commitment;
2527
pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
@@ -528,10 +530,11 @@ pub mod testing {
528530
rho in arb_nullifier().prop_map(Rho::from_nf_old),
529531
rseed in arb_rseed(),
530532
) -> Note {
533+
use crate::note::AssetId;
531534
Note {
532535
recipient,
533536
value,
534-
asset: AssetBase::derive(&ik, &asset_desc_hash),
537+
asset: AssetBase::custom(&AssetId::new_v0(&ik, &asset_desc_hash)),
535538
rho,
536539
rseed,
537540
rseed_split_note: CtOption::new(rseed, 0u8.into()),

src/note/asset_base.rs

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,63 @@ use {
2020
blake2b_simd::{Hash as Blake2bHash, Params},
2121
};
2222

23+
/// Asset Identifier
24+
#[cfg(feature = "zsa-issuance")]
25+
#[derive(Debug)]
26+
pub enum AssetId<'a> {
27+
/// Version V0 of AssetId
28+
V0 {
29+
/// Issue validating Key
30+
ik: &'a IssueValidatingKey<ZSASchnorr>,
31+
/// Asset description hash
32+
asset_desc_hash: &'a [u8; 32],
33+
},
34+
}
35+
36+
#[cfg(feature = "zsa-issuance")]
37+
impl<'a> AssetId<'a> {
38+
/// Generates a new V0 AssetId.
39+
pub fn new_v0(ik: &'a IssueValidatingKey<ZSASchnorr>, asset_desc_hash: &'a [u8; 32]) -> Self {
40+
AssetId::V0 {
41+
ik,
42+
asset_desc_hash,
43+
}
44+
}
45+
46+
/// Encoding the Asset Identifier, as defined in [ZIP 227][assetidentifier].
47+
///
48+
/// [assetidentifier]: https://zips.z.cash/zip-0227.html#specification-asset-identifier-asset-digest-and-asset-base
49+
fn encode_asset_id(&self) -> Vec<u8> {
50+
match self {
51+
AssetId::V0 {
52+
ik,
53+
asset_desc_hash,
54+
} => {
55+
let issuer = ik.encode();
56+
let mut asset_id = Vec::with_capacity(1 + issuer.len() + asset_desc_hash.len());
57+
asset_id.push(0u8); // version
58+
asset_id.extend(issuer);
59+
asset_id.extend_from_slice(&asset_desc_hash[..]);
60+
asset_id
61+
}
62+
}
63+
}
64+
65+
/// Derives the Asset Digest for the given ZSA asset.
66+
///
67+
/// Defined in [ZIP-227: Issuance of Zcash Shielded Assets][assetdigest].
68+
///
69+
/// [assetdigest]: https://zips.z.cash/zip-0227#asset-digests
70+
fn asset_digest(&self) -> Blake2bHash {
71+
Params::new()
72+
.hash_length(64)
73+
.personal(ZSA_ASSET_DIGEST_PERSONALIZATION)
74+
.to_state()
75+
.update(&self.encode_asset_id())
76+
.finalize()
77+
}
78+
}
79+
2380
/// Note type identifier.
2481
#[derive(Clone, Copy, Debug, Eq)]
2582
pub struct AssetBase(pallas::Point);
@@ -41,38 +98,6 @@ impl Ord for AssetBase {
4198
#[cfg(feature = "zsa-issuance")]
4299
pub const ZSA_ASSET_DIGEST_PERSONALIZATION: &[u8; 16] = b"ZSA-Asset-Digest";
43100

44-
/// Derives the Asset Digest for the given ZSA asset.
45-
///
46-
/// Defined in [ZIP-227: Issuance of Zcash Shielded Assets][assetdigest].
47-
///
48-
/// [assetdigest]: https://zips.z.cash/zip-0227#asset-digests
49-
#[cfg(feature = "zsa-issuance")]
50-
pub fn asset_digest(encode_asset_id: &[u8]) -> Blake2bHash {
51-
Params::new()
52-
.hash_length(64)
53-
.personal(ZSA_ASSET_DIGEST_PERSONALIZATION)
54-
.to_state()
55-
.update(encode_asset_id)
56-
.finalize()
57-
}
58-
59-
/// Encoding the Asset Identifier, as defined in [ZIP 227][assetidentifier].
60-
///
61-
/// [assetidentifier]: https://zips.z.cash/zip-0227.html#specification-asset-identifier-asset-digest-and-asset-base
62-
#[cfg(feature = "zsa-issuance")]
63-
pub fn encode_asset_id(
64-
version: u8,
65-
ik: &IssueValidatingKey<ZSASchnorr>,
66-
asset_desc_hash: &[u8; 32],
67-
) -> Vec<u8> {
68-
let issuer = ik.encode();
69-
let mut asset_id = Vec::with_capacity(1 + issuer.len() + asset_desc_hash.len());
70-
asset_id.push(version);
71-
asset_id.extend(issuer);
72-
asset_id.extend_from_slice(&asset_desc_hash[..]);
73-
asset_id
74-
}
75-
76101
impl AssetBase {
77102
/// Deserialize the AssetBase from a byte array.
78103
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
@@ -95,12 +120,8 @@ impl AssetBase {
95120
/// Panics if the derived AssetBase is the identity point.
96121
#[cfg(feature = "zsa-issuance")]
97122
#[allow(non_snake_case)]
98-
pub fn derive(ik: &IssueValidatingKey<ZSASchnorr>, asset_desc_hash: &[u8; 32]) -> Self {
99-
let version_byte: u8 = 0x00;
100-
101-
// EncodeAssetId(ik, asset_desc_hash) = version_byte || ik || asset_desc_hash
102-
let asset_id = encode_asset_id(version_byte, ik, asset_desc_hash);
103-
let asset_digest = asset_digest(&asset_id);
123+
pub fn custom(asset_id: &AssetId<'_>) -> Self {
124+
let asset_digest = asset_id.asset_digest();
104125

105126
let asset_base =
106127
pallas::Point::hash_to_curve(ZSA_ASSET_BASE_PERSONALIZATION)(asset_digest.as_bytes());
@@ -219,7 +240,7 @@ pub mod testing {
219240
mod tests {
220241
use crate::{
221242
issuance::auth::{IssueValidatingKey, ZSASchnorr},
222-
note::AssetBase,
243+
note::{AssetBase, AssetId},
223244
};
224245

225246
#[test]
@@ -230,10 +251,10 @@ mod tests {
230251
let asset_desc_hash = crate::issuance::compute_asset_desc_hash(
231252
&nonempty::NonEmpty::from_slice(&tv.description).unwrap(),
232253
);
233-
let calculated_asset_base = AssetBase::derive(
254+
let calculated_asset_base = AssetBase::custom(&AssetId::new_v0(
234255
&IssueValidatingKey::<ZSASchnorr>::decode(&tv.key).unwrap(),
235256
&asset_desc_hash,
236-
);
257+
));
237258
let test_vector_asset_base = AssetBase::from_bytes(&tv.asset_base).unwrap();
238259

239260
assert_eq!(calculated_asset_base, test_vector_asset_base);

tests/issuance_global_state.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use orchard::{
1717
IssueBundle, IssueInfo, Signed,
1818
},
1919
keys::{FullViewingKey, Scope, SpendingKey},
20-
note::{AssetBase, Nullifier},
20+
note::{AssetBase, AssetId, Nullifier},
2121
value::NoteValue,
2222
Address, Note,
2323
};
@@ -190,22 +190,22 @@ fn issue_bundle_verify_with_global_state() {
190190
let asset3_desc = b"Verify with issued assets 3".to_vec();
191191
let asset4_desc = b"Verify with issued assets 4".to_vec();
192192

193-
let asset1_base = AssetBase::derive(
193+
let asset1_base = AssetBase::custom(&AssetId::new_v0(
194194
&ik,
195195
&compute_asset_desc_hash(&NonEmpty::from_slice(&asset1_desc).unwrap()),
196-
);
197-
let asset2_base = AssetBase::derive(
196+
));
197+
let asset2_base = AssetBase::custom(&AssetId::new_v0(
198198
&ik,
199199
&compute_asset_desc_hash(&NonEmpty::from_slice(&asset2_desc).unwrap()),
200-
);
201-
let asset3_base = AssetBase::derive(
200+
));
201+
let asset3_base = AssetBase::custom(&AssetId::new_v0(
202202
&ik,
203203
&compute_asset_desc_hash(&NonEmpty::from_slice(&asset3_desc).unwrap()),
204-
);
205-
let asset4_base = AssetBase::derive(
204+
));
205+
let asset4_base = AssetBase::custom(&AssetId::new_v0(
206206
&ik,
207207
&compute_asset_desc_hash(&NonEmpty::from_slice(&asset4_desc).unwrap()),
208-
);
208+
));
209209

210210
let mut global_state = BTreeMap::new();
211211

tests/zsa.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use orchard::{
1616
Signed,
1717
},
1818
keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey},
19-
note::{AssetBase, ExtractedNoteCommitment, Nullifier},
19+
note::{AssetBase, AssetId, ExtractedNoteCommitment, Nullifier},
2020
primitives::OrchardDomain,
2121
tree::{MerkleHashOrchard, MerklePath},
2222
value::NoteValue,
@@ -184,7 +184,7 @@ fn issue_zsa_notes(
184184

185185
verify_reference_note(
186186
reference_note,
187-
AssetBase::derive(&keys.ik().clone(), &asset_desc_hash),
187+
AssetBase::custom(&AssetId::new_v0(keys.ik(), &asset_desc_hash)),
188188
);
189189

190190
assert!(verify_issue_bundle(

0 commit comments

Comments
 (0)