Skip to content

Commit b10dd2b

Browse files
PR471: Make rho optional in Note (#226)
Address the following review comments: r2543612212 r2543614980 When creating an issuance note, the rho value is now initialized to None (instead of being set to 0 previously). The rho value is later deterministically derived and assigned when calling the `update_rho` method on the `IssueBundle`. In addition, this PR fixes an issue where issuance notes could be invalid. Previously, rseed was sampled before the final rho value was known, which could result in an invalid note. With this change, rseed is updated after rho is set, ensuring that the resulting note is always valid.
1 parent 7aafdc0 commit b10dd2b

File tree

5 files changed

+132
-83
lines changed

5 files changed

+132
-83
lines changed

src/bundle/commitments/issuance.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ mod tests {
148148
.unwrap();
149149
assert_ne!(asset, third_asset);
150150

151-
(bundle.update_rho(&first_nullifier), isk)
151+
(bundle.update_rho(&first_nullifier, rng), isk)
152152
}
153153

154154
/// Verify that the `issuance_digest` of an IssueBundle matches a fixed reference value
@@ -159,7 +159,7 @@ mod tests {
159159
let issuance_digest = hash_issue_bundle_txid_data(&bundle);
160160
assert_eq!(
161161
issuance_digest.to_hex().as_str(),
162-
"d99afbab7c0e8bd5d250e2df7d6df39d06891b264fff34090b55c5fac2d65ce5"
162+
"ee70e3b61674fd0428ac0020cc4fc5819386e39c4eb3c63357c84c998195bcdb"
163163
);
164164
}
165165

@@ -178,7 +178,7 @@ mod tests {
178178
hash_issue_bundle_auth_data(&signed_bundle, &sighash_version_map);
179179
assert_eq!(
180180
issuance_auth_digest.to_hex().as_str(),
181-
"1e737ae27e378d3cd90c93efb7a5f8ef5a7b4db7aa9848ed7b57a7795253af86"
181+
"6df77af7b5323d99376336b770e4c5b06ffc195de81ac7692d1b08b6eb19534d"
182182
);
183183
}
184184
}

src/issuance.rs

Lines changed: 49 additions & 53 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, AssetId, Nullifier, Rho},
26+
note::{rho_for_issuance_note, AssetBase, AssetId, Nullifier},
2727
value::NoteValue,
2828
Address, Note,
2929
};
@@ -441,13 +441,8 @@ impl IssueBundle<AwaitingNullifier> {
441441
flags: IssuanceFlags::from_parts(true),
442442
},
443443
Some(issue_info) => {
444-
let note = Note::new(
445-
issue_info.recipient,
446-
issue_info.value,
447-
asset,
448-
Rho::zero(),
449-
&mut rng,
450-
);
444+
let note =
445+
Note::new_issue_note(issue_info.recipient, issue_info.value, asset, &mut rng);
451446

452447
notes.push(note);
453448

@@ -484,7 +479,7 @@ impl IssueBundle<AwaitingNullifier> {
484479
) -> Result<AssetBase, Error> {
485480
let asset = AssetBase::custom(&AssetId::new_v0(&self.ik, &asset_desc_hash));
486481

487-
let note = Note::new(recipient, value, asset, Rho::zero(), &mut rng);
482+
let note = Note::new_issue_note(recipient, value, asset, &mut rng);
488483

489484
let notes = if first_issuance {
490485
vec![create_reference_note(asset, &mut rng), note]
@@ -541,7 +536,11 @@ impl IssueBundle<AwaitingNullifier> {
541536
/// [ZIP-227: Issuance of Zcash Shielded Assets][zip227].
542537
///
543538
/// [zip227]: https://zips.z.cash/zip-0227
544-
pub fn update_rho(self, first_nullifier: &Nullifier) -> IssueBundle<AwaitingSighash> {
539+
pub fn update_rho(
540+
self,
541+
first_nullifier: &Nullifier,
542+
mut rng: impl RngCore,
543+
) -> IssueBundle<AwaitingSighash> {
545544
let mut bundle = self;
546545
bundle
547546
.actions
@@ -557,6 +556,7 @@ impl IssueBundle<AwaitingNullifier> {
557556
first_nullifier,
558557
index_action.try_into().unwrap(),
559558
index_note.try_into().unwrap(),
559+
&mut rng,
560560
);
561561
});
562562
});
@@ -576,11 +576,10 @@ impl IssueBundle<AwaitingSighash> {
576576
}
577577

578578
fn create_reference_note(asset: AssetBase, mut rng: impl RngCore) -> Note {
579-
Note::new(
579+
Note::new_issue_note(
580580
ReferenceKeys::recipient(),
581581
NoteValue::zero(),
582582
asset,
583-
Rho::zero(),
584583
&mut rng,
585584
)
586585
}
@@ -999,19 +998,13 @@ mod tests {
999998
))
1000999
});
10011000

