mirror of https://github.com/zcash/orchard.git
added note_type to builder
This commit is contained in:
parent
3a6ed1ed30
commit
48d65ee706
|
@ -2,3 +2,6 @@
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
|
action-circuit-layout.png
|
||||||
|
proptest-regressions/*.txt
|
||||||
|
|
|
@ -6,6 +6,7 @@ use criterion::{BenchmarkId, Criterion};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use pprof::criterion::{Output, PProfProfiler};
|
use pprof::criterion::{Output, PProfProfiler};
|
||||||
|
|
||||||
|
use orchard::note::NoteType;
|
||||||
use orchard::{
|
use orchard::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
bundle::Flags,
|
bundle::Flags,
|
||||||
|
@ -32,7 +33,13 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||||
);
|
);
|
||||||
for _ in 0..num_recipients {
|
for _ in 0..num_recipients {
|
||||||
builder
|
builder
|
||||||
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
|
.add_recipient(
|
||||||
|
None,
|
||||||
|
recipient,
|
||||||
|
NoteValue::from_raw(10),
|
||||||
|
NoteType::native(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
|
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
|
||||||
|
|
|
@ -4,6 +4,7 @@ use orchard::{
|
||||||
bundle::Flags,
|
bundle::Flags,
|
||||||
circuit::ProvingKey,
|
circuit::ProvingKey,
|
||||||
keys::{FullViewingKey, Scope, SpendingKey},
|
keys::{FullViewingKey, Scope, SpendingKey},
|
||||||
|
note::NoteType,
|
||||||
note_encryption::{CompactAction, OrchardDomain},
|
note_encryption::{CompactAction, OrchardDomain},
|
||||||
value::NoteValue,
|
value::NoteValue,
|
||||||
Anchor, Bundle,
|
Anchor, Bundle,
|
||||||
|
@ -51,10 +52,22 @@ fn bench_note_decryption(c: &mut Criterion) {
|
||||||
// The builder pads to two actions, and shuffles their order. Add two recipients
|
// The builder pads to two actions, and shuffles their order. Add two recipients
|
||||||
// so the first action is always decryptable.
|
// so the first action is always decryptable.
|
||||||
builder
|
builder
|
||||||
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
|
.add_recipient(
|
||||||
|
None,
|
||||||
|
recipient,
|
||||||
|
NoteValue::from_raw(10),
|
||||||
|
NoteType::native(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
builder
|
builder
|
||||||
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
|
.add_recipient(
|
||||||
|
None,
|
||||||
|
recipient,
|
||||||
|
NoteValue::from_raw(10),
|
||||||
|
NoteType::native(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
|
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
|
||||||
bundle
|
bundle
|
||||||
|
|
|
@ -126,7 +126,7 @@ pub(crate) mod testing {
|
||||||
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use crate::note::NoteType;
|
use crate::note::note_type::testing::arb_note_type;
|
||||||
use crate::{
|
use crate::{
|
||||||
note::{
|
note::{
|
||||||
commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
|
commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
|
||||||
|
@ -147,12 +147,13 @@ pub(crate) mod testing {
|
||||||
nf in arb_nullifier(),
|
nf in arb_nullifier(),
|
||||||
rk in arb_spendauth_verification_key(),
|
rk in arb_spendauth_verification_key(),
|
||||||
note in arb_note(output_value),
|
note in arb_note(output_value),
|
||||||
|
note_type in arb_note_type()
|
||||||
) -> Action<()> {
|
) -> Action<()> {
|
||||||
let cmx = ExtractedNoteCommitment::from(note.commitment());
|
let cmx = ExtractedNoteCommitment::from(note.commitment());
|
||||||
let cv_net = ValueCommitment::derive(
|
let cv_net = ValueCommitment::derive(
|
||||||
spend_value - output_value,
|
spend_value - output_value,
|
||||||
ValueCommitTrapdoor::zero(),
|
ValueCommitTrapdoor::zero(),
|
||||||
NoteType::native()
|
note_type
|
||||||
);
|
);
|
||||||
// FIXME: make a real one from the note.
|
// FIXME: make a real one from the note.
|
||||||
let encrypted_note = TransmittedNoteCiphertext {
|
let encrypted_note = TransmittedNoteCiphertext {
|
||||||
|
@ -179,12 +180,13 @@ pub(crate) mod testing {
|
||||||
note in arb_note(output_value),
|
note in arb_note(output_value),
|
||||||
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
|
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
|
||||||
fake_sighash in prop::array::uniform32(prop::num::u8::ANY),
|
fake_sighash in prop::array::uniform32(prop::num::u8::ANY),
|
||||||
|
note_type in arb_note_type()
|
||||||
) -> Action<redpallas::Signature<SpendAuth>> {
|
) -> Action<redpallas::Signature<SpendAuth>> {
|
||||||
let cmx = ExtractedNoteCommitment::from(note.commitment());
|
let cmx = ExtractedNoteCommitment::from(note.commitment());
|
||||||
let cv_net = ValueCommitment::derive(
|
let cv_net = ValueCommitment::derive(
|
||||||
spend_value - output_value,
|
spend_value - output_value,
|
||||||
ValueCommitTrapdoor::zero(),
|
ValueCommitTrapdoor::zero(),
|
||||||
NoteType::native()
|
note_type
|
||||||
);
|
);
|
||||||
|
|
||||||
// FIXME: make a real one from the note.
|
// FIXME: make a real one from the note.
|
||||||
|
|
156
src/builder.rs
156
src/builder.rs
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
|
@ -57,21 +58,23 @@ impl From<value::OverflowError> for Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a specific note to be spent in an [`Action`].
|
/// Information about a specific note to be spent in an [`Action`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
struct SpendInfo {
|
struct SpendInfo {
|
||||||
dummy_sk: Option<SpendingKey>,
|
dummy_sk: Option<SpendingKey>,
|
||||||
fvk: FullViewingKey,
|
fvk: FullViewingKey,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
note: Note,
|
note: Note,
|
||||||
merkle_path: MerklePath,
|
merkle_path: MerklePath,
|
||||||
|
// a flag to indicate whether the value of the note will be counted in the value sum of the action.
|
||||||
|
split_flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpendInfo {
|
impl SpendInfo {
|
||||||
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
||||||
///
|
///
|
||||||
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
||||||
fn dummy(rng: &mut impl RngCore) -> Self {
|
fn dummy(note_type: NoteType, rng: &mut impl RngCore) -> Self {
|
||||||
let (sk, fvk, note) = Note::dummy(rng, None);
|
let (sk, fvk, note) = Note::dummy(rng, None, note_type);
|
||||||
let merkle_path = MerklePath::dummy(rng);
|
let merkle_path = MerklePath::dummy(rng);
|
||||||
|
|
||||||
SpendInfo {
|
SpendInfo {
|
||||||
|
@ -82,16 +85,25 @@ impl SpendInfo {
|
||||||
scope: Scope::External,
|
scope: Scope::External,
|
||||||
note,
|
note,
|
||||||
merkle_path,
|
merkle_path,
|
||||||
|
split_flag: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Duplicates the spend info and set the split flag to `true`.
|
||||||
|
fn create_split_spend(&self) -> Self {
|
||||||
|
let mut split_spend = self.clone();
|
||||||
|
split_spend.split_flag = true;
|
||||||
|
split_spend
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a specific recipient to receive funds in an [`Action`].
|
/// Information about a specific recipient to receive funds in an [`Action`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
struct RecipientInfo {
|
struct RecipientInfo {
|
||||||
ovk: Option<OutgoingViewingKey>,
|
ovk: Option<OutgoingViewingKey>,
|
||||||
recipient: Address,
|
recipient: Address,
|
||||||
value: NoteValue,
|
value: NoteValue,
|
||||||
|
note_type: NoteType,
|
||||||
memo: Option<[u8; 512]>,
|
memo: Option<[u8; 512]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +119,7 @@ impl RecipientInfo {
|
||||||
ovk: None,
|
ovk: None,
|
||||||
recipient,
|
recipient,
|
||||||
value: NoteValue::zero(),
|
value: NoteValue::zero(),
|
||||||
|
note_type: NoteType::native(),
|
||||||
memo: None,
|
memo: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +143,17 @@ impl ActionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value sum for this action.
|
/// Returns the value sum for this action.
|
||||||
|
/// Split notes does not contribute to the value sum.
|
||||||
fn value_sum(&self) -> ValueSum {
|
fn value_sum(&self) -> ValueSum {
|
||||||
|
// TODO: Aurel, uncomment when circuit for split flag is implemented.
|
||||||
|
// let spent_value = self
|
||||||
|
// .spend
|
||||||
|
// .split_flag
|
||||||
|
// .then(|| self.spend.note.value())
|
||||||
|
// .unwrap_or_else(NoteValue::zero);
|
||||||
|
//
|
||||||
|
// spent_value - self.output.value
|
||||||
|
|
||||||
self.spend.note.value() - self.output.value
|
self.spend.note.value() - self.output.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +163,15 @@ impl ActionInfo {
|
||||||
///
|
///
|
||||||
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||||
fn build(self, mut rng: impl RngCore) -> (Action<SigningMetadata>, Circuit) {
|
fn build(self, mut rng: impl RngCore) -> (Action<SigningMetadata>, Circuit) {
|
||||||
|
assert_eq!(
|
||||||
|
self.output.note_type,
|
||||||
|
self.spend.note.note_type(),
|
||||||
|
"spend and recipient note types must be equal"
|
||||||
|
);
|
||||||
|
|
||||||
let v_net = self.value_sum();
|
let v_net = self.value_sum();
|
||||||
let cv_net = ValueCommitment::derive(v_net, self.rcv, NoteType::native());
|
let note_type = self.output.note_type;
|
||||||
|
let cv_net = ValueCommitment::derive(v_net, self.rcv, note_type);
|
||||||
|
|
||||||
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
|
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
|
||||||
let sender_address = self.spend.note.recipient();
|
let sender_address = self.spend.note.recipient();
|
||||||
|
@ -151,7 +181,6 @@ impl ActionInfo {
|
||||||
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
|
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
|
||||||
let alpha = pallas::Scalar::random(&mut rng);
|
let alpha = pallas::Scalar::random(&mut rng);
|
||||||
let rk = ak.randomize(&alpha);
|
let rk = ak.randomize(&alpha);
|
||||||
let note_type = self.spend.note.note_type();
|
|
||||||
|
|
||||||
let note = Note::new(
|
let note = Note::new(
|
||||||
self.output.recipient,
|
self.output.recipient,
|
||||||
|
@ -201,6 +230,7 @@ impl ActionInfo {
|
||||||
g_d_old: Some(sender_address.g_d()),
|
g_d_old: Some(sender_address.g_d()),
|
||||||
pk_d_old: Some(*sender_address.pk_d()),
|
pk_d_old: Some(*sender_address.pk_d()),
|
||||||
v_old: Some(self.spend.note.value()),
|
v_old: Some(self.spend.note.value()),
|
||||||
|
//split: Some(self.spend.split_flag),
|
||||||
rho_old: Some(rho_old),
|
rho_old: Some(rho_old),
|
||||||
psi_old: Some(psi_old),
|
psi_old: Some(psi_old),
|
||||||
rcm_old: Some(rcm_old),
|
rcm_old: Some(rcm_old),
|
||||||
|
@ -274,6 +304,7 @@ impl Builder {
|
||||||
scope,
|
scope,
|
||||||
note,
|
note,
|
||||||
merkle_path,
|
merkle_path,
|
||||||
|
split_flag: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -285,6 +316,7 @@ impl Builder {
|
||||||
ovk: Option<OutgoingViewingKey>,
|
ovk: Option<OutgoingViewingKey>,
|
||||||
recipient: Address,
|
recipient: Address,
|
||||||
value: NoteValue,
|
value: NoteValue,
|
||||||
|
note_type: NoteType,
|
||||||
memo: Option<[u8; 512]>,
|
memo: Option<[u8; 512]>,
|
||||||
) -> Result<(), &'static str> {
|
) -> Result<(), &'static str> {
|
||||||
if !self.flags.outputs_enabled() {
|
if !self.flags.outputs_enabled() {
|
||||||
|
@ -295,6 +327,7 @@ impl Builder {
|
||||||
ovk,
|
ovk,
|
||||||
recipient,
|
recipient,
|
||||||
value,
|
value,
|
||||||
|
note_type,
|
||||||
memo,
|
memo,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -306,23 +339,31 @@ impl Builder {
|
||||||
/// The returned bundle will have no proof or signatures; these can be applied with
|
/// The returned bundle will have no proof or signatures; these can be applied with
|
||||||
/// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively.
|
/// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively.
|
||||||
pub fn build<V: TryFrom<i64>>(
|
pub fn build<V: TryFrom<i64>>(
|
||||||
mut self,
|
self,
|
||||||
mut rng: impl RngCore,
|
mut rng: impl RngCore,
|
||||||
) -> Result<Bundle<InProgress<Unproven, Unauthorized>, V>, Error> {
|
) -> Result<Bundle<InProgress<Unproven, Unauthorized>, V>, Error> {
|
||||||
|
let mut pre_actions: Vec<_> = Vec::new();
|
||||||
|
|
||||||
// Pair up the spends and recipients, extending with dummy values as necessary.
|
// Pair up the spends and recipients, extending with dummy values as necessary.
|
||||||
let pre_actions: Vec<_> = {
|
for (note_type, (mut spends, mut recipients)) in partition(&self.spends, &self.recipients) {
|
||||||
let num_spends = self.spends.len();
|
let num_spends = spends.len();
|
||||||
let num_recipients = self.recipients.len();
|
let num_recipients = recipients.len();
|
||||||
let num_actions = [num_spends, num_recipients, MIN_ACTIONS]
|
let num_actions = [num_spends, num_recipients, MIN_ACTIONS]
|
||||||
.iter()
|
.iter()
|
||||||
.max()
|
.max()
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.spends.extend(
|
// use the first spend to create split spend(s) or create a dummy if empty.
|
||||||
iter::repeat_with(|| SpendInfo::dummy(&mut rng)).take(num_actions - num_spends),
|
let dummy_spend = spends.first().map_or_else(
|
||||||
|
|| SpendInfo::dummy(note_type, &mut rng),
|
||||||
|
|s| s.create_split_spend(),
|
||||||
);
|
);
|
||||||
self.recipients.extend(
|
|
||||||
|
// Extend the spends and recipients with dummy values.
|
||||||
|
spends.extend(iter::repeat_with(|| dummy_spend.clone()).take(num_actions - num_spends));
|
||||||
|
|
||||||
|
recipients.extend(
|
||||||
iter::repeat_with(|| RecipientInfo::dummy(&mut rng))
|
iter::repeat_with(|| RecipientInfo::dummy(&mut rng))
|
||||||
.take(num_actions - num_recipients),
|
.take(num_actions - num_recipients),
|
||||||
);
|
);
|
||||||
|
@ -330,15 +371,16 @@ impl Builder {
|
||||||
// Shuffle the spends and recipients, so that learning the position of a
|
// Shuffle the spends and recipients, so that learning the position of a
|
||||||
// specific spent note or output note doesn't reveal anything on its own about
|
// specific spent note or output note doesn't reveal anything on its own about
|
||||||
// the meaning of that note in the transaction context.
|
// the meaning of that note in the transaction context.
|
||||||
self.spends.shuffle(&mut rng);
|
spends.shuffle(&mut rng);
|
||||||
self.recipients.shuffle(&mut rng);
|
recipients.shuffle(&mut rng);
|
||||||
|
|
||||||
self.spends
|
pre_actions.extend(
|
||||||
.into_iter()
|
spends
|
||||||
.zip(self.recipients.into_iter())
|
.into_iter()
|
||||||
.map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng))
|
.zip(recipients.into_iter())
|
||||||
.collect()
|
.map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng)),
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Move some things out of self that we will need.
|
// Move some things out of self that we will need.
|
||||||
let flags = self.flags;
|
let flags = self.flags;
|
||||||
|
@ -390,6 +432,30 @@ impl Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// partition a list of spends and recipients by note types.
|
||||||
|
fn partition(
|
||||||
|
spends: &[SpendInfo],
|
||||||
|
recipients: &[RecipientInfo],
|
||||||
|
) -> HashMap<NoteType, (Vec<SpendInfo>, Vec<RecipientInfo>)> {
|
||||||
|
let mut hm = HashMap::new();
|
||||||
|
|
||||||
|
for s in spends.iter() {
|
||||||
|
hm.entry(s.note.note_type())
|
||||||
|
.or_insert((vec![], vec![]))
|
||||||
|
.0
|
||||||
|
.push(s.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for r in recipients.iter() {
|
||||||
|
hm.entry(r.note_type)
|
||||||
|
.or_insert((vec![], vec![]))
|
||||||
|
.1
|
||||||
|
.push(r.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
hm
|
||||||
|
}
|
||||||
|
|
||||||
/// Marker trait representing bundle signatures in the process of being created.
|
/// Marker trait representing bundle signatures in the process of being created.
|
||||||
pub trait InProgressSignatures: fmt::Debug {
|
pub trait InProgressSignatures: fmt::Debug {
|
||||||
/// The authorization type of an Orchard action in the process of being authorized.
|
/// The authorization type of an Orchard action in the process of being authorized.
|
||||||
|
@ -654,6 +720,7 @@ pub mod testing {
|
||||||
use proptest::collection::vec;
|
use proptest::collection::vec;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
|
use crate::note::NoteType;
|
||||||
use crate::{
|
use crate::{
|
||||||
address::testing::arb_address,
|
address::testing::arb_address,
|
||||||
bundle::{Authorized, Bundle, Flags},
|
bundle::{Authorized, Bundle, Flags},
|
||||||
|
@ -681,7 +748,7 @@ pub mod testing {
|
||||||
sk: SpendingKey,
|
sk: SpendingKey,
|
||||||
anchor: Anchor,
|
anchor: Anchor,
|
||||||
notes: Vec<(Note, MerklePath)>,
|
notes: Vec<(Note, MerklePath)>,
|
||||||
recipient_amounts: Vec<(Address, NoteValue)>,
|
recipient_amounts: Vec<(Address, NoteValue, NoteType)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
|
impl<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
|
||||||
|
@ -695,12 +762,12 @@ pub mod testing {
|
||||||
builder.add_spend(fvk.clone(), note, path).unwrap();
|
builder.add_spend(fvk.clone(), note, path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (addr, value) in self.recipient_amounts.into_iter() {
|
for (addr, value, note_type) in self.recipient_amounts.into_iter() {
|
||||||
let scope = fvk.scope_for_address(&addr).unwrap();
|
let scope = fvk.scope_for_address(&addr).unwrap();
|
||||||
let ovk = fvk.to_ovk(scope);
|
let ovk = fvk.to_ovk(scope);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.add_recipient(Some(ovk.clone()), addr, value, None)
|
.add_recipient(Some(ovk.clone()), addr, value, note_type, None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,9 +801,12 @@ pub mod testing {
|
||||||
recipient_amounts in vec(
|
recipient_amounts in vec(
|
||||||
arb_address().prop_flat_map(move |a| {
|
arb_address().prop_flat_map(move |a| {
|
||||||
arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64)
|
arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64)
|
||||||
.prop_map(move |v| (a, v))
|
.prop_map(move |v| {
|
||||||
|
// replace note type with arb_note_type
|
||||||
|
(a,v, NoteType::native())
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
n_recipients as usize
|
n_recipients as usize,
|
||||||
),
|
),
|
||||||
rng_seed in prop::array::uniform32(prop::num::u8::ANY)
|
rng_seed in prop::array::uniform32(prop::num::u8::ANY)
|
||||||
) -> ArbitraryBundleInputs<StdRng> {
|
) -> ArbitraryBundleInputs<StdRng> {
|
||||||
|
@ -784,6 +854,8 @@ mod tests {
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use super::Builder;
|
use super::Builder;
|
||||||
|
// use crate::keys::{IssuerAuthorizingKey, IssuerValidatingKey};
|
||||||
|
use crate::note::NoteType;
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{Authorized, Bundle, Flags},
|
bundle::{Authorized, Bundle, Flags},
|
||||||
circuit::ProvingKey,
|
circuit::ProvingKey,
|
||||||
|
@ -808,8 +880,38 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
|
.add_recipient(
|
||||||
|
None,
|
||||||
|
recipient,
|
||||||
|
NoteValue::from_raw(5000),
|
||||||
|
NoteType::native(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
//
|
||||||
|
// builder
|
||||||
|
// .add_recipient(
|
||||||
|
// None,
|
||||||
|
// recipient,
|
||||||
|
// NoteValue::from_raw(3),
|
||||||
|
// NoteType::native(),
|
||||||
|
// None,
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// let isk = IssuerAuthorizingKey::from(&sk);
|
||||||
|
//
|
||||||
|
// builder
|
||||||
|
// .add_recipient(
|
||||||
|
// None,
|
||||||
|
// recipient,
|
||||||
|
// NoteValue::from_raw(5),
|
||||||
|
// NoteType::derive(&IssuerValidatingKey::from(&isk), Vec::new()),
|
||||||
|
// None,
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
let bundle: Bundle<Authorized, i64> = builder
|
let bundle: Bundle<Authorized, i64> = builder
|
||||||
.build(&mut rng)
|
.build(&mut rng)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -104,6 +104,7 @@ pub struct Circuit {
|
||||||
pub(crate) g_d_old: Option<NonIdentityPallasPoint>,
|
pub(crate) g_d_old: Option<NonIdentityPallasPoint>,
|
||||||
pub(crate) pk_d_old: Option<DiversifiedTransmissionKey>,
|
pub(crate) pk_d_old: Option<DiversifiedTransmissionKey>,
|
||||||
pub(crate) v_old: Option<NoteValue>,
|
pub(crate) v_old: Option<NoteValue>,
|
||||||
|
// pub(crate) split: Option<bool>,
|
||||||
pub(crate) rho_old: Option<Nullifier>,
|
pub(crate) rho_old: Option<Nullifier>,
|
||||||
pub(crate) psi_old: Option<pallas::Base>,
|
pub(crate) psi_old: Option<pallas::Base>,
|
||||||
pub(crate) rcm_old: Option<NoteCommitTrapdoor>,
|
pub(crate) rcm_old: Option<NoteCommitTrapdoor>,
|
||||||
|
@ -891,7 +892,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn generate_circuit_instance<R: RngCore>(mut rng: R) -> (Circuit, Instance) {
|
fn generate_circuit_instance<R: RngCore>(mut rng: R) -> (Circuit, Instance) {
|
||||||
let (_, fvk, spent_note) = Note::dummy(&mut rng, None);
|
let (_, fvk, spent_note) = Note::dummy(&mut rng, None, NoteType::native());
|
||||||
|
|
||||||
let sender_address = spent_note.recipient();
|
let sender_address = spent_note.recipient();
|
||||||
let nk = *fvk.nk();
|
let nk = *fvk.nk();
|
||||||
|
@ -901,7 +902,7 @@ mod tests {
|
||||||
let alpha = pallas::Scalar::random(&mut rng);
|
let alpha = pallas::Scalar::random(&mut rng);
|
||||||
let rk = ak.randomize(&alpha);
|
let rk = ak.randomize(&alpha);
|
||||||
|
|
||||||
let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old));
|
let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), NoteType::native());
|
||||||
let cmx = output_note.commitment().into();
|
let cmx = output_note.commitment().into();
|
||||||
|
|
||||||
let value = spent_note.value() - output_note.value();
|
let value = spent_note.value() - output_note.value();
|
||||||
|
@ -918,6 +919,7 @@ mod tests {
|
||||||
g_d_old: Some(sender_address.g_d()),
|
g_d_old: Some(sender_address.g_d()),
|
||||||
pk_d_old: Some(*sender_address.pk_d()),
|
pk_d_old: Some(*sender_address.pk_d()),
|
||||||
v_old: Some(spent_note.value()),
|
v_old: Some(spent_note.value()),
|
||||||
|
// split: Some(false),
|
||||||
rho_old: Some(spent_note.rho()),
|
rho_old: Some(spent_note.rho()),
|
||||||
psi_old: Some(spent_note.rseed().psi(&spent_note.rho())),
|
psi_old: Some(spent_note.rseed().psi(&spent_note.rho())),
|
||||||
rcm_old: Some(spent_note.rseed().rcm(&spent_note.rho())),
|
rcm_old: Some(spent_note.rseed().rcm(&spent_note.rho())),
|
||||||
|
@ -1118,6 +1120,7 @@ mod tests {
|
||||||
psi_new: None,
|
psi_new: None,
|
||||||
rcm_new: None,
|
rcm_new: None,
|
||||||
rcv: None,
|
rcv: None,
|
||||||
|
// split: None,
|
||||||
};
|
};
|
||||||
halo2_proofs::dev::CircuitLayout::default()
|
halo2_proofs::dev::CircuitLayout::default()
|
||||||
.show_labels(false)
|
.show_labels(false)
|
||||||
|
|
|
@ -163,6 +163,7 @@ impl Note {
|
||||||
pub(crate) fn dummy(
|
pub(crate) fn dummy(
|
||||||
rng: &mut impl RngCore,
|
rng: &mut impl RngCore,
|
||||||
rho: Option<Nullifier>,
|
rho: Option<Nullifier>,
|
||||||
|
note_type: NoteType,
|
||||||
) -> (SpendingKey, FullViewingKey, Self) {
|
) -> (SpendingKey, FullViewingKey, Self) {
|
||||||
let sk = SpendingKey::random(rng);
|
let sk = SpendingKey::random(rng);
|
||||||
let fvk: FullViewingKey = (&sk).into();
|
let fvk: FullViewingKey = (&sk).into();
|
||||||
|
@ -171,7 +172,7 @@ impl Note {
|
||||||
let note = Note::new(
|
let note = Note::new(
|
||||||
recipient,
|
recipient,
|
||||||
NoteValue::zero(),
|
NoteValue::zero(),
|
||||||
NoteType::native(),
|
note_type,
|
||||||
rho.unwrap_or_else(|| Nullifier::dummy(rng)),
|
rho.unwrap_or_else(|| Nullifier::dummy(rng)),
|
||||||
rng,
|
rng,
|
||||||
);
|
);
|
||||||
|
@ -296,7 +297,7 @@ pub mod testing {
|
||||||
}
|
}
|
||||||
|
|
||||||
prop_compose! {
|
prop_compose! {
|
||||||
/// Generate an action without authorization data.
|
/// Generate an arbitrary note
|
||||||
pub fn arb_note(value: NoteValue)(
|
pub fn arb_note(value: NoteValue)(
|
||||||
recipient in arb_address(),
|
recipient in arb_address(),
|
||||||
rho in arb_nullifier(),
|
rho in arb_nullifier(),
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use group::GroupEncoding;
|
use group::GroupEncoding;
|
||||||
use halo2_proofs::arithmetic::CurveExt;
|
use halo2_proofs::arithmetic::CurveExt;
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use subtle::{Choice, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConstantTimeEq, CtOption};
|
||||||
|
|
||||||
use crate::constants::fixed_bases::{VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_V_BYTES};
|
use crate::constants::fixed_bases::{VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_V_BYTES};
|
||||||
use crate::keys::IssuerValidatingKey;
|
use crate::keys::IssuerValidatingKey;
|
||||||
|
|
||||||
/// Note type identifier.
|
/// Note type identifier.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Eq)]
|
||||||
pub struct NoteType(pallas::Point);
|
pub struct NoteType(pallas::Point);
|
||||||
|
|
||||||
pub const MAX_ASSET_DESCRIPTION_SIZE: usize = 512;
|
pub const MAX_ASSET_DESCRIPTION_SIZE: usize = 512;
|
||||||
|
@ -62,6 +63,19 @@ impl NoteType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hash for NoteType {
|
||||||
|
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||||
|
h.write(&self.to_bytes());
|
||||||
|
h.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for NoteType {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
bool::from(self.0.ct_eq(&other.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generators for property testing.
|
/// Generators for property testing.
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
#[cfg(any(test, feature = "test-dependencies"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
|
||||||
|
@ -83,4 +97,12 @@ pub mod testing {
|
||||||
NoteType::derive(&IssuerValidatingKey::from(&isk), bytes64)
|
NoteType::derive(&IssuerValidatingKey::from(&isk), bytes64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prop_compose! {
|
||||||
|
/// Generate the native note type
|
||||||
|
pub fn native_note_type()(_i in 0..10) -> NoteType {
|
||||||
|
// TODO: remove _i
|
||||||
|
NoteType::native()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl Anchor {
|
||||||
|
|
||||||
/// The Merkle path from a leaf of the note commitment tree
|
/// The Merkle path from a leaf of the note commitment tree
|
||||||
/// to its anchor.
|
/// to its anchor.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MerklePath {
|
pub struct MerklePath {
|
||||||
position: u32,
|
position: u32,
|
||||||
auth_path: [MerkleHashOrchard; MERKLE_DEPTH_ORCHARD],
|
auth_path: [MerkleHashOrchard; MERKLE_DEPTH_ORCHARD],
|
||||||
|
|
77
src/value.rs
77
src/value.rs
|
@ -38,6 +38,7 @@
|
||||||
use core::fmt::{self, Debug};
|
use core::fmt::{self, Debug};
|
||||||
use core::iter::Sum;
|
use core::iter::Sum;
|
||||||
use core::ops::{Add, RangeInclusive, Sub};
|
use core::ops::{Add, RangeInclusive, Sub};
|
||||||
|
use std::ops::Neg;
|
||||||
|
|
||||||
use bitvec::{array::BitArray, order::Lsb0};
|
use bitvec::{array::BitArray, order::Lsb0};
|
||||||
use ff::{Field, PrimeField};
|
use ff::{Field, PrimeField};
|
||||||
|
@ -187,6 +188,18 @@ impl Add for ValueSum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Neg for ValueSum {
|
||||||
|
type Output = Option<ValueSum>;
|
||||||
|
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
self.0
|
||||||
|
.checked_neg()
|
||||||
|
.filter(|v| VALUE_SUM_RANGE.contains(v))
|
||||||
|
.map(ValueSum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Sum<&'a ValueSum> for Result<ValueSum, OverflowError> {
|
impl<'a> Sum<&'a ValueSum> for Result<ValueSum, OverflowError> {
|
||||||
fn sum<I: Iterator<Item = &'a ValueSum>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = &'a ValueSum>>(iter: I) -> Self {
|
||||||
iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + *v).ok_or(OverflowError))
|
iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + *v).ok_or(OverflowError))
|
||||||
|
@ -407,7 +420,7 @@ pub mod testing {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::note::note_type::testing::arb_note_type;
|
use crate::note::note_type::testing::{arb_note_type, native_note_type};
|
||||||
use crate::note::NoteType;
|
use crate::note::NoteType;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
|
@ -417,24 +430,42 @@ mod tests {
|
||||||
};
|
};
|
||||||
use crate::primitives::redpallas;
|
use crate::primitives::redpallas;
|
||||||
|
|
||||||
fn _bsk_consistent_with_bvk(values: &[(ValueSum, ValueCommitTrapdoor)], note_type: NoteType) {
|
fn _bsk_consistent_with_bvk(
|
||||||
let value_balance = values
|
native_values: &[(ValueSum, ValueCommitTrapdoor, NoteType)],
|
||||||
|
arb_values: &[(ValueSum, ValueCommitTrapdoor, NoteType)],
|
||||||
|
neg_trapdoors: &[ValueCommitTrapdoor],
|
||||||
|
) {
|
||||||
|
// for each arb value, create a negative value with a different trapdoor
|
||||||
|
let neg_arb_values: Vec<_> = arb_values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(value, _)| value)
|
.cloned()
|
||||||
|
.zip(neg_trapdoors.iter().cloned())
|
||||||
|
.map(|((value, _, note_type), rcv)| ((-value).unwrap(), rcv, note_type))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let native_value_balance = native_values
|
||||||
|
.iter()
|
||||||
|
.map(|(value, _, _)| value)
|
||||||
.sum::<Result<ValueSum, OverflowError>>()
|
.sum::<Result<ValueSum, OverflowError>>()
|
||||||
.expect("we generate values that won't overflow");
|
.expect("we generate values that won't overflow");
|
||||||
|
|
||||||
|
let values = [native_values, arb_values, &neg_arb_values].concat();
|
||||||
|
|
||||||
let bsk = values
|
let bsk = values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, rcv)| rcv)
|
.map(|(_, rcv, _)| rcv)
|
||||||
.sum::<ValueCommitTrapdoor>()
|
.sum::<ValueCommitTrapdoor>()
|
||||||
.into_bsk();
|
.into_bsk();
|
||||||
|
|
||||||
let bvk = (values
|
let bvk = (values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(value, rcv)| ValueCommitment::derive(*value, *rcv, note_type))
|
.map(|(value, rcv, note_type)| ValueCommitment::derive(*value, *rcv, *note_type))
|
||||||
.sum::<ValueCommitment>()
|
.sum::<ValueCommitment>()
|
||||||
- ValueCommitment::derive(value_balance, ValueCommitTrapdoor::zero(), note_type))
|
- ValueCommitment::derive(
|
||||||
|
native_value_balance,
|
||||||
|
ValueCommitTrapdoor::zero(),
|
||||||
|
NoteType::native(),
|
||||||
|
))
|
||||||
.into_bvk();
|
.into_bvk();
|
||||||
|
|
||||||
assert_eq!(redpallas::VerificationKey::from(&bsk), bvk);
|
assert_eq!(redpallas::VerificationKey::from(&bsk), bvk);
|
||||||
|
@ -442,18 +473,34 @@ mod tests {
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn bsk_consistent_with_bvk(
|
fn bsk_consistent_with_bvk_native_only(
|
||||||
values in (1usize..10).prop_flat_map(|n_values|
|
native_values in (1usize..10).prop_flat_map(|n_values|
|
||||||
arb_note_value_bounded(MAX_NOTE_VALUE / n_values as u64).prop_flat_map(move |bound|
|
arb_note_value_bounded(MAX_NOTE_VALUE / n_values as u64).prop_flat_map(move |bound|
|
||||||
prop::collection::vec((arb_value_sum_bounded(bound), arb_trapdoor()), n_values)
|
prop::collection::vec((arb_value_sum_bounded(bound), arb_trapdoor(), native_note_type()), n_values)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
arb_note_type in arb_note_type(),
|
|
||||||
) {
|
) {
|
||||||
// Test with native note type (zec)
|
// Test with native note type (zec) only
|
||||||
_bsk_consistent_with_bvk(&values, NoteType::native());
|
_bsk_consistent_with_bvk(&native_values, &[], &[]);
|
||||||
// Test with arbitrary note type
|
}
|
||||||
_bsk_consistent_with_bvk(&values, arb_note_type);
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#[test]
|
||||||
|
fn bsk_consistent_with_bvk(
|
||||||
|
native_values in (1usize..10).prop_flat_map(|n_values|
|
||||||
|
arb_note_value_bounded(MAX_NOTE_VALUE / n_values as u64).prop_flat_map(move |bound|
|
||||||
|
prop::collection::vec((arb_value_sum_bounded(bound), arb_trapdoor(), native_note_type()), n_values)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(arb_values,neg_trapdoors) in (1usize..10).prop_flat_map(|n_values|
|
||||||
|
(arb_note_value_bounded(MAX_NOTE_VALUE / n_values as u64).prop_flat_map(move |bound|
|
||||||
|
prop::collection::vec((arb_value_sum_bounded(bound), arb_trapdoor(), arb_note_type()), n_values)
|
||||||
|
), prop::collection::vec(arb_trapdoor(), n_values))
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
// Test with native and arbitrary note types
|
||||||
|
_bsk_consistent_with_bvk(&native_values, &arb_values, &neg_trapdoors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use incrementalmerkletree::{bridgetree::BridgeTree, Hashable, Tree};
|
use incrementalmerkletree::{bridgetree::BridgeTree, Hashable, Tree};
|
||||||
|
use orchard::note::NoteType;
|
||||||
use orchard::{
|
use orchard::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
bundle::{Authorized, Flags},
|
bundle::{Authorized, Flags},
|
||||||
|
@ -43,7 +44,13 @@ fn bundle_chain() {
|
||||||
|
|
||||||
let mut builder = Builder::new(Flags::from_parts(false, true), anchor);
|
let mut builder = Builder::new(Flags::from_parts(false, true), anchor);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None),
|
builder.add_recipient(
|
||||||
|
None,
|
||||||
|
recipient,
|
||||||
|
NoteValue::from_raw(5000),
|
||||||
|
NoteType::native(),
|
||||||
|
None
|
||||||
|
),
|
||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
let unauthorized = builder.build(&mut rng).unwrap();
|
let unauthorized = builder.build(&mut rng).unwrap();
|
||||||
|
@ -85,7 +92,13 @@ fn bundle_chain() {
|
||||||
let mut builder = Builder::new(Flags::from_parts(true, true), anchor);
|
let mut builder = Builder::new(Flags::from_parts(true, true), anchor);
|
||||||
assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(()));
|
assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None),
|
builder.add_recipient(
|
||||||
|
None,
|
||||||
|
recipient,
|
||||||
|
NoteValue::from_raw(5000),
|
||||||
|
NoteType::native(),
|
||||||
|
None
|
||||||
|
),
|
||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
let unauthorized = builder.build(&mut rng).unwrap();
|
let unauthorized = builder.build(&mut rng).unwrap();
|
||||||
|
|
Loading…
Reference in New Issue