Orchard: Fix trait imports and Arbitrary for Action

This commit is contained in:
Deirdre Connolly 2021-03-13 06:19:55 -05:00 committed by Deirdre Connolly
parent 981080f049
commit cb9d6956d7
11 changed files with 141 additions and 71 deletions

View File

@ -1,6 +1,6 @@
use std::io; use std::io;
use halo2::pasta::pallas; use halo2::{arithmetic::FieldExt, pasta::pallas};
use crate::{ use crate::{
primitives::redpallas::{self, SpendAuth}, primitives::redpallas::{self, SpendAuth},
@ -27,7 +27,7 @@ pub struct Action {
/// A value commitment to net value of the input note minus the output note /// A value commitment to net value of the input note minus the output note
pub cv: commitment::ValueCommitment, pub cv: commitment::ValueCommitment,
/// The nullifier of the input note being spent. /// The nullifier of the input note being spent.
pub nullifer: note::Nullifier, pub nullifier: note::Nullifier,
/// The randomized validating key for spendAuthSig, /// The randomized validating key for spendAuthSig,
pub rk: redpallas::VerificationKeyBytes<SpendAuth>, pub rk: redpallas::VerificationKeyBytes<SpendAuth>,
/// The 𝑥-coordinate of the note commitment for the output note. /// The 𝑥-coordinate of the note commitment for the output note.

View File

@ -1,56 +1,30 @@
use jubjub::AffinePoint; use group::prime::PrimeCurveAffine;
use proptest::{arbitrary::any, array, collection::vec, prelude::*}; use halo2::pasta::pallas;
use proptest::{arbitrary::any, array, prelude::*};
use crate::primitives::Groth16Proof; use super::{keys, note, Action, NoteCommitment, ValueCommitment};
use super::{keys, note, tree, NoteCommitment, Output, Spend, ValueCommitment}; impl Arbitrary for Action {
impl Arbitrary for Spend {
type Parameters = (); type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
( (
any::<tree::Root>(),
any::<note::Nullifier>(), any::<note::Nullifier>(),
array::uniform32(any::<u8>()), array::uniform32(any::<u8>()),
any::<Groth16Proof>(),
vec(any::<u8>(), 64),
)
.prop_map(|(anchor, nullifier, rpk_bytes, proof, sig_bytes)| Self {
anchor,
cv: ValueCommitment(AffinePoint::identity()),
nullifier,
rk: redjubjub::VerificationKeyBytes::from(rpk_bytes),
zkproof: proof,
spend_auth_sig: redjubjub::Signature::from({
let mut b = [0u8; 64];
b.copy_from_slice(sig_bytes.as_slice());
b
}),
})
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for Output {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<note::EncryptedNote>(), any::<note::EncryptedNote>(),
any::<note::WrappedNoteKey>(), any::<note::WrappedNoteKey>(),
any::<Groth16Proof>(),
) )
.prop_map(|(enc_ciphertext, out_ciphertext, zkproof)| Self { .prop_map(
cv: ValueCommitment(AffinePoint::identity()), |(nullifier, rpk_bytes, enc_ciphertext, out_ciphertext)| Self {
cm_u: NoteCommitment(AffinePoint::identity()).extract_u(), cv: ValueCommitment(pallas::Affine::identity()),
ephemeral_key: keys::EphemeralPublicKey(AffinePoint::identity()), nullifier,
enc_ciphertext, rk: crate::primitives::redpallas::VerificationKeyBytes::from(rpk_bytes),
out_ciphertext, cm_x: NoteCommitment(pallas::Affine::identity()).extract_x(),
zkproof, ephemeral_key: keys::EphemeralPublicKey(pallas::Affine::identity()),
}) enc_ciphertext,
out_ciphertext,
},
)
.boxed() .boxed()
} }

View File

@ -6,7 +6,12 @@
use std::{convert::TryFrom, fmt, io}; use std::{convert::TryFrom, fmt, io};
use bitvec::prelude::*; use bitvec::prelude::*;
use halo2::pasta::pallas; use group::{prime::PrimeCurveAffine, GroupEncoding};
use halo2::{
arithmetic::{CurveAffine, FieldExt},
pasta::pallas,
};
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
use crate::{ use crate::{
@ -44,9 +49,13 @@ pub struct NoteCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::A
impl fmt::Debug for NoteCommitment { impl fmt::Debug for NoteCommitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// This will panic if the public key is the identity, which is bad news
// bears.
let (x, y) = self.0.get_xy().unwrap();
f.debug_struct("NoteCommitment") f.debug_struct("NoteCommitment")
.field("x", &hex::encode(self.0.get_x().to_bytes())) .field("x", &hex::encode(x.to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes())) .field("y", &hex::encode(y.to_bytes()))
.finish() .finish()
} }
} }
@ -136,7 +145,7 @@ impl NoteCommitment {
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub pallas::Affine); pub struct ValueCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
impl<'a> std::ops::Add<&'a ValueCommitment> for ValueCommitment { impl<'a> std::ops::Add<&'a ValueCommitment> for ValueCommitment {
type Output = Self; type Output = Self;
@ -163,9 +172,13 @@ impl std::ops::AddAssign<ValueCommitment> for ValueCommitment {
impl fmt::Debug for ValueCommitment { impl fmt::Debug for ValueCommitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// This will panic if the public key is the identity, which is bad news
// bears.
let (x, y) = self.0.get_xy().unwrap();
f.debug_struct("ValueCommitment") f.debug_struct("ValueCommitment")
.field("x", &hex::encode(self.0.get_x().to_bytes())) .field("x", &hex::encode(x.to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes())) .field("y", &hex::encode(y.to_bytes()))
.finish() .finish()
} }
} }
@ -228,7 +241,7 @@ impl TryFrom<[u8; 32]> for ValueCommitment {
type Error = &'static str; type Error = &'static str;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> { fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let possible_point = pallas::Affine::from_bytes(bytes); let possible_point = pallas::Affine::from_bytes(&bytes);
if possible_point.is_some().into() { if possible_point.is_some().into() {
Ok(Self(possible_point.unwrap())) Ok(Self(possible_point.unwrap()))
@ -271,12 +284,12 @@ impl ValueCommitment {
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn new(rcv: pallas::Scalar, value: Amount) -> Self { pub fn new(rcv: pallas::Scalar, value: Amount) -> Self {
let v = pallas::Scalar::from(value); let v = pallas::Scalar::from_bytes(value.to_bytes());
// TODO: These generator points can be generated once somewhere else to // TODO: These generator points can be generated once somewhere else to
// avoid having to recompute them on every new commitment. // avoid having to recompute them on every new commitment.
let V = pallas_group_hash(*b"z.cash:Orchard-cv", b"v"); let V = pallas_group_hash(b"z.cash:Orchard-cv", b"v");
let R = pallas_group_hash(*b"z.cash:Orchard-cv", b"r"); let R = pallas_group_hash(b"z.cash:Orchard-cv", b"r");
Self::from(V * v + R * rcv) Self::from(V * v + R * rcv)
} }

View File

@ -18,7 +18,7 @@ use std::{
use aes::Aes256; use aes::Aes256;
use bech32::{self, FromBase32, ToBase32, Variant}; use bech32::{self, FromBase32, ToBase32, Variant};
use fpe::ff1::{BinaryNumeralString, FF1}; use fpe::ff1::{BinaryNumeralString, FF1};
use group::{prime::PrimeCurveAffine, GroupEncoding}; use group::GroupEncoding;
use halo2::{ use halo2::{
arithmetic::{CurveAffine, FieldExt}, arithmetic::{CurveAffine, FieldExt},
pasta::pallas, pasta::pallas,

View File

@ -2,7 +2,10 @@
use bitvec::prelude::*; use bitvec::prelude::*;
use halo2::pasta::pallas; use halo2::{
arithmetic::{CurveAffine, CurveExt},
pasta::pallas,
};
/// [Hash Extractor for Pallas][concreteextractorpallas] /// [Hash Extractor for Pallas][concreteextractorpallas]
/// ///
@ -27,7 +30,9 @@ pub fn extract_p(point: pallas::Point) -> pallas::Base {
/// https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta /// https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn pallas_group_hash(D: &[u8], M: &[u8]) -> pallas::Point { pub fn pallas_group_hash(D: &[u8], M: &[u8]) -> pallas::Point {
pallas::Point::hash_to_curve(D)(M) let domain_separator = std::str::from_utf8(D).unwrap();
pallas::Point::hash_to_curve(domain_separator)(M)
} }
/// Q(D) := GroupHash^P(“z.cash:SinsemillaQ”, D) /// Q(D) := GroupHash^P(“z.cash:SinsemillaQ”, D)

View File

@ -11,11 +11,11 @@
//! A root of a note commitment tree is associated with each treestate. //! A root of a note commitment tree is associated with each treestate.
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
#![allow(dead_code)]
use std::{collections::VecDeque, fmt}; use std::{collections::VecDeque, fmt};
use bitvec::prelude::*; use bitvec::prelude::*;
use group::GroupEncoding;
use lazy_static::lazy_static; use lazy_static::lazy_static;
#[cfg(any(test, feature = "proptest-impl"))] #[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary; use proptest_derive::Arbitrary;
@ -42,7 +42,7 @@ fn merkle_crh_orchard(layer: u8, left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
s.extend_from_slice(&left.bits::<Lsb0>()[0..255]); s.extend_from_slice(&left.bits::<Lsb0>()[0..255]);
s.extend_from_slice(&right.bits::<Lsb0>()[0..255]); s.extend_from_slice(&right.bits::<Lsb0>()[0..255]);
sinsemilla_hash_to_point(*b"Zcash_PH", &s).to_bytes() sinsemilla_hash_to_point(b"Zcash_PH", &s).to_bytes()
} }
lazy_static! { lazy_static! {

View File

@ -1,7 +1,6 @@
// Extracted from redjubjub for now. // XXX: Extracted from redjubjub for now.
#![deny(missing_docs)]
use group::GroupEncoding;
use halo2::pasta::pallas; use halo2::pasta::pallas;
// pub mod batch; // pub mod batch;

View File

@ -3,9 +3,10 @@ use std::{
marker::PhantomData, marker::PhantomData,
}; };
use halo2::arithmetic::FieldExt;
use halo2::pasta::pallas; use halo2::pasta::pallas;
use super::{SigType, VerificationKey}; use super::{Error, SigType, VerificationKey};
/// A RedPallas signing key. /// A RedPallas signing key.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -17,3 +18,25 @@ pub struct SigningKey<T: SigType> {
sk: pallas::Scalar, sk: pallas::Scalar,
pk: VerificationKey<T>, pk: VerificationKey<T>,
} }
impl<'a, T: SigType> From<&'a SigningKey<T>> for VerificationKey<T> {
fn from(sk: &'a SigningKey<T>) -> VerificationKey<T> {
sk.pk.clone()
}
}
impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
type Error = Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let maybe_sk = pallas::Scalar::from_bytes(&bytes);
if maybe_sk.is_some().into() {
let sk = maybe_sk.unwrap();
let pk = VerificationKey::from(&sk);
Ok(SigningKey { sk, pk })
} else {
Err(Error::MalformedSigningKey)
}
}
}

View File

@ -4,9 +4,10 @@ use std::{
marker::PhantomData, marker::PhantomData,
}; };
use group::{cofactor::CofactorGroup, GroupEncoding};
use halo2::pasta::pallas; use halo2::pasta::pallas;
use super::SigType; use super::{Error, SigType, SigningKey};
/// A refinement type for `[u8; 32]` indicating that the bytes represent /// A refinement type for `[u8; 32]` indicating that the bytes represent
/// an encoding of a RedPallas verification key. /// an encoding of a RedPallas verification key.
@ -65,3 +66,57 @@ pub struct VerificationKey<T: SigType> {
pub(crate) point: pallas::Point, pub(crate) point: pallas::Point,
pub(crate) bytes: VerificationKeyBytes<T>, pub(crate) bytes: VerificationKeyBytes<T>,
} }
impl<T: SigType> From<VerificationKey<T>> for VerificationKeyBytes<T> {
fn from(pk: VerificationKey<T>) -> VerificationKeyBytes<T> {
pk.bytes
}
}
impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
fn from(pk: VerificationKey<T>) -> [u8; 32] {
pk.bytes.bytes
}
}
impl<T: SigType> TryFrom<VerificationKeyBytes<T>> for VerificationKey<T> {
type Error = Error;
fn try_from(bytes: VerificationKeyBytes<T>) -> Result<Self, Self::Error> {
// This checks that the encoding is canonical...
let maybe_point = pallas::Affine::from_bytes(&bytes.bytes);
if maybe_point.is_some().into() {
let point: pallas::Point = maybe_point.unwrap().into();
// This checks that the verification key is not of small order.
if <bool>::from(point.is_small_order()) == false {
Ok(VerificationKey { point, bytes })
} else {
Err(Error::MalformedVerificationKey)
}
} else {
Err(Error::MalformedVerificationKey)
}
}
}
impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
type Error = Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
use std::convert::TryInto;
VerificationKeyBytes::from(bytes).try_into()
}
}
impl<T: SigType> VerificationKey<T> {
pub(crate) fn from(s: &pallas::Scalar) -> VerificationKey<T> {
let point = &T::basepoint() * s;
let bytes = VerificationKeyBytes {
bytes: pallas::Affine::from(&point).to_bytes(),
_marker: PhantomData,
};
VerificationKey { bytes, point }
}
}