1002-
let note1 = Note::new(
1003-
recipient,
1004-
NoteValue::from_raw(note1_value),
1005-
asset,
1006-
Rho::zero(),
1007-
&mut rng,
1008-
);
1001+
let note1 =
1002+
Note::new_issue_note(recipient, NoteValue::from_raw(note1_value), asset, &mut rng);
10091003

1010-
let note2 = Note::new(
1004+
let note2 = Note::new_issue_note(
10111005
recipient,
10121006
NoteValue::from_raw(note2_value),
10131007
note2_asset,
1014-
Rho::zero(),
10151008
&mut rng,
10161009
);
10171010

@@ -1124,14 +1117,14 @@ mod tests {
11241117
action
11251118
.notes()
11261119
.iter()
1127-
.for_each(|note| assert_eq!(note.rho(), Rho::zero()))
1120+
.for_each(|note| assert!(!note.has_rho()))
11281121
});
1129-
let awaiting_sighash_bundle = bundle.update_rho(&first_nullifier);
1122+
let awaiting_sighash_bundle = bundle.update_rho(&first_nullifier, rng);
11301123
awaiting_sighash_bundle.actions().iter().for_each(|action| {
11311124
action
11321125
.notes()
11331126
.iter()
1134-
.for_each(|note| assert_ne!(note.rho(), Rho::zero()))
1127+
.for_each(|note| assert!(note.has_rho()))
11351128
});
11361129

11371130
let actions = awaiting_sighash_bundle.actions();
@@ -1225,7 +1218,7 @@ mod tests {
12251218
rng,
12261219
);
12271220

1228-
let prepared = bundle.update_rho(&first_nullifier).prepare(sighash);
1221+
let prepared = bundle.update_rho(&first_nullifier, rng).prepare(sighash);
12291222
assert_eq!(prepared.authorization().sighash, sighash);
12301223
}
12311224

@@ -1254,7 +1247,7 @@ mod tests {
12541247
);
12551248

