Skip to content

Commit 8a6f599

Browse files
authored
Added NoteType to Notes (#2)
* Added NoteType to Notes * Added NoteType to value commitment derivation
1 parent 769f2f7 commit 8a6f599

11 files changed

Lines changed: 182 additions & 38 deletions

File tree

.circleci/config.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
# See: https://circleci.com/docs/2.0/configuration-reference
33
version: 2.1
44

5+
orbs:
6+
slack: circleci/slack@4.1
7+
58
# Define a job to be invoked later in a workflow.
69
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
710
jobs:
@@ -17,13 +20,21 @@ jobs:
1720
- run:
1821
name: "cargo test"
1922
command: |
23+
sudo apt update && sudo apt-get install libfontconfig libfontconfig1-dev libfreetype6-dev;
2024
cargo version;
21-
cargo test;
25+
cargo test --all --all-features;
26+
- slack/notify:
27+
event: fail
28+
template: basic_fail_1
29+
- slack/notify:
30+
event: pass
31+
template: basic_success_1
2232

2333

2434
# Invoke jobs via workflows
2535
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
2636
workflows:
2737
build-and-test:
2838
jobs:
29-
- cargo-test
39+
- cargo-test:
40+
context: CI-Orchard-slack

src/action.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub(crate) mod testing {
126126

127127
use proptest::prelude::*;
128128

129+
use crate::note::NoteType;
129130
use crate::{
130131
note::{
131132
commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
@@ -150,7 +151,8 @@ pub(crate) mod testing {
150151
let cmx = ExtractedNoteCommitment::from(note.commitment());
151152
let cv_net = ValueCommitment::derive(
152153
spend_value - output_value,
153-
ValueCommitTrapdoor::zero()
154+
ValueCommitTrapdoor::zero(),
155+
NoteType::native()
154156
);
155157
// FIXME: make a real one from the note.
156158
let encrypted_note = TransmittedNoteCiphertext {
@@ -181,7 +183,8 @@ pub(crate) mod testing {
181183
let cmx = ExtractedNoteCommitment::from(note.commitment());
182184
let cv_net = ValueCommitment::derive(
183185
spend_value - output_value,
184-
ValueCommitTrapdoor::zero()
186+
ValueCommitTrapdoor::zero(),
187+
NoteType::native()
185188
);
186189

187190
// FIXME: make a real one from the note.

src/builder.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use nonempty::NonEmpty;
88
use pasta_curves::pallas;
99
use rand::{prelude::SliceRandom, CryptoRng, RngCore};
1010

11+
use crate::note::NoteType;
1112
use crate::{
1213
action::Action,
1314
address::Address,
@@ -140,7 +141,7 @@ impl ActionInfo {
140141
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
141142
fn build(self, mut rng: impl RngCore) -> (Action<SigningMetadata>, Circuit) {
142143
let v_net = self.value_sum();
143-
let cv_net = ValueCommitment::derive(v_net, self.rcv.clone());
144+
let cv_net = ValueCommitment::derive(v_net, self.rcv, NoteType::native());
144145

145146
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
146147
let sender_address = self.spend.note.recipient();
@@ -150,8 +151,15 @@ impl ActionInfo {
150151
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
151152
let alpha = pallas::Scalar::random(&mut rng);
152153
let rk = ak.randomize(&alpha);
154+
let note_type = self.spend.note.note_type();
153155

154-
let note = Note::new(self.output.recipient, self.output.value, nf_old, &mut rng);
156+
let note = Note::new(
157+
self.output.recipient,
158+
self.output.value,
159+
note_type,
160+
nf_old,
161+
&mut rng,
162+
);
155163
let cm_new = note.commitment();
156164
let cmx = cm_new.into();
157165

@@ -361,7 +369,11 @@ impl Builder {
361369

362370
// Verify that bsk and bvk are consistent.
363371
let bvk = (actions.iter().map(|a| a.cv_net()).sum::<ValueCommitment>()
364-
- ValueCommitment::derive(value_balance, ValueCommitTrapdoor::zero()))
372+
- ValueCommitment::derive(
373+
value_balance,
374+
ValueCommitTrapdoor::zero(),
375+
NoteType::native(),
376+
))
365377
.into_bvk();
366378
assert_eq!(redpallas::VerificationKey::from(&bsk), bvk);
367379

src/bundle.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use memuse::DynamicUsage;
99
use nonempty::NonEmpty;
1010
use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk};
1111

12+
use crate::note::NoteType;
1213
use crate::{
1314
action::Action,
1415
address::Address,
@@ -376,6 +377,7 @@ impl<T: Authorization, V: Copy + Into<i64>> Bundle<T, V> {
376377
- ValueCommitment::derive(
377378
ValueSum::from_raw(self.value_balance.into()),
378379
ValueCommitTrapdoor::zero(),
380+
NoteType::native(),
379381
))
380382
.into_bvk()
381383
}

src/circuit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,7 @@ mod tests {
882882
use rand::{rngs::OsRng, RngCore};
883883

884884
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
885+
use crate::note::NoteType;
885886
use crate::{
886887
keys::SpendValidatingKey,
887888
note::Note,
@@ -905,7 +906,7 @@ mod tests {
905906

906907
let value = spent_note.value() - output_note.value();
907908
let rcv = ValueCommitTrapdoor::random(&mut rng);
908-
let cv_net = ValueCommitment::derive(value, rcv.clone());
909+
let cv_net = ValueCommitment::derive(value, rcv, NoteType::native());
909910

910911
let path = MerklePath::dummy(&mut rng);
911912
let anchor = path.root(spent_note.commitment().into());

src/constants/fixed_bases.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ pub mod value_commit_v;
1919
pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard";
2020

2121
/// SWU hash-to-curve personalization for the value commitment generator
22+
/// TODO: should we change to "NOTE_TYPE_PERSONALIZATION"?
2223
pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv";
2324

25+
/// SWU hash-to-curve personalization for the note type generator
26+
// pub const NOTE_TYPE_PERSONALIZATION: &str = "z.cash:Orchard-NoteType";
27+
2428
/// SWU hash-to-curve value for the value commitment generator
2529
pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v";
2630

src/keys.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ mod tests {
10451045
testing::{arb_diversifier_index, arb_diversifier_key, arb_esk, arb_spending_key},
10461046
*,
10471047
};
1048+
use crate::note::NoteType;
10481049
use crate::{
10491050
note::{ExtractedNoteCommitment, Nullifier, RandomSeed},
10501051
value::NoteValue,
@@ -1136,6 +1137,7 @@ mod tests {
11361137
let note = Note::from_parts(
11371138
addr,
11381139
NoteValue::from_raw(tv.note_v),
1140+
NoteType::native(),
11391141
rho,
11401142
RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
11411143
);

src/note.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
1919
pub(crate) mod nullifier;
2020
pub use self::nullifier::Nullifier;
2121

22+
pub(crate) mod note_type;
23+
pub use self::note_type::NoteType;
24+
2225
/// The ZIP 212 seed randomness for a note.
2326
#[derive(Copy, Clone, Debug)]
2427
pub(crate) struct RandomSeed([u8; 32]);
@@ -86,6 +89,8 @@ pub struct Note {
8689
recipient: Address,
8790
/// The value of this note.
8891
value: NoteValue,
92+
/// The type of this note.
93+
note_type: NoteType,
8994
/// A unique creation ID for this note.
9095
///
9196
/// This is set to the nullifier of the note that was spent in the [`Action`] that
@@ -111,12 +116,14 @@ impl Note {
111116
pub(crate) fn from_parts(
112117
recipient: Address,
113118
value: NoteValue,
119+
note_type: NoteType,
114120
rho: Nullifier,
115121
rseed: RandomSeed,
116122
) -> Self {
117123
Note {
118124
recipient,
119125
value,
126+
note_type,
120127
rho,
121128
rseed,
122129
}
@@ -130,13 +137,15 @@ impl Note {
130137
pub(crate) fn new(
131138
recipient: Address,
132139
value: NoteValue,
140+
note_type: NoteType,
133141
rho: Nullifier,
134142
mut rng: impl RngCore,
135143
) -> Self {
136144
loop {
137145
let note = Note {
138146
recipient,
139147
value,
148+
note_type,
140149
rho,
141150
rseed: RandomSeed::random(&mut rng, &rho),
142151
};
@@ -162,6 +171,7 @@ impl Note {
162171
let note = Note::new(
163172
recipient,
164173
NoteValue::zero(),
174+
NoteType::native(),
165175
rho.unwrap_or_else(|| Nullifier::dummy(rng)),
166176
rng,
167177
);
@@ -179,6 +189,11 @@ impl Note {
179189
self.value
180190
}
181191

192+
/// Returns the note type
193+
pub fn note_type(&self) -> NoteType {
194+
self.note_type
195+
}
196+
182197
/// Returns the rseed value of this note.
183198
pub(crate) fn rseed(&self) -> &RandomSeed {
184199
&self.rseed
@@ -265,6 +280,7 @@ impl fmt::Debug for TransmittedNoteCiphertext {
265280
pub mod testing {
266281
use proptest::prelude::*;
267282

283+
use crate::note::note_type::testing::arb_note_type;
268284
use crate::{
269285
address::testing::arb_address, note::nullifier::testing::arb_nullifier, value::NoteValue,
270286
};
@@ -284,10 +300,12 @@ pub mod testing {
284300
recipient in arb_address(),
285301
rho in arb_nullifier(),
286302
rseed in arb_rseed(),
303+
note_type in arb_note_type(),
287304
) -> Note {
288305
Note {
289306
recipient,
290307
value,
308+
note_type,
291309
rho,
292310
rseed,
293311
}

src/note/note_type.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use group::GroupEncoding;
2+
use halo2_proofs::arithmetic::CurveExt;
3+
use pasta_curves::pallas;
4+
use subtle::CtOption;
5+
6+
use crate::constants::fixed_bases::{VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_V_BYTES};
7+
use crate::keys::IssuerValidatingKey;
8+
9+
/// Note type identifier.
10+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11+
pub struct NoteType(pub(crate) pallas::Point);
12+
13+
const MAX_ASSET_DESCRIPTION_SIZE: usize = 512;
14+
15+
// the hasher used to derive the assetID
16+
#[allow(non_snake_case)]
17+
fn assetID_hasher(msg: Vec<u8>) -> pallas::Point {
18+
// TODO(zsa) replace personalization, will require circuit change?
19+
pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION)(&msg)
20+
}
21+
22+
impl NoteType {
23+
/// Deserialize the note_type from a byte array.
24+
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
25+
pallas::Point::from_bytes(bytes).map(NoteType)
26+
}
27+
28+
/// Serialize the note_type to its canonical byte representation.
29+
pub fn to_bytes(self) -> [u8; 32] {
30+
self.0.to_bytes()
31+
}
32+
33+
/// $DeriveNoteType$.
34+
///
35+
/// Defined in [Zcash Protocol Spec § TBD: Note Types][notetypes].
36+
///
37+
/// [notetypes]: https://zips.z.cash/protocol/nu5.pdf#notetypes
38+
#[allow(non_snake_case)]
39+
pub fn derive(ik: &IssuerValidatingKey, assetDesc: Vec<u8>) -> Self {
40+
assert!(assetDesc.len() < MAX_ASSET_DESCRIPTION_SIZE);
41+
42+
let mut s = vec![];
43+
s.extend(ik.to_bytes());
44+
s.extend(assetDesc);
45+
46+
NoteType(assetID_hasher(s))
47+
}
48+
49+
/// Note type for the "native" currency (zec), maintains backward compatibility with Orchard untyped notes.
50+
pub fn native() -> Self {
51+
NoteType(assetID_hasher(VALUE_COMMITMENT_V_BYTES.to_vec()))
52+
}
53+
}
54+
55+
/// Generators for property testing.
56+
#[cfg(any(test, feature = "test-dependencies"))]
57+
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
58+
pub mod testing {
59+
use proptest::prelude::*;
60+
61+
use super::NoteType;
62+
63+
use crate::keys::{testing::arb_spending_key, IssuerAuthorizingKey, IssuerValidatingKey};
64+
65+
prop_compose! {
66+
/// Generate a uniformly distributed note type
67+
pub fn arb_note_type()(
68+
sk in arb_spending_key(),
69+
bytes32a in prop::array::uniform32(prop::num::u8::ANY),
70+
bytes32b in prop::array::uniform32(prop::num::u8::ANY),
71+
) -> NoteType {
72+
let bytes64 = [bytes32a, bytes32b].concat();
73+
let isk = IssuerAuthorizingKey::from(&sk);
74+
NoteType::derive(&IssuerValidatingKey::from(&isk), bytes64)
75+
}
76+
}
77+
}

src/note_encryption.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use zcash_note_encryption::{
1010
OUT_PLAINTEXT_SIZE,
1111
};
1212

13+
use crate::note::NoteType;
1314
use crate::{
1415
action::Action,
1516
keys::{
@@ -75,7 +76,8 @@ where
7576
let pk_d = get_validated_pk_d(&diversifier)?;
7677

7778
let recipient = Address::from_parts(diversifier, pk_d);
78-
let note = Note::from_parts(recipient, value, domain.rho, rseed);
79+
// TODO: add note_type
80+
let note = Note::from_parts(recipient, value, NoteType::native(), domain.rho, rseed);
7981
Some((note, recipient))
8082
}
8183

@@ -151,6 +153,7 @@ impl Domain for OrchardDomain {
151153
np[0] = 0x02;
152154
np[1..12].copy_from_slice(note.recipient().diversifier().as_array());
153155
np[12..20].copy_from_slice(&note.value().to_bytes());
156+
// todo: add note_type
154157
np[20..52].copy_from_slice(note.rseed().as_bytes());
155158
np[52..].copy_from_slice(memo);
156159
NotePlaintextBytes(np)
@@ -316,6 +319,7 @@ mod tests {
316319
};
317320

318321
use super::{prf_ock_orchard, CompactAction, OrchardDomain, OrchardNoteEncryption};
322+
use crate::note::NoteType;
319323
use crate::{
320324
action::Action,
321325
keys::{
@@ -369,7 +373,7 @@ mod tests {
369373
assert_eq!(ock.as_ref(), tv.ock);
370374

371375
let recipient = Address::from_parts(d, pk_d);
372-
let note = Note::from_parts(recipient, value, rho, rseed);
376+
let note = Note::from_parts(recipient, value, NoteType::native(), rho, rseed);
373377
assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx);
374378

375379
let action = Action::from_parts(

0 commit comments

Comments
 (0)