Orchard: Fix trait imports and Arbitrary for Action
This commit is contained in:
parent
981080f049
commit
cb9d6956d7
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
rk: crate::primitives::redpallas::VerificationKeyBytes::from(rpk_bytes),
|
||||||
|
cm_x: NoteCommitment(pallas::Affine::identity()).extract_x(),
|
||||||
|
ephemeral_key: keys::EphemeralPublicKey(pallas::Affine::identity()),
|
||||||
enc_ciphertext,
|
enc_ciphertext,
|
||||||
out_ciphertext,
|
out_ciphertext,
|
||||||
zkproof,
|
},
|
||||||
})
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
Loading…
Reference in New Issue