12561249
let signed = bundle
1257-
.update_rho(&first_nullifier)
1250+
.update_rho(&first_nullifier, rng)
12581251
.prepare(sighash)
12591252
.sign(&isk)
12601253
.unwrap();
@@ -1290,7 +1283,7 @@ mod tests {
12901283
let wrong_isk = IssueAuthKey::<ZSASchnorr>::random(&mut rng);
12911284

12921285
let err = bundle
1293-
.update_rho(&first_nullifier)
1286+
.update_rho(&first_nullifier, rng)
12941287
.prepare([0; 32])
12951288
.sign(&wrong_isk)
12961289
.expect_err("should not be able to sign");
@@ -1322,20 +1315,19 @@ mod tests {
13221315
);
13231316

13241317
// Add "bad" note
1325-
let note = Note::new(
1318+
let note = Note::new_issue_note(
13261319
recipient,
13271320
NoteValue::from_raw(5),
13281321
AssetBase::custom(&AssetId::new_v0(
13291322
bundle.ik(),
13301323
&compute_asset_desc_hash(&NonEmpty::from_slice(b"zsa_asset").unwrap()),
13311324
)),
1332-
Rho::zero(),
13331325
&mut rng,
13341326
);
13351327
bundle.actions.first_mut().notes.push(note);
13361328

13371329
let err = bundle
1338-
.update_rho(&first_nullifier)
1330+
.update_rho(&first_nullifier, rng)
13391331
.prepare([0; 32])
13401332
.sign(&isk)
13411333
.expect_err("should not be able to sign");
@@ -1368,7 +1360,7 @@ mod tests {
13681360
);
13691361

13701362
let signed = bundle
1371-
.update_rho(&first_nullifier)
1363+
.update_rho(&first_nullifier, rng)
13721364
.prepare(sighash)
13731365
.sign(&isk)
13741366
.unwrap();
@@ -1414,7 +1406,7 @@ mod tests {
14141406
bundle.finalize_action(&asset_desc_hash).unwrap();
14151407

14161408
let signed = bundle
1417-
.update_rho(&first_nullifier)
1409+
.update_rho(&first_nullifier, rng)
14181410
.prepare(sighash)
14191411
.sign(&isk)
14201412
.unwrap();
@@ -1500,7 +1492,7 @@ mod tests {
15001492
.unwrap();
15011493

15021494
let signed = bundle
1503-
.update_rho(&first_nullifier)
1495+
.update_rho(&first_nullifier, rng)
15041496
.prepare(sighash)
15051497
.sign(&isk)
15061498
.unwrap();
@@ -1566,7 +1558,7 @@ mod tests {
15661558
);
15671559

15681560
let signed = bundle
1569-
.update_rho(&first_nullifier)
1561+
.update_rho(&first_nullifier, rng)
15701562
.prepare(sighash)
15711563
.sign(&isk)
15721564
.unwrap();
@@ -1604,7 +1596,7 @@ mod tests {
16041596
);
16051597

16061598
let signed = bundle
1607-
.update_rho(&first_nullifier)
1599+
.update_rho(&first_nullifier, rng)
16081600
.prepare(sighash)
16091601
.sign(&isk)
16101602
.unwrap();
@@ -1616,13 +1608,7 @@ mod tests {
16161608
AssetRecord::new(
16171609
NoteValue::from_raw(20),
16181610
true,
1619-
Note::new(
1620-
recipient,
1621-
NoteValue::from_raw(10),
1622-
final_type,
1623-
Rho::zero(),
1624-
&mut rng,
1625-
),
1611+
Note::new_issue_note(recipient, NoteValue::from_raw(10), final_type, &mut rng),
16261612
),
16271613
)]
16281614
.into_iter()
@@ -1672,7 +1658,7 @@ mod tests {
16721658
let wrong_isk = IssueAuthKey::<ZSASchnorr>::random(&mut rng);
16731659

16741660
let mut signed = bundle
1675-
.update_rho(&first_nullifier)
1661+
.update_rho(&first_nullifier, rng)
16761662
.prepare(sighash)
16771663
.sign(&isk)
16781664
.unwrap();
@@ -1712,10 +1698,9 @@ mod tests {
17121698
rng,
17131699
);
17141700

1715-
let sighash: [u8; 32] = bundle.commitment().into();
17161701
let signed = bundle
1717-
.update_rho(&first_nullifier)
1718-
.prepare(sighash)
1702+
.update_rho(&first_nullifier, rng)
1703+
.prepare([0_u8; 32])
17191704
.sign(&isk)
17201705
.unwrap();
17211706

@@ -1748,7 +1733,7 @@ mod tests {
17481733
);
17491734

17501735
let mut signed = bundle
1751-
.update_rho(&first_nullifier)
1736+
.update_rho(&first_nullifier, rng)
17521737
.prepare(sighash)
17531738
.sign(&isk)
17541739
.unwrap();
@@ -1798,7 +1783,7 @@ mod tests {
17981783
);
17991784

18001785
let mut signed = bundle
1801-
.update_rho(&first_nullifier)
1786+
.update_rho(&first_nullifier, rng)
18021787
.prepare(sighash)
18031788
.sign(&isk)
18041789
.unwrap();
@@ -1844,7 +1829,11 @@ mod tests {
18441829
#[test]
18451830
fn test_get_action_by_desc_hash() {
18461831
let TestParams {
1847-
rng, ik, recipient, ..
1832+
rng,
1833+
ik,
1834+
recipient,
1835+
first_nullifier,
1836+
..
18481837
} = setup_params();
18491838

18501839
// UTF heavy test string
@@ -1864,14 +1853,20 @@ mod tests {
18641853
rng,
18651854
);
18661855

1856+
// NOTE: Equality between two IssueActions can only be tested once `rho` is initialized.
1857+
// This call is required for the final `assert_eq!`.
1858+
let bundle_with_rho = bundle.update_rho(&first_nullifier, rng);
1859+
18671860
// Checks for the case of UTF-8 encoded asset description.
1868-
let action = bundle.get_action_by_asset(&asset_base_1).unwrap();
1861+
let action = bundle_with_rho.get_action_by_asset(&asset_base_1).unwrap();
18691862
assert_eq!(action.asset_desc_hash(), &asset_desc_hash_1);
18701863
let reference_note = action.notes.first().unwrap();
18711864
verify_reference_note(reference_note, asset_base_1);
18721865
assert_eq!(action.notes.get(1).unwrap().value().inner(), 5);
18731866
assert_eq!(
1874-
bundle.get_action_by_desc_hash(&asset_desc_hash_1).unwrap(),
1867+
bundle_with_rho
1868+
.get_action_by_desc_hash(&asset_desc_hash_1)
1869+
.unwrap(),
18751870
action
18761871
);
18771872
}
@@ -2009,10 +2004,11 @@ mod tests {
20092004
action
20102005
.notes()
20112006
.iter()
2012-
.for_each(|note| assert_eq!(note.rho(), Rho::zero()))
2007+
.for_each(|note| assert!(!note.has_rho()))
20132008
});
20142009

2015-
let awaiting_sighash_bundle = bundle.update_rho(authorized.actions().first().nullifier());
2010+
let awaiting_sighash_bundle =
2011+
bundle.update_rho(authorized.actions().first().nullifier(), rng);
20162012

20172013
assert_eq!(awaiting_sighash_bundle.actions().len(), 2);
20182014
assert_eq!(

0 commit comments

Comments
 (0)