View File

@ -15,7 +15,7 @@ mod test_vectors;
mod tests; mod tests;
use std::{ use std::{
convert::{From, Into, TryFrom}, convert::{From, Into, TryFrom, TryInto},
fmt, fmt,
io::{self, Write}, io::{self, Write},
str::FromStr, str::FromStr,
@ -73,13 +73,13 @@ fn prf_ock(ovk: [u8; 32], cv: [u8; 32], cm_u: [u8; 32], ephemeral_key: [u8; 32])
.hash_length(32) .hash_length(32)
.personal(b"Zcash_Derive_ock") .personal(b"Zcash_Derive_ock")
.to_state() .to_state()
.update(ovk) .update(&ovk)
.update(cv) .update(&cv)
.update(cm_u) .update(&cm_u)
.update(ephemeral_key) .update(&ephemeral_key)
.finalize(); .finalize();
*hash.as_array() *hash.as_bytes().try_into().expect("32 byte array")
} }
/// Invokes Blake2s-256 as _CRH^ivk_, to derive the IncomingViewingKey /// Invokes Blake2s-256 as _CRH^ivk_, to derive the IncomingViewingKey

View File

@ -1,4 +1,5 @@
use halo2::pasta::pallas; use group::GroupEncoding;
use halo2::{arithmetic::FieldExt, pasta::pallas};
use serde_big_array::big_array; use serde_big_array::big_array;
big_array! { big_array! {