Merge pull request #1067 from zcash/738-extract-sapling-crypto
Extract `sapling-crypto` crate again
This commit is contained in:
commit
164e21dbd6
|
@ -2085,6 +2085,37 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sapling-crypto"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "git+https://github.com/zcash/sapling-crypto.git?rev=df6681c1046bd28073befd7d42224e4a4c70032a#df6681c1046bd28073befd7d42224e4a4c70032a"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"bellman",
|
||||||
|
"bitvec",
|
||||||
|
"blake2b_simd",
|
||||||
|
"blake2s_simd",
|
||||||
|
"bls12_381",
|
||||||
|
"byteorder",
|
||||||
|
"ff",
|
||||||
|
"fpe",
|
||||||
|
"group",
|
||||||
|
"hex",
|
||||||
|
"incrementalmerkletree",
|
||||||
|
"jubjub",
|
||||||
|
"lazy_static",
|
||||||
|
"memuse",
|
||||||
|
"proptest",
|
||||||
|
"rand",
|
||||||
|
"rand_core",
|
||||||
|
"redjubjub",
|
||||||
|
"subtle",
|
||||||
|
"tracing",
|
||||||
|
"zcash_note_encryption",
|
||||||
|
"zcash_spec",
|
||||||
|
"zip32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemer"
|
name = "schemer"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -3079,12 +3110,8 @@ version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"assert_matches",
|
"assert_matches",
|
||||||
"bellman",
|
|
||||||
"bip0039",
|
"bip0039",
|
||||||
"bitvec",
|
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"blake2s_simd",
|
|
||||||
"bls12_381",
|
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
@ -3096,7 +3123,6 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"incrementalmerkletree",
|
"incrementalmerkletree",
|
||||||
"jubjub",
|
"jubjub",
|
||||||
"lazy_static",
|
|
||||||
"memuse",
|
"memuse",
|
||||||
"nonempty",
|
"nonempty",
|
||||||
"orchard",
|
"orchard",
|
||||||
|
@ -3107,6 +3133,7 @@ dependencies = [
|
||||||
"rand_xorshift",
|
"rand_xorshift",
|
||||||
"redjubjub",
|
"redjubjub",
|
||||||
"ripemd",
|
"ripemd",
|
||||||
|
"sapling-crypto",
|
||||||
"secp256k1",
|
"secp256k1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
|
|
@ -47,6 +47,7 @@ bitvec = "1"
|
||||||
blake2s_simd = "1"
|
blake2s_simd = "1"
|
||||||
bls12_381 = "0.8"
|
bls12_381 = "0.8"
|
||||||
jubjub = "0.10"
|
jubjub = "0.10"
|
||||||
|
sapling = { package = "sapling-crypto", version = "0.0" }
|
||||||
|
|
||||||
# - Orchard
|
# - Orchard
|
||||||
nonempty = "0.7"
|
nonempty = "0.7"
|
||||||
|
@ -109,3 +110,6 @@ zip32 = "0.1"
|
||||||
lto = true
|
lto = true
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
sapling = { package = "sapling-crypto", git = "https://github.com/zcash/sapling-crypto.git", rev = "df6681c1046bd28073befd7d42224e4a4c70032a" }
|
||||||
|
|
|
@ -41,21 +41,17 @@ tracing.workspace = true
|
||||||
subtle.workspace = true
|
subtle.workspace = true
|
||||||
|
|
||||||
# - Shielded protocols
|
# - Shielded protocols
|
||||||
bellman = { version = "0.14", default-features = false, features = ["groth16"] }
|
|
||||||
bls12_381.workspace = true
|
|
||||||
ff.workspace = true
|
ff.workspace = true
|
||||||
group = { workspace = true, features = ["wnaf-memuse"] }
|
group = { workspace = true, features = ["wnaf-memuse"] }
|
||||||
jubjub.workspace = true
|
jubjub.workspace = true
|
||||||
nonempty.workspace = true
|
nonempty.workspace = true
|
||||||
orchard.workspace = true
|
orchard.workspace = true
|
||||||
|
sapling.workspace = true
|
||||||
zcash_spec.workspace = true
|
zcash_spec.workspace = true
|
||||||
|
|
||||||
# - Note Commitment Trees
|
# - Note Commitment Trees
|
||||||
incrementalmerkletree = { workspace = true, features = ["legacy-api"] }
|
incrementalmerkletree = { workspace = true, features = ["legacy-api"] }
|
||||||
|
|
||||||
# - Static constants
|
|
||||||
lazy_static.workspace = true
|
|
||||||
|
|
||||||
# - Test dependencies
|
# - Test dependencies
|
||||||
proptest = { workspace = true, optional = true }
|
proptest = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
@ -75,8 +71,6 @@ byteorder.workspace = true
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
|
|
||||||
# - Shielded protocols
|
# - Shielded protocols
|
||||||
bitvec.workspace = true
|
|
||||||
blake2s_simd.workspace = true
|
|
||||||
redjubjub = "0.7"
|
redjubjub = "0.7"
|
||||||
|
|
||||||
# - Transparent inputs
|
# - Transparent inputs
|
||||||
|
@ -104,10 +98,14 @@ pprof = { version = "0.11", features = ["criterion", "flamegraph"] } # MSRV 1.56
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["multicore"]
|
default = ["multicore"]
|
||||||
multicore = ["bellman/multicore", "orchard/multicore"]
|
multicore = ["orchard/multicore", "sapling/multicore"]
|
||||||
transparent-inputs = ["hdwallet", "ripemd", "secp256k1"]
|
transparent-inputs = ["hdwallet", "ripemd", "secp256k1"]
|
||||||
temporary-zcashd = []
|
temporary-zcashd = []
|
||||||
test-dependencies = ["proptest", "orchard/test-dependencies"]
|
test-dependencies = [
|
||||||
|
"proptest",
|
||||||
|
"orchard/test-dependencies",
|
||||||
|
"sapling/test-dependencies",
|
||||||
|
]
|
||||||
unstable-nu6 = []
|
unstable-nu6 = []
|
||||||
zfuture = []
|
zfuture = []
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,10 @@ pub mod keys;
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
pub mod memo;
|
pub mod memo;
|
||||||
pub mod merkle_tree;
|
pub mod merkle_tree;
|
||||||
pub mod sapling;
|
pub use sapling;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
pub use zip32;
|
pub use zip32;
|
||||||
pub mod zip339;
|
pub mod zip339;
|
||||||
|
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
pub mod extensions;
|
pub mod extensions;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_vectors;
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
//! Structs and constants specific to the Sapling shielded pool.
|
|
||||||
|
|
||||||
mod address;
|
|
||||||
pub mod builder;
|
|
||||||
pub mod bundle;
|
|
||||||
pub mod circuit;
|
|
||||||
pub mod constants;
|
|
||||||
pub mod group_hash;
|
|
||||||
pub mod keys;
|
|
||||||
pub mod note;
|
|
||||||
pub mod note_encryption;
|
|
||||||
pub mod pedersen_hash;
|
|
||||||
pub mod prover;
|
|
||||||
mod spec;
|
|
||||||
mod tree;
|
|
||||||
pub mod util;
|
|
||||||
pub mod value;
|
|
||||||
mod verifier;
|
|
||||||
pub mod zip32;
|
|
||||||
|
|
||||||
pub use address::PaymentAddress;
|
|
||||||
pub use bundle::Bundle;
|
|
||||||
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
|
||||||
pub use note::{nullifier::Nullifier, Note, Rseed};
|
|
||||||
pub use tree::{
|
|
||||||
merkle_hash, CommitmentTree, IncrementalWitness, MerklePath, Node, NOTE_COMMITMENT_TREE_DEPTH,
|
|
||||||
};
|
|
||||||
pub use verifier::{BatchValidator, SaplingVerificationContext};
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub mod testing {
|
|
||||||
pub use super::{
|
|
||||||
address::testing::arb_payment_address, keys::testing::arb_incoming_viewing_key,
|
|
||||||
note::testing::arb_note, tree::testing::arb_node,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_vectors;
|
|
|
@ -1,118 +0,0 @@
|
||||||
use super::{
|
|
||||||
keys::{DiversifiedTransmissionKey, Diversifier},
|
|
||||||
note::{Note, Rseed},
|
|
||||||
value::NoteValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A Sapling payment address.
|
|
||||||
///
|
|
||||||
/// # Invariants
|
|
||||||
///
|
|
||||||
/// - `diversifier` is guaranteed to be valid for Sapling (only 50% of diversifiers are).
|
|
||||||
/// - `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
|
|
||||||
/// and not the identity).
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct PaymentAddress {
|
|
||||||
pk_d: DiversifiedTransmissionKey,
|
|
||||||
diversifier: Diversifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for PaymentAddress {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.pk_d == other.pk_d && self.diversifier == other.diversifier
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for PaymentAddress {}
|
|
||||||
|
|
||||||
impl PaymentAddress {
|
|
||||||
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
|
|
||||||
///
|
|
||||||
/// Returns None if `diversifier` is not valid for Sapling, or `pk_d` is the identity.
|
|
||||||
/// Note that we cannot verify in this constructor that `pk_d` is derived from
|
|
||||||
/// `diversifier`, so addresses for which these values have no known relationship
|
|
||||||
/// (and therefore no-one can receive funds at them) can still be constructed.
|
|
||||||
pub fn from_parts(diversifier: Diversifier, pk_d: DiversifiedTransmissionKey) -> Option<Self> {
|
|
||||||
// Check that the diversifier is valid
|
|
||||||
diversifier.g_d()?;
|
|
||||||
|
|
||||||
Self::from_parts_unchecked(diversifier, pk_d)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
|
|
||||||
///
|
|
||||||
/// Returns None if `pk_d` is the identity. The caller must check that `diversifier`
|
|
||||||
/// is valid for Sapling.
|
|
||||||
pub(crate) fn from_parts_unchecked(
|
|
||||||
diversifier: Diversifier,
|
|
||||||
pk_d: DiversifiedTransmissionKey,
|
|
||||||
) -> Option<Self> {
|
|
||||||
if pk_d.is_identity() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(PaymentAddress { pk_d, diversifier })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a PaymentAddress from bytes.
|
|
||||||
pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
|
|
||||||
let diversifier = {
|
|
||||||
let mut tmp = [0; 11];
|
|
||||||
tmp.copy_from_slice(&bytes[0..11]);
|
|
||||||
Diversifier(tmp)
|
|
||||||
};
|
|
||||||
|
|
||||||
let pk_d = DiversifiedTransmissionKey::from_bytes(bytes[11..43].try_into().unwrap());
|
|
||||||
if pk_d.is_some().into() {
|
|
||||||
// The remaining invariants are checked here.
|
|
||||||
PaymentAddress::from_parts(diversifier, pk_d.unwrap())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the byte encoding of this `PaymentAddress`.
|
|
||||||
pub fn to_bytes(&self) -> [u8; 43] {
|
|
||||||
let mut bytes = [0; 43];
|
|
||||||
bytes[0..11].copy_from_slice(&self.diversifier.0);
|
|
||||||
bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [`Diversifier`] for this `PaymentAddress`.
|
|
||||||
pub fn diversifier(&self) -> &Diversifier {
|
|
||||||
&self.diversifier
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `pk_d` for this `PaymentAddress`.
|
|
||||||
pub fn pk_d(&self) -> &DiversifiedTransmissionKey {
|
|
||||||
&self.pk_d
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn g_d(&self) -> jubjub::SubgroupPoint {
|
|
||||||
self.diversifier.g_d().expect("checked at construction")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_note(&self, value: NoteValue, rseed: Rseed) -> Note {
|
|
||||||
Note::from_parts(*self, value, rseed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub(super) mod testing {
|
|
||||||
use proptest::prelude::*;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
super::keys::{testing::arb_incoming_viewing_key, Diversifier, SaplingIvk},
|
|
||||||
PaymentAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn arb_payment_address() -> impl Strategy<Value = PaymentAddress> {
|
|
||||||
arb_incoming_viewing_key().prop_flat_map(|ivk: SaplingIvk| {
|
|
||||||
any::<[u8; 11]>().prop_filter_map(
|
|
||||||
"Sampled diversifier must generate a valid Sapling payment address.",
|
|
||||||
move |d| ivk.to_payment_address(Diversifier(d)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,937 +0,0 @@
|
||||||
//! Types and functions for building Sapling transaction components.
|
|
||||||
|
|
||||||
use core::fmt;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use group::ff::Field;
|
|
||||||
use rand::{seq::SliceRandom, RngCore};
|
|
||||||
use rand_core::CryptoRng;
|
|
||||||
use redjubjub::{Binding, SpendAuth};
|
|
||||||
|
|
||||||
use crate::sapling::{
|
|
||||||
self,
|
|
||||||
bundle::{
|
|
||||||
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
|
|
||||||
SpendDescription,
|
|
||||||
},
|
|
||||||
keys::{OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey},
|
|
||||||
note_encryption::{sapling_note_encryption, Zip212Enforcement},
|
|
||||||
prover::{OutputProver, SpendProver},
|
|
||||||
util::generate_random_rseed_internal,
|
|
||||||
value::{
|
|
||||||
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
|
|
||||||
},
|
|
||||||
zip32::ExtendedSpendingKey,
|
|
||||||
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// If there are any shielded inputs, always have at least two shielded outputs, padding
|
|
||||||
/// with dummy outputs if necessary. See <https://github.com/zcash/zcash/issues/3615>.
|
|
||||||
const MIN_SHIELDED_OUTPUTS: usize = 2;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum Error {
|
|
||||||
AnchorMismatch,
|
|
||||||
BindingSig,
|
|
||||||
/// A signature is valid for more than one input. This should never happen if `alpha`
|
|
||||||
/// is sampled correctly, and indicates a critical failure in randomness generation.
|
|
||||||
DuplicateSignature,
|
|
||||||
InvalidAddress,
|
|
||||||
InvalidAmount,
|
|
||||||
/// External signature is not valid.
|
|
||||||
InvalidExternalSignature,
|
|
||||||
/// A bundle could not be built because required signatures were missing.
|
|
||||||
MissingSignatures,
|
|
||||||
SpendProof,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::AnchorMismatch => {
|
|
||||||
write!(f, "Anchor mismatch (anchors for all spends must be equal)")
|
|
||||||
}
|
|
||||||
Error::BindingSig => write!(f, "Failed to create bindingSig"),
|
|
||||||
Error::DuplicateSignature => write!(f, "Signature valid for more than one input"),
|
|
||||||
Error::InvalidAddress => write!(f, "Invalid address"),
|
|
||||||
Error::InvalidAmount => write!(f, "Invalid amount"),
|
|
||||||
Error::InvalidExternalSignature => write!(f, "External signature was invalid"),
|
|
||||||
Error::MissingSignatures => write!(f, "Required signatures were missing during build"),
|
|
||||||
Error::SpendProof => write!(f, "Failed to create Sapling spend proof"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SpendDescriptionInfo {
|
|
||||||
proof_generation_key: ProofGenerationKey,
|
|
||||||
note: Note,
|
|
||||||
alpha: jubjub::Fr,
|
|
||||||
merkle_path: MerklePath,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpendDescriptionInfo {
|
|
||||||
fn new_internal<R: RngCore>(
|
|
||||||
mut rng: &mut R,
|
|
||||||
extsk: &ExtendedSpendingKey,
|
|
||||||
note: Note,
|
|
||||||
merkle_path: MerklePath,
|
|
||||||
) -> Self {
|
|
||||||
SpendDescriptionInfo {
|
|
||||||
proof_generation_key: extsk.expsk.proof_generation_key(),
|
|
||||||
note,
|
|
||||||
alpha: jubjub::Fr::random(&mut rng),
|
|
||||||
merkle_path,
|
|
||||||
rcv: ValueCommitTrapdoor::random(rng),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(&self) -> NoteValue {
|
|
||||||
self.note.value()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build<Pr: SpendProver>(
|
|
||||||
self,
|
|
||||||
anchor: Option<bls12_381::Scalar>,
|
|
||||||
) -> Result<SpendDescription<InProgress<Unproven, Unsigned>>, Error> {
|
|
||||||
let anchor = anchor.expect("Sapling anchor must be set if Sapling spends are present.");
|
|
||||||
|
|
||||||
// Construct the value commitment.
|
|
||||||
let cv = ValueCommitment::derive(self.note.value(), self.rcv.clone());
|
|
||||||
|
|
||||||
let ak = self.proof_generation_key.ak.clone();
|
|
||||||
|
|
||||||
// This is the result of the re-randomization, we compute it for the caller
|
|
||||||
let rk = ak.randomize(&self.alpha);
|
|
||||||
|
|
||||||
let nullifier = self.note.nf(
|
|
||||||
&self.proof_generation_key.to_viewing_key().nk,
|
|
||||||
u64::try_from(self.merkle_path.position())
|
|
||||||
.expect("Sapling note commitment tree position must fit into a u64"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let zkproof = Pr::prepare_circuit(
|
|
||||||
self.proof_generation_key,
|
|
||||||
*self.note.recipient().diversifier(),
|
|
||||||
*self.note.rseed(),
|
|
||||||
self.note.value(),
|
|
||||||
self.alpha,
|
|
||||||
self.rcv,
|
|
||||||
anchor,
|
|
||||||
self.merkle_path.clone(),
|
|
||||||
)
|
|
||||||
.ok_or(Error::SpendProof)?;
|
|
||||||
|
|
||||||
Ok(SpendDescription::from_parts(
|
|
||||||
cv,
|
|
||||||
anchor,
|
|
||||||
nullifier,
|
|
||||||
rk,
|
|
||||||
zkproof,
|
|
||||||
SigningParts {
|
|
||||||
ak,
|
|
||||||
alpha: self.alpha,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A struct containing the information required in order to construct a
|
|
||||||
/// Sapling output to a transaction.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SaplingOutputInfo {
|
|
||||||
/// `None` represents the `ovk = ⊥` case.
|
|
||||||
ovk: Option<OutgoingViewingKey>,
|
|
||||||
note: Note,
|
|
||||||
memo: Option<[u8; 512]>,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SaplingOutputInfo {
|
|
||||||
fn dummy<R: RngCore>(mut rng: &mut R, zip212_enforcement: Zip212Enforcement) -> Self {
|
|
||||||
// This is a dummy output
|
|
||||||
let dummy_to = {
|
|
||||||
let mut diversifier = Diversifier([0; 11]);
|
|
||||||
loop {
|
|
||||||
rng.fill_bytes(&mut diversifier.0);
|
|
||||||
let dummy_ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
|
|
||||||
if let Some(addr) = dummy_ivk.to_payment_address(diversifier) {
|
|
||||||
break addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::new_internal(
|
|
||||||
rng,
|
|
||||||
None,
|
|
||||||
dummy_to,
|
|
||||||
NoteValue::from_raw(0),
|
|
||||||
None,
|
|
||||||
zip212_enforcement,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_internal<R: RngCore>(
|
|
||||||
rng: &mut R,
|
|
||||||
ovk: Option<OutgoingViewingKey>,
|
|
||||||
to: PaymentAddress,
|
|
||||||
value: NoteValue,
|
|
||||||
memo: Option<[u8; 512]>,
|
|
||||||
zip212_enforcement: Zip212Enforcement,
|
|
||||||
) -> Self {
|
|
||||||
let rseed = generate_random_rseed_internal(zip212_enforcement, rng);
|
|
||||||
|
|
||||||
let note = Note::from_parts(to, value, rseed);
|
|
||||||
|
|
||||||
SaplingOutputInfo {
|
|
||||||
ovk,
|
|
||||||
note,
|
|
||||||
memo,
|
|
||||||
rcv: ValueCommitTrapdoor::random(rng),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build<Pr: OutputProver, R: RngCore>(
|
|
||||||
self,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> OutputDescription<sapling::circuit::Output> {
|
|
||||||
let encryptor = sapling_note_encryption::<R>(
|
|
||||||
self.ovk,
|
|
||||||
self.note.clone(),
|
|
||||||
self.memo.unwrap_or_else(|| {
|
|
||||||
let mut memo = [0; 512];
|
|
||||||
memo[0] = 0xf6;
|
|
||||||
memo
|
|
||||||
}),
|
|
||||||
rng,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Construct the value commitment.
|
|
||||||
let cv = ValueCommitment::derive(self.note.value(), self.rcv.clone());
|
|
||||||
|
|
||||||
// Prepare the circuit that will be used to construct the proof.
|
|
||||||
let zkproof = Pr::prepare_circuit(
|
|
||||||
encryptor.esk().0,
|
|
||||||
self.note.recipient(),
|
|
||||||
self.note.rcm(),
|
|
||||||
self.note.value(),
|
|
||||||
self.rcv,
|
|
||||||
);
|
|
||||||
|
|
||||||
let cmu = self.note.cmu();
|
|
||||||
|
|
||||||
let enc_ciphertext = encryptor.encrypt_note_plaintext();
|
|
||||||
let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu, rng);
|
|
||||||
|
|
||||||
let epk = encryptor.epk();
|
|
||||||
|
|
||||||
OutputDescription::from_parts(
|
|
||||||
cv,
|
|
||||||
cmu,
|
|
||||||
epk.to_bytes(),
|
|
||||||
enc_ciphertext,
|
|
||||||
out_ciphertext,
|
|
||||||
zkproof,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recipient(&self) -> PaymentAddress {
|
|
||||||
self.note.recipient()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(&self) -> NoteValue {
|
|
||||||
self.note.value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata about a transaction created by a [`SaplingBuilder`].
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct SaplingMetadata {
|
|
||||||
spend_indices: Vec<usize>,
|
|
||||||
output_indices: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SaplingMetadata {
|
|
||||||
pub fn empty() -> Self {
|
|
||||||
SaplingMetadata {
|
|
||||||
spend_indices: vec![],
|
|
||||||
output_indices: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the index within the transaction of the [`SpendDescription`] corresponding
|
|
||||||
/// to the `n`-th call to [`SaplingBuilder::add_spend`].
|
|
||||||
///
|
|
||||||
/// Note positions are randomized when building transactions for indistinguishability.
|
|
||||||
/// This means that the transaction consumer cannot assume that e.g. the first spend
|
|
||||||
/// they added (via the first call to [`SaplingBuilder::add_spend`]) is the first
|
|
||||||
/// [`SpendDescription`] in the transaction.
|
|
||||||
pub fn spend_index(&self, n: usize) -> Option<usize> {
|
|
||||||
self.spend_indices.get(n).copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the index within the transaction of the [`OutputDescription`] corresponding
|
|
||||||
/// to the `n`-th call to [`SaplingBuilder::add_output`].
|
|
||||||
///
|
|
||||||
/// Note positions are randomized when building transactions for indistinguishability.
|
|
||||||
/// This means that the transaction consumer cannot assume that e.g. the first output
|
|
||||||
/// they added (via the first call to [`SaplingBuilder::add_output`]) is the first
|
|
||||||
/// [`OutputDescription`] in the transaction.
|
|
||||||
pub fn output_index(&self, n: usize) -> Option<usize> {
|
|
||||||
self.output_indices.get(n).copied()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SaplingBuilder {
|
|
||||||
anchor: Option<bls12_381::Scalar>,
|
|
||||||
value_balance: ValueSum,
|
|
||||||
spends: Vec<SpendDescriptionInfo>,
|
|
||||||
outputs: Vec<SaplingOutputInfo>,
|
|
||||||
zip212_enforcement: Zip212Enforcement,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SaplingBuilder {
|
|
||||||
pub fn new(zip212_enforcement: Zip212Enforcement) -> Self {
|
|
||||||
SaplingBuilder {
|
|
||||||
anchor: None,
|
|
||||||
value_balance: ValueSum::zero(),
|
|
||||||
spends: vec![],
|
|
||||||
outputs: vec![],
|
|
||||||
zip212_enforcement,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the list of Sapling inputs that will be consumed by the transaction being
|
|
||||||
/// constructed.
|
|
||||||
pub fn inputs(&self) -> &[SpendDescriptionInfo] {
|
|
||||||
&self.spends
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the Sapling outputs that will be produced by the transaction being constructed
|
|
||||||
pub fn outputs(&self) -> &[SaplingOutputInfo] {
|
|
||||||
&self.outputs
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of outputs that will be present in the Sapling bundle built by
|
|
||||||
/// this builder.
|
|
||||||
///
|
|
||||||
/// This may be larger than the number of outputs that have been added to the builder,
|
|
||||||
/// depending on whether padding is going to be applied.
|
|
||||||
pub(crate) fn bundle_output_count(&self) -> usize {
|
|
||||||
// This matches the padding behaviour in `Self::build`.
|
|
||||||
match self.spends.len() {
|
|
||||||
0 => self.outputs.len(),
|
|
||||||
_ => std::cmp::max(MIN_SHIELDED_OUTPUTS, self.outputs.len()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the net value represented by the spends and outputs added to this builder,
|
|
||||||
/// or an error if the values added to this builder overflow the range of a Zcash
|
|
||||||
/// monetary amount.
|
|
||||||
fn try_value_balance<V: TryFrom<i64>>(&self) -> Result<V, Error> {
|
|
||||||
self.value_balance
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ())
|
|
||||||
.and_then(|vb| V::try_from(vb).map_err(|_| ()))
|
|
||||||
.map_err(|()| Error::InvalidAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the net value represented by the spends and outputs added to this builder.
|
|
||||||
pub fn value_balance<V: TryFrom<i64>>(&self) -> V {
|
|
||||||
self.try_value_balance()
|
|
||||||
.expect("we check this when mutating self.value_balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a Sapling note to be spent in this transaction.
|
|
||||||
///
|
|
||||||
/// Returns an error if the given Merkle path does not have the same anchor as the
|
|
||||||
/// paths for previous Sapling notes.
|
|
||||||
pub fn add_spend<R: RngCore>(
|
|
||||||
&mut self,
|
|
||||||
mut rng: R,
|
|
||||||
extsk: &ExtendedSpendingKey,
|
|
||||||
note: Note,
|
|
||||||
merkle_path: MerklePath,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// Consistency check: all anchors must equal the first one
|
|
||||||
let node = Node::from_cmu(¬e.cmu());
|
|
||||||
if let Some(anchor) = self.anchor {
|
|
||||||
let path_root: bls12_381::Scalar = merkle_path.root(node).into();
|
|
||||||
if path_root != anchor {
|
|
||||||
return Err(Error::AnchorMismatch);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.anchor = Some(merkle_path.root(node).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
self.value_balance = (self.value_balance + note.value()).ok_or(Error::InvalidAmount)?;
|
|
||||||
self.try_value_balance::<i64>()?;
|
|
||||||
|
|
||||||
let spend = SpendDescriptionInfo::new_internal(&mut rng, extsk, note, merkle_path);
|
|
||||||
|
|
||||||
self.spends.push(spend);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a Sapling address to send funds to.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn add_output<R: RngCore>(
|
|
||||||
&mut self,
|
|
||||||
mut rng: R,
|
|
||||||
ovk: Option<OutgoingViewingKey>,
|
|
||||||
to: PaymentAddress,
|
|
||||||
value: NoteValue,
|
|
||||||
memo: Option<[u8; 512]>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let output = SaplingOutputInfo::new_internal(
|
|
||||||
&mut rng,
|
|
||||||
ovk,
|
|
||||||
to,
|
|
||||||
value,
|
|
||||||
memo,
|
|
||||||
self.zip212_enforcement,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.value_balance = (self.value_balance - value).ok_or(Error::InvalidAddress)?;
|
|
||||||
self.try_value_balance::<i64>()?;
|
|
||||||
|
|
||||||
self.outputs.push(output);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
|
||||||
self,
|
|
||||||
mut rng: R,
|
|
||||||
) -> Result<Option<(UnauthorizedBundle<V>, SaplingMetadata)>, Error> {
|
|
||||||
let value_balance = self.try_value_balance()?;
|
|
||||||
|
|
||||||
// Record initial positions of spends and outputs
|
|
||||||
let mut indexed_spends: Vec<_> = self.spends.into_iter().enumerate().collect();
|
|
||||||
let mut indexed_outputs: Vec<_> = self
|
|
||||||
.outputs
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, o)| Some((i, o)))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Set up the transaction metadata that will be used to record how
|
|
||||||
// inputs and outputs are shuffled.
|
|
||||||
let mut tx_metadata = SaplingMetadata::empty();
|
|
||||||
tx_metadata.spend_indices.resize(indexed_spends.len(), 0);
|
|
||||||
tx_metadata.output_indices.resize(indexed_outputs.len(), 0);
|
|
||||||
|
|
||||||
// Pad Sapling outputs
|
|
||||||
if !indexed_spends.is_empty() {
|
|
||||||
while indexed_outputs.len() < MIN_SHIELDED_OUTPUTS {
|
|
||||||
indexed_outputs.push(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomize order of inputs and outputs
|
|
||||||
indexed_spends.shuffle(&mut rng);
|
|
||||||
indexed_outputs.shuffle(&mut rng);
|
|
||||||
|
|
||||||
// Record the transaction metadata and create dummy outputs.
|
|
||||||
let spend_infos = indexed_spends
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, (pos, spend))| {
|
|
||||||
// Record the post-randomized spend location
|
|
||||||
tx_metadata.spend_indices[pos] = i;
|
|
||||||
|
|
||||||
spend
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let output_infos = indexed_outputs
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, output)| {
|
|
||||||
if let Some((pos, output)) = output {
|
|
||||||
// Record the post-randomized output location
|
|
||||||
tx_metadata.output_indices[pos] = i;
|
|
||||||
|
|
||||||
output
|
|
||||||
} else {
|
|
||||||
// This is a dummy output
|
|
||||||
SaplingOutputInfo::dummy(&mut rng, self.zip212_enforcement)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Compute the transaction binding signing key.
|
|
||||||
let bsk = {
|
|
||||||
let spends: TrapdoorSum = spend_infos.iter().map(|spend| &spend.rcv).sum();
|
|
||||||
let outputs: TrapdoorSum = output_infos.iter().map(|output| &output.rcv).sum();
|
|
||||||
(spends - outputs).into_bsk()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the unauthorized Spend and Output descriptions.
|
|
||||||
let shielded_spends = spend_infos
|
|
||||||
.into_iter()
|
|
||||||
.map(|a| a.build::<SP>(self.anchor))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
let shielded_outputs = output_infos
|
|
||||||
.into_iter()
|
|
||||||
.map(|a| a.build::<OP, _>(&mut rng))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Verify that bsk and bvk are consistent.
|
|
||||||
let bvk = {
|
|
||||||
let spends = shielded_spends
|
|
||||||
.iter()
|
|
||||||
.map(|spend| spend.cv())
|
|
||||||
.sum::<CommitmentSum>();
|
|
||||||
let outputs = shielded_outputs
|
|
||||||
.iter()
|
|
||||||
.map(|output| output.cv())
|
|
||||||
.sum::<CommitmentSum>();
|
|
||||||
(spends - outputs)
|
|
||||||
.into_bvk(i64::try_from(self.value_balance).map_err(|_| Error::InvalidAmount)?)
|
|
||||||
};
|
|
||||||
assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk);
|
|
||||||
|
|
||||||
let bundle = if shielded_spends.is_empty() && shielded_outputs.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((
|
|
||||||
Bundle::from_parts(
|
|
||||||
shielded_spends,
|
|
||||||
shielded_outputs,
|
|
||||||
value_balance,
|
|
||||||
InProgress {
|
|
||||||
sigs: Unsigned { bsk },
|
|
||||||
_proof_state: PhantomData::default(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
tx_metadata,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(bundle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type alias for an in-progress bundle that has no proofs or signatures.
|
|
||||||
///
|
|
||||||
/// This is returned by [`SaplingBuilder::build`].
|
|
||||||
pub type UnauthorizedBundle<V> = Bundle<InProgress<Unproven, Unsigned>, V>;
|
|
||||||
|
|
||||||
/// Marker trait representing bundle proofs in the process of being created.
|
|
||||||
pub trait InProgressProofs: fmt::Debug {
|
|
||||||
/// The proof type of a Sapling spend in the process of being proven.
|
|
||||||
type SpendProof: Clone + fmt::Debug;
|
|
||||||
/// The proof type of a Sapling output in the process of being proven.
|
|
||||||
type OutputProof: Clone + fmt::Debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker trait representing bundle signatures in the process of being created.
|
|
||||||
pub trait InProgressSignatures: fmt::Debug {
|
|
||||||
/// The authorization type of a Sapling spend or output in the process of being
|
|
||||||
/// authorized.
|
|
||||||
type AuthSig: Clone + fmt::Debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker for a bundle in the process of being built.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct InProgress<P: InProgressProofs, S: InProgressSignatures> {
|
|
||||||
sigs: S,
|
|
||||||
_proof_state: PhantomData<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: InProgressProofs, S: InProgressSignatures> Authorization for InProgress<P, S> {
|
|
||||||
type SpendProof = P::SpendProof;
|
|
||||||
type OutputProof = P::OutputProof;
|
|
||||||
type AuthSig = S::AuthSig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker for a [`Bundle`] without proofs.
|
|
||||||
///
|
|
||||||
/// The [`SpendDescription`]s and [`OutputDescription`]s within the bundle contain the
|
|
||||||
/// private data needed to create proofs.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Unproven;
|
|
||||||
|
|
||||||
impl InProgressProofs for Unproven {
|
|
||||||
type SpendProof = sapling::circuit::Spend;
|
|
||||||
type OutputProof = sapling::circuit::Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker for a [`Bundle`] with proofs.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Proven;
|
|
||||||
|
|
||||||
impl InProgressProofs for Proven {
|
|
||||||
type SpendProof = GrothProofBytes;
|
|
||||||
type OutputProof = GrothProofBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reports on the progress made towards creating proofs for a bundle.
|
|
||||||
pub trait ProverProgress {
|
|
||||||
/// Updates the progress instance with the number of steps completed and the total
|
|
||||||
/// number of steps.
|
|
||||||
fn update(&mut self, cur: u32, end: u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProverProgress for () {
|
|
||||||
fn update(&mut self, _: u32, _: u32) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U: From<(u32, u32)>> ProverProgress for std::sync::mpsc::Sender<U> {
|
|
||||||
fn update(&mut self, cur: u32, end: u32) {
|
|
||||||
// If the send fails, we should ignore the error, not crash.
|
|
||||||
self.send(U::from((cur, end))).unwrap_or(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U: ProverProgress> ProverProgress for &mut U {
|
|
||||||
fn update(&mut self, cur: u32, end: u32) {
|
|
||||||
(*self).update(cur, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CreateProofs<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress> {
|
|
||||||
spend_prover: &'a SP,
|
|
||||||
output_prover: &'a OP,
|
|
||||||
rng: R,
|
|
||||||
progress_notifier: U,
|
|
||||||
total_progress: u32,
|
|
||||||
progress: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress>
|
|
||||||
CreateProofs<'a, SP, OP, R, U>
|
|
||||||
{
|
|
||||||
fn new(
|
|
||||||
spend_prover: &'a SP,
|
|
||||||
output_prover: &'a OP,
|
|
||||||
rng: R,
|
|
||||||
progress_notifier: U,
|
|
||||||
total_progress: u32,
|
|
||||||
) -> Self {
|
|
||||||
// Keep track of the total number of steps computed
|
|
||||||
Self {
|
|
||||||
spend_prover,
|
|
||||||
output_prover,
|
|
||||||
rng,
|
|
||||||
progress_notifier,
|
|
||||||
total_progress,
|
|
||||||
progress: 0u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_progress(&mut self) {
|
|
||||||
// Update progress and send a notification on the channel
|
|
||||||
self.progress += 1;
|
|
||||||
self.progress_notifier
|
|
||||||
.update(self.progress, self.total_progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
'a,
|
|
||||||
S: InProgressSignatures,
|
|
||||||
SP: SpendProver,
|
|
||||||
OP: OutputProver,
|
|
||||||
R: RngCore,
|
|
||||||
U: ProverProgress,
|
|
||||||
> MapAuth<InProgress<Unproven, S>, InProgress<Proven, S>> for CreateProofs<'a, SP, OP, R, U>
|
|
||||||
{
|
|
||||||
fn map_spend_proof(&mut self, spend: sapling::circuit::Spend) -> GrothProofBytes {
|
|
||||||
let proof = self.spend_prover.create_proof(spend, &mut self.rng);
|
|
||||||
self.update_progress();
|
|
||||||
SP::encode_proof(proof)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_output_proof(&mut self, output: sapling::circuit::Output) -> GrothProofBytes {
|
|
||||||
let proof = self.output_prover.create_proof(output, &mut self.rng);
|
|
||||||
self.update_progress();
|
|
||||||
OP::encode_proof(proof)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_auth_sig(&mut self, s: S::AuthSig) -> S::AuthSig {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&mut self, a: InProgress<Unproven, S>) -> InProgress<Proven, S> {
|
|
||||||
InProgress {
|
|
||||||
sigs: a.sigs,
|
|
||||||
_proof_state: PhantomData::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
|
|
||||||
/// Creates the proofs for this bundle.
|
|
||||||
pub fn create_proofs<SP: SpendProver, OP: OutputProver>(
|
|
||||||
self,
|
|
||||||
spend_prover: &SP,
|
|
||||||
output_prover: &OP,
|
|
||||||
rng: impl RngCore,
|
|
||||||
progress_notifier: impl ProverProgress,
|
|
||||||
) -> Bundle<InProgress<Proven, S>, V> {
|
|
||||||
let total_progress =
|
|
||||||
self.shielded_spends().len() as u32 + self.shielded_outputs().len() as u32;
|
|
||||||
self.map_authorization(CreateProofs::new(
|
|
||||||
spend_prover,
|
|
||||||
output_prover,
|
|
||||||
rng,
|
|
||||||
progress_notifier,
|
|
||||||
total_progress,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker for an unauthorized bundle with no signatures.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Unsigned {
|
|
||||||
bsk: redjubjub::SigningKey<Binding>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Unsigned {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("Unsigned").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InProgressSignatures for Unsigned {
|
|
||||||
type AuthSig = SigningParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The parts needed to sign a [`SpendDescription`].
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SigningParts {
|
|
||||||
/// The spend validating key for this spend description. Used to match spend
|
|
||||||
/// authorizing keys to spend descriptions they can create signatures for.
|
|
||||||
ak: SpendValidatingKey,
|
|
||||||
/// The randomization needed to derive the actual signing key for this note.
|
|
||||||
alpha: jubjub::Scalar,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marker for a partially-authorized bundle, in the process of being signed.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PartiallyAuthorized {
|
|
||||||
binding_signature: redjubjub::Signature<Binding>,
|
|
||||||
sighash: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InProgressSignatures for PartiallyAuthorized {
|
|
||||||
type AuthSig = MaybeSigned;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A heisen[`Signature`] for a particular [`SpendDescription`].
|
|
||||||
///
|
|
||||||
/// [`Signature`]: redjubjub::Signature
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum MaybeSigned {
|
|
||||||
/// The information needed to sign this [`SpendDescription`].
|
|
||||||
SigningMetadata(SigningParts),
|
|
||||||
/// The signature for this [`SpendDescription`].
|
|
||||||
Signature(redjubjub::Signature<SpendAuth>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaybeSigned {
|
|
||||||
fn finalize(self) -> Result<redjubjub::Signature<SpendAuth>, Error> {
|
|
||||||
match self {
|
|
||||||
Self::Signature(sig) => Ok(sig),
|
|
||||||
_ => Err(Error::MissingSignatures),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: InProgressProofs, V> Bundle<InProgress<P, Unsigned>, V> {
|
|
||||||
/// Loads the sighash into this bundle, preparing it for signing.
|
|
||||||
///
|
|
||||||
/// This API ensures that all signatures are created over the same sighash.
|
|
||||||
pub fn prepare<R: RngCore + CryptoRng>(
|
|
||||||
self,
|
|
||||||
mut rng: R,
|
|
||||||
sighash: [u8; 32],
|
|
||||||
) -> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
|
||||||
self.map_authorization((
|
|
||||||
|proof| proof,
|
|
||||||
|proof| proof,
|
|
||||||
MaybeSigned::SigningMetadata,
|
|
||||||
|auth: InProgress<P, Unsigned>| InProgress {
|
|
||||||
sigs: PartiallyAuthorized {
|
|
||||||
binding_signature: auth.sigs.bsk.sign(&mut rng, &sighash),
|
|
||||||
sighash,
|
|
||||||
},
|
|
||||||
_proof_state: PhantomData::default(),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Bundle<InProgress<Proven, Unsigned>, V> {
|
|
||||||
/// Applies signatures to this bundle, in order to authorize it.
|
|
||||||
///
|
|
||||||
/// This is a helper method that wraps [`Bundle::prepare`], [`Bundle::sign`], and
|
|
||||||
/// [`Bundle::finalize`].
|
|
||||||
pub fn apply_signatures<R: RngCore + CryptoRng>(
|
|
||||||
self,
|
|
||||||
mut rng: R,
|
|
||||||
sighash: [u8; 32],
|
|
||||||
signing_keys: &[SpendAuthorizingKey],
|
|
||||||
) -> Result<Bundle<Authorized, V>, Error> {
|
|
||||||
signing_keys
|
|
||||||
.iter()
|
|
||||||
.fold(self.prepare(&mut rng, sighash), |partial, ask| {
|
|
||||||
partial.sign(&mut rng, ask)
|
|
||||||
})
|
|
||||||
.finalize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
|
||||||
/// Signs this bundle with the given [`redjubjub::SigningKey`].
|
|
||||||
///
|
|
||||||
/// This will apply signatures for all notes controlled by this spending key.
|
|
||||||
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &SpendAuthorizingKey) -> Self {
|
|
||||||
let expected_ak = ask.into();
|
|
||||||
let sighash = self.authorization().sigs.sighash;
|
|
||||||
self.map_authorization((
|
|
||||||
|proof| proof,
|
|
||||||
|proof| proof,
|
|
||||||
|maybe| match maybe {
|
|
||||||
MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => {
|
|
||||||
MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(&mut rng, &sighash))
|
|
||||||
}
|
|
||||||
s => s,
|
|
||||||
},
|
|
||||||
|partial| partial,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends externally computed [`redjubjub::Signature`]s.
|
|
||||||
///
|
|
||||||
/// Each signature will be applied to the one input for which it is valid. An error
|
|
||||||
/// will be returned if the signature is not valid for any inputs, or if it is valid
|
|
||||||
/// for more than one input.
|
|
||||||
pub fn append_signatures(
|
|
||||||
self,
|
|
||||||
signatures: &[redjubjub::Signature<SpendAuth>],
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
signatures.iter().try_fold(self, Self::append_signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_signature(self, signature: &redjubjub::Signature<SpendAuth>) -> Result<Self, Error> {
|
|
||||||
let sighash = self.authorization().sigs.sighash;
|
|
||||||
let mut signature_valid_for = 0usize;
|
|
||||||
let bundle = self.map_authorization((
|
|
||||||
|proof| proof,
|
|
||||||
|proof| proof,
|
|
||||||
|maybe| match maybe {
|
|
||||||
MaybeSigned::SigningMetadata(parts) => {
|
|
||||||
let rk = parts.ak.randomize(&parts.alpha);
|
|
||||||
if rk.verify(&sighash, signature).is_ok() {
|
|
||||||
signature_valid_for += 1;
|
|
||||||
MaybeSigned::Signature(*signature)
|
|
||||||
} else {
|
|
||||||
// Signature isn't for this input.
|
|
||||||
MaybeSigned::SigningMetadata(parts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s => s,
|
|
||||||
},
|
|
||||||
|partial| partial,
|
|
||||||
));
|
|
||||||
match signature_valid_for {
|
|
||||||
0 => Err(Error::InvalidExternalSignature),
|
|
||||||
1 => Ok(bundle),
|
|
||||||
_ => Err(Error::DuplicateSignature),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Bundle<InProgress<Proven, PartiallyAuthorized>, V> {
|
|
||||||
/// Finalizes this bundle, enabling it to be included in a transaction.
|
|
||||||
///
|
|
||||||
/// Returns an error if any signatures are missing.
|
|
||||||
pub fn finalize(self) -> Result<Bundle<Authorized, V>, Error> {
|
|
||||||
self.try_map_authorization((
|
|
||||||
Ok,
|
|
||||||
Ok,
|
|
||||||
|maybe: MaybeSigned| maybe.finalize(),
|
|
||||||
|partial: InProgress<Proven, PartiallyAuthorized>| {
|
|
||||||
Ok(Authorized {
|
|
||||||
binding_sig: partial.sigs.binding_signature,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub mod testing {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use proptest::collection::vec;
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use rand::{rngs::StdRng, SeedableRng};
|
|
||||||
|
|
||||||
use crate::sapling::{
|
|
||||||
bundle::{Authorized, Bundle},
|
|
||||||
note_encryption::Zip212Enforcement,
|
|
||||||
prover::mock::{MockOutputProver, MockSpendProver},
|
|
||||||
testing::{arb_node, arb_note},
|
|
||||||
value::testing::arb_positive_note_value,
|
|
||||||
zip32::testing::arb_extended_spending_key,
|
|
||||||
};
|
|
||||||
use incrementalmerkletree::{
|
|
||||||
frontier::testing::arb_commitment_tree, witness::IncrementalWitness,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::SaplingBuilder;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn arb_bundle<V: fmt::Debug + From<i64>>(
|
|
||||||
max_money: u64,
|
|
||||||
zip212_enforcement: Zip212Enforcement,
|
|
||||||
) -> impl Strategy<Value = Bundle<Authorized, V>> {
|
|
||||||
(1..30usize)
|
|
||||||
.prop_flat_map(move |n_notes| {
|
|
||||||
(
|
|
||||||
arb_extended_spending_key(),
|
|
||||||
vec(
|
|
||||||
arb_positive_note_value(max_money / 10000).prop_flat_map(arb_note),
|
|
||||||
n_notes,
|
|
||||||
),
|
|
||||||
vec(
|
|
||||||
arb_commitment_tree::<_, _, 32>(n_notes, arb_node())
|
|
||||||
.prop_map(|t| IncrementalWitness::from_tree(t).path().unwrap()),
|
|
||||||
n_notes,
|
|
||||||
),
|
|
||||||
prop::array::uniform32(any::<u8>()),
|
|
||||||
prop::array::uniform32(any::<u8>()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.prop_map(
|
|
||||||
move |(extsk, spendable_notes, commitment_trees, rng_seed, fake_sighash_bytes)| {
|
|
||||||
let mut builder = SaplingBuilder::new(zip212_enforcement);
|
|
||||||
let mut rng = StdRng::from_seed(rng_seed);
|
|
||||||
|
|
||||||
for (note, path) in spendable_notes
|
|
||||||
.into_iter()
|
|
||||||
.zip(commitment_trees.into_iter())
|
|
||||||
{
|
|
||||||
builder.add_spend(&mut rng, &extsk, note, path).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let (bundle, _) = builder
|
|
||||||
.build::<MockSpendProver, MockOutputProver, _, _>(&mut rng)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let bundle =
|
|
||||||
bundle.create_proofs(&MockSpendProver, &MockOutputProver, &mut rng, ());
|
|
||||||
|
|
||||||
bundle
|
|
||||||
.apply_signatures(&mut rng, fake_sighash_bytes, &[extsk.expsk.ask])
|
|
||||||
.unwrap()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,747 +0,0 @@
|
||||||
use core::fmt::Debug;
|
|
||||||
|
|
||||||
use memuse::DynamicUsage;
|
|
||||||
use redjubjub::{Binding, SpendAuth};
|
|
||||||
|
|
||||||
use zcash_note_encryption::{
|
|
||||||
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::sapling::{
|
|
||||||
circuit::GROTH_PROOF_SIZE,
|
|
||||||
note::ExtractedNoteCommitment,
|
|
||||||
note_encryption::{CompactOutputDescription, SaplingDomain},
|
|
||||||
value::ValueCommitment,
|
|
||||||
Nullifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type GrothProofBytes = [u8; GROTH_PROOF_SIZE];
|
|
||||||
|
|
||||||
/// Defines the authorization type of a Sapling bundle.
|
|
||||||
pub trait Authorization: Debug {
|
|
||||||
type SpendProof: Clone + Debug;
|
|
||||||
type OutputProof: Clone + Debug;
|
|
||||||
type AuthSig: Clone + Debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Authorizing data for a bundle of Sapling spends and outputs, ready to be committed to
|
|
||||||
/// the ledger.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Authorized {
|
|
||||||
// TODO: Make this private.
|
|
||||||
pub binding_sig: redjubjub::Signature<Binding>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Authorization for Authorized {
|
|
||||||
type SpendProof = GrothProofBytes;
|
|
||||||
type OutputProof = GrothProofBytes;
|
|
||||||
type AuthSig = redjubjub::Signature<SpendAuth>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A map from one bundle authorization to another.
|
|
||||||
///
|
|
||||||
/// For use with [`Bundle::map_authorization`].
|
|
||||||
pub trait MapAuth<A: Authorization, B: Authorization> {
|
|
||||||
fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof;
|
|
||||||
fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof;
|
|
||||||
fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig;
|
|
||||||
fn map_authorization(&mut self, a: A) -> B;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The identity map.
|
|
||||||
///
|
|
||||||
/// This can be used with [`TransactionData::map_authorization`] when you want to map the
|
|
||||||
/// authorization of a subset of the transaction's bundles.
|
|
||||||
///
|
|
||||||
/// [`TransactionData::map_authorization`]: crate::transaction::TransactionData::map_authorization
|
|
||||||
impl MapAuth<Authorized, Authorized> for () {
|
|
||||||
fn map_spend_proof(
|
|
||||||
&mut self,
|
|
||||||
p: <Authorized as Authorization>::SpendProof,
|
|
||||||
) -> <Authorized as Authorization>::SpendProof {
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_output_proof(
|
|
||||||
&mut self,
|
|
||||||
p: <Authorized as Authorization>::OutputProof,
|
|
||||||
) -> <Authorized as Authorization>::OutputProof {
|
|
||||||
p
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_auth_sig(
|
|
||||||
&mut self,
|
|
||||||
s: <Authorized as Authorization>::AuthSig,
|
|
||||||
) -> <Authorized as Authorization>::AuthSig {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&mut self, a: Authorized) -> Authorized {
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper for implementing `MapAuth` with a set of closures.
|
|
||||||
impl<A, B, F, G, H, I> MapAuth<A, B> for (F, G, H, I)
|
|
||||||
where
|
|
||||||
A: Authorization,
|
|
||||||
B: Authorization,
|
|
||||||
F: FnMut(A::SpendProof) -> B::SpendProof,
|
|
||||||
G: FnMut(A::OutputProof) -> B::OutputProof,
|
|
||||||
H: FnMut(A::AuthSig) -> B::AuthSig,
|
|
||||||
I: FnMut(A) -> B,
|
|
||||||
{
|
|
||||||
fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof {
|
|
||||||
self.0(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof {
|
|
||||||
self.1(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig {
|
|
||||||
self.2(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_authorization(&mut self, a: A) -> B {
|
|
||||||
self.3(a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A fallible map from one bundle authorization to another.
|
|
||||||
///
|
|
||||||
/// For use with [`Bundle::try_map_authorization`].
|
|
||||||
pub trait TryMapAuth<A: Authorization, B: Authorization> {
|
|
||||||
type Error;
|
|
||||||
fn try_map_spend_proof(&mut self, p: A::SpendProof) -> Result<B::SpendProof, Self::Error>;
|
|
||||||
fn try_map_output_proof(&mut self, p: A::OutputProof) -> Result<B::OutputProof, Self::Error>;
|
|
||||||
fn try_map_auth_sig(&mut self, s: A::AuthSig) -> Result<B::AuthSig, Self::Error>;
|
|
||||||
fn try_map_authorization(&mut self, a: A) -> Result<B, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper for implementing `TryMapAuth` with a set of closures.
|
|
||||||
impl<A, B, E, F, G, H, I> TryMapAuth<A, B> for (F, G, H, I)
|
|
||||||
where
|
|
||||||
A: Authorization,
|
|
||||||
B: Authorization,
|
|
||||||
F: FnMut(A::SpendProof) -> Result<B::SpendProof, E>,
|
|
||||||
G: FnMut(A::OutputProof) -> Result<B::OutputProof, E>,
|
|
||||||
H: FnMut(A::AuthSig) -> Result<B::AuthSig, E>,
|
|
||||||
I: FnMut(A) -> Result<B, E>,
|
|
||||||
{
|
|
||||||
type Error = E;
|
|
||||||
|
|
||||||
fn try_map_spend_proof(&mut self, p: A::SpendProof) -> Result<B::SpendProof, Self::Error> {
|
|
||||||
self.0(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_map_output_proof(&mut self, p: A::OutputProof) -> Result<B::OutputProof, Self::Error> {
|
|
||||||
self.1(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_map_auth_sig(&mut self, s: A::AuthSig) -> Result<B::AuthSig, Self::Error> {
|
|
||||||
self.2(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_map_authorization(&mut self, a: A) -> Result<B, Self::Error> {
|
|
||||||
self.3(a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Bundle<A: Authorization, V> {
|
|
||||||
shielded_spends: Vec<SpendDescription<A>>,
|
|
||||||
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
|
|
||||||
value_balance: V,
|
|
||||||
authorization: A,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Authorization, V> Bundle<A, V> {
|
|
||||||
/// Constructs a `Bundle` from its constituent parts.
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
|
||||||
pub fn temporary_zcashd_from_parts(
|
|
||||||
shielded_spends: Vec<SpendDescription<A>>,
|
|
||||||
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
|
|
||||||
value_balance: V,
|
|
||||||
authorization: A,
|
|
||||||
) -> Self {
|
|
||||||
Self::from_parts(
|
|
||||||
shielded_spends,
|
|
||||||
shielded_outputs,
|
|
||||||
value_balance,
|
|
||||||
authorization,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a `Bundle` from its constituent parts.
|
|
||||||
pub(crate) fn from_parts(
|
|
||||||
shielded_spends: Vec<SpendDescription<A>>,
|
|
||||||
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
|
|
||||||
value_balance: V,
|
|
||||||
authorization: A,
|
|
||||||
) -> Self {
|
|
||||||
Bundle {
|
|
||||||
shielded_spends,
|
|
||||||
shielded_outputs,
|
|
||||||
value_balance,
|
|
||||||
authorization,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the list of spends in this bundle.
|
|
||||||
pub fn shielded_spends(&self) -> &[SpendDescription<A>] {
|
|
||||||
&self.shielded_spends
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the list of outputs in this bundle.
|
|
||||||
pub fn shielded_outputs(&self) -> &[OutputDescription<A::OutputProof>] {
|
|
||||||
&self.shielded_outputs
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the net value moved into or out of the Sapling shielded pool.
|
|
||||||
///
|
|
||||||
/// This is the sum of Sapling spends minus the sum of Sapling outputs.
|
|
||||||
pub fn value_balance(&self) -> &V {
|
|
||||||
&self.value_balance
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the authorization for this bundle.
|
|
||||||
///
|
|
||||||
/// In the case of a `Bundle<Authorized>`, this is the binding signature.
|
|
||||||
pub fn authorization(&self) -> &A {
|
|
||||||
&self.authorization
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transitions this bundle from one authorization state to another.
|
|
||||||
pub fn map_authorization<B: Authorization, F: MapAuth<A, B>>(self, mut f: F) -> Bundle<B, V> {
|
|
||||||
Bundle {
|
|
||||||
shielded_spends: self
|
|
||||||
.shielded_spends
|
|
||||||
.into_iter()
|
|
||||||
.map(|d| SpendDescription {
|
|
||||||
cv: d.cv,
|
|
||||||
anchor: d.anchor,
|
|
||||||
nullifier: d.nullifier,
|
|
||||||
rk: d.rk,
|
|
||||||
zkproof: f.map_spend_proof(d.zkproof),
|
|
||||||
spend_auth_sig: f.map_auth_sig(d.spend_auth_sig),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
shielded_outputs: self
|
|
||||||
.shielded_outputs
|
|
||||||
.into_iter()
|
|
||||||
.map(|o| OutputDescription {
|
|
||||||
cv: o.cv,
|
|
||||||
cmu: o.cmu,
|
|
||||||
ephemeral_key: o.ephemeral_key,
|
|
||||||
enc_ciphertext: o.enc_ciphertext,
|
|
||||||
out_ciphertext: o.out_ciphertext,
|
|
||||||
zkproof: f.map_output_proof(o.zkproof),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
value_balance: self.value_balance,
|
|
||||||
authorization: f.map_authorization(self.authorization),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transitions this bundle from one authorization state to another.
|
|
||||||
pub fn try_map_authorization<B: Authorization, F: TryMapAuth<A, B>>(
|
|
||||||
self,
|
|
||||||
mut f: F,
|
|
||||||
) -> Result<Bundle<B, V>, F::Error> {
|
|
||||||
Ok(Bundle {
|
|
||||||
shielded_spends: self
|
|
||||||
.shielded_spends
|
|
||||||
.into_iter()
|
|
||||||
.map(|d| {
|
|
||||||
Ok(SpendDescription {
|
|
||||||
cv: d.cv,
|
|
||||||
anchor: d.anchor,
|
|
||||||
nullifier: d.nullifier,
|
|
||||||
rk: d.rk,
|
|
||||||
zkproof: f.try_map_spend_proof(d.zkproof)?,
|
|
||||||
spend_auth_sig: f.try_map_auth_sig(d.spend_auth_sig)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?,
|
|
||||||
shielded_outputs: self
|
|
||||||
.shielded_outputs
|
|
||||||
.into_iter()
|
|
||||||
.map(|o| {
|
|
||||||
Ok(OutputDescription {
|
|
||||||
cv: o.cv,
|
|
||||||
cmu: o.cmu,
|
|
||||||
ephemeral_key: o.ephemeral_key,
|
|
||||||
enc_ciphertext: o.enc_ciphertext,
|
|
||||||
out_ciphertext: o.out_ciphertext,
|
|
||||||
zkproof: f.try_map_output_proof(o.zkproof)?,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?,
|
|
||||||
value_balance: self.value_balance,
|
|
||||||
authorization: f.try_map_authorization(self.authorization)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: DynamicUsage> DynamicUsage for Bundle<Authorized, V> {
|
|
||||||
fn dynamic_usage(&self) -> usize {
|
|
||||||
self.shielded_spends.dynamic_usage()
|
|
||||||
+ self.shielded_outputs.dynamic_usage()
|
|
||||||
+ self.value_balance.dynamic_usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
|
||||||
let bounds = (
|
|
||||||
self.shielded_spends.dynamic_usage_bounds(),
|
|
||||||
self.shielded_outputs.dynamic_usage_bounds(),
|
|
||||||
self.value_balance.dynamic_usage_bounds(),
|
|
||||||
);
|
|
||||||
|
|
||||||
(
|
|
||||||
bounds.0 .0 + bounds.1 .0 + bounds.2 .0,
|
|
||||||
bounds
|
|
||||||
.0
|
|
||||||
.1
|
|
||||||
.zip(bounds.1 .1)
|
|
||||||
.zip(bounds.2 .1)
|
|
||||||
.map(|((a, b), c)| a + b + c),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SpendDescription<A: Authorization> {
|
|
||||||
cv: ValueCommitment,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
nullifier: Nullifier,
|
|
||||||
rk: redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
zkproof: A::SpendProof,
|
|
||||||
spend_auth_sig: A::AuthSig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Authorization> std::fmt::Debug for SpendDescription<A> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})",
|
|
||||||
self.cv, self.anchor, self.nullifier, self.rk, self.spend_auth_sig
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Authorization> SpendDescription<A> {
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
|
||||||
pub fn temporary_zcashd_from_parts(
|
|
||||||
cv: ValueCommitment,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
nullifier: Nullifier,
|
|
||||||
rk: redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
zkproof: A::SpendProof,
|
|
||||||
spend_auth_sig: A::AuthSig,
|
|
||||||
) -> Self {
|
|
||||||
Self::from_parts(cv, anchor, nullifier, rk, zkproof, spend_auth_sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_parts(
|
|
||||||
cv: ValueCommitment,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
nullifier: Nullifier,
|
|
||||||
rk: redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
zkproof: A::SpendProof,
|
|
||||||
spend_auth_sig: A::AuthSig,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
cv,
|
|
||||||
anchor,
|
|
||||||
nullifier,
|
|
||||||
rk,
|
|
||||||
zkproof,
|
|
||||||
spend_auth_sig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the commitment to the value consumed by this spend.
|
|
||||||
pub fn cv(&self) -> &ValueCommitment {
|
|
||||||
&self.cv
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the root of the Sapling commitment tree that this spend commits to.
|
|
||||||
pub fn anchor(&self) -> &bls12_381::Scalar {
|
|
||||||
&self.anchor
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the nullifier of the note being spent.
|
|
||||||
pub fn nullifier(&self) -> &Nullifier {
|
|
||||||
&self.nullifier
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the randomized verification key for the note being spent.
|
|
||||||
pub fn rk(&self) -> &redjubjub::VerificationKey<SpendAuth> {
|
|
||||||
&self.rk
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the proof for this spend.
|
|
||||||
pub fn zkproof(&self) -> &A::SpendProof {
|
|
||||||
&self.zkproof
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the authorization signature for this spend.
|
|
||||||
pub fn spend_auth_sig(&self) -> &A::AuthSig {
|
|
||||||
&self.spend_auth_sig
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynamicUsage for SpendDescription<Authorized> {
|
|
||||||
fn dynamic_usage(&self) -> usize {
|
|
||||||
self.zkproof.dynamic_usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
|
||||||
self.zkproof.dynamic_usage_bounds()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SpendDescriptionV5 {
|
|
||||||
cv: ValueCommitment,
|
|
||||||
nullifier: Nullifier,
|
|
||||||
rk: redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpendDescriptionV5 {
|
|
||||||
pub(crate) fn from_parts(
|
|
||||||
cv: ValueCommitment,
|
|
||||||
nullifier: Nullifier,
|
|
||||||
rk: redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
) -> Self {
|
|
||||||
Self { cv, nullifier, rk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_spend_description(
|
|
||||||
self,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
zkproof: GrothProofBytes,
|
|
||||||
spend_auth_sig: redjubjub::Signature<SpendAuth>,
|
|
||||||
) -> SpendDescription<Authorized> {
|
|
||||||
SpendDescription {
|
|
||||||
cv: self.cv,
|
|
||||||
anchor,
|
|
||||||
nullifier: self.nullifier,
|
|
||||||
rk: self.rk,
|
|
||||||
zkproof,
|
|
||||||
spend_auth_sig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct OutputDescription<Proof> {
|
|
||||||
cv: ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
ephemeral_key: EphemeralKeyBytes,
|
|
||||||
enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
|
|
||||||
out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
|
|
||||||
zkproof: Proof,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Proof> OutputDescription<Proof> {
|
|
||||||
/// Returns the commitment to the value consumed by this output.
|
|
||||||
pub fn cv(&self) -> &ValueCommitment {
|
|
||||||
&self.cv
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the commitment to the new note being created.
|
|
||||||
pub fn cmu(&self) -> &ExtractedNoteCommitment {
|
|
||||||
&self.cmu
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ephemeral_key(&self) -> &EphemeralKeyBytes {
|
|
||||||
&self.ephemeral_key
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the encrypted note ciphertext.
|
|
||||||
pub fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] {
|
|
||||||
&self.enc_ciphertext
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the output recovery ciphertext.
|
|
||||||
pub fn out_ciphertext(&self) -> &[u8; OUT_CIPHERTEXT_SIZE] {
|
|
||||||
&self.out_ciphertext
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the proof for this output.
|
|
||||||
pub fn zkproof(&self) -> &Proof {
|
|
||||||
&self.zkproof
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
|
||||||
pub fn temporary_zcashd_from_parts(
|
|
||||||
cv: ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
ephemeral_key: EphemeralKeyBytes,
|
|
||||||
enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
|
|
||||||
out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
|
|
||||||
zkproof: Proof,
|
|
||||||
) -> Self {
|
|
||||||
Self::from_parts(
|
|
||||||
cv,
|
|
||||||
cmu,
|
|
||||||
ephemeral_key,
|
|
||||||
enc_ciphertext,
|
|
||||||
out_ciphertext,
|
|
||||||
zkproof,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_parts(
|
|
||||||
cv: ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
ephemeral_key: EphemeralKeyBytes,
|
|
||||||
enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
|
|
||||||
out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
|
|
||||||
zkproof: Proof,
|
|
||||||
) -> Self {
|
|
||||||
OutputDescription {
|
|
||||||
cv,
|
|
||||||
cmu,
|
|
||||||
ephemeral_key,
|
|
||||||
enc_ciphertext,
|
|
||||||
out_ciphertext,
|
|
||||||
zkproof,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl<Proof> OutputDescription<Proof> {
|
|
||||||
pub(crate) fn cv_mut(&mut self) -> &mut ValueCommitment {
|
|
||||||
&mut self.cv
|
|
||||||
}
|
|
||||||
pub(crate) fn cmu_mut(&mut self) -> &mut ExtractedNoteCommitment {
|
|
||||||
&mut self.cmu
|
|
||||||
}
|
|
||||||
pub(crate) fn ephemeral_key_mut(&mut self) -> &mut EphemeralKeyBytes {
|
|
||||||
&mut self.ephemeral_key
|
|
||||||
}
|
|
||||||
pub(crate) fn enc_ciphertext_mut(&mut self) -> &mut [u8; ENC_CIPHERTEXT_SIZE] {
|
|
||||||
&mut self.enc_ciphertext
|
|
||||||
}
|
|
||||||
pub(crate) fn out_ciphertext_mut(&mut self) -> &mut [u8; OUT_CIPHERTEXT_SIZE] {
|
|
||||||
&mut self.out_ciphertext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Proof: DynamicUsage> DynamicUsage for OutputDescription<Proof> {
|
|
||||||
fn dynamic_usage(&self) -> usize {
|
|
||||||
self.zkproof.dynamic_usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
|
||||||
self.zkproof.dynamic_usage_bounds()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> ShieldedOutput<SaplingDomain, ENC_CIPHERTEXT_SIZE> for OutputDescription<A> {
|
|
||||||
fn ephemeral_key(&self) -> EphemeralKeyBytes {
|
|
||||||
self.ephemeral_key.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmstar_bytes(&self) -> [u8; 32] {
|
|
||||||
self.cmu.to_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] {
|
|
||||||
&self.enc_ciphertext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> std::fmt::Debug for OutputDescription<A> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})",
|
|
||||||
self.cv, self.cmu, self.ephemeral_key
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct OutputDescriptionV5 {
|
|
||||||
cv: ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
ephemeral_key: EphemeralKeyBytes,
|
|
||||||
enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
|
|
||||||
out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
|
|
||||||
}
|
|
||||||
|
|
||||||
memuse::impl_no_dynamic_usage!(OutputDescriptionV5);
|
|
||||||
|
|
||||||
impl OutputDescriptionV5 {
|
|
||||||
pub(crate) fn from_parts(
|
|
||||||
cv: ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
ephemeral_key: EphemeralKeyBytes,
|
|
||||||
enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
|
|
||||||
out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
cv,
|
|
||||||
cmu,
|
|
||||||
ephemeral_key,
|
|
||||||
enc_ciphertext,
|
|
||||||
out_ciphertext,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_output_description(
|
|
||||||
self,
|
|
||||||
zkproof: GrothProofBytes,
|
|
||||||
) -> OutputDescription<GrothProofBytes> {
|
|
||||||
OutputDescription {
|
|
||||||
cv: self.cv,
|
|
||||||
cmu: self.cmu,
|
|
||||||
ephemeral_key: self.ephemeral_key,
|
|
||||||
enc_ciphertext: self.enc_ciphertext,
|
|
||||||
out_ciphertext: self.out_ciphertext,
|
|
||||||
zkproof,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> From<OutputDescription<A>> for CompactOutputDescription {
|
|
||||||
fn from(out: OutputDescription<A>) -> CompactOutputDescription {
|
|
||||||
CompactOutputDescription {
|
|
||||||
ephemeral_key: out.ephemeral_key,
|
|
||||||
cmu: out.cmu,
|
|
||||||
enc_ciphertext: out.enc_ciphertext[..COMPACT_NOTE_SIZE].try_into().unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub mod testing {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use ff::Field;
|
|
||||||
use group::{Group, GroupEncoding};
|
|
||||||
use proptest::collection::vec;
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use rand::{rngs::StdRng, SeedableRng};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
sapling::{
|
|
||||||
note::testing::arb_cmu,
|
|
||||||
value::{
|
|
||||||
testing::{arb_note_value_bounded, arb_trapdoor},
|
|
||||||
ValueCommitment, MAX_NOTE_VALUE,
|
|
||||||
},
|
|
||||||
Nullifier,
|
|
||||||
},
|
|
||||||
transaction::components::GROTH_PROOF_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
Authorized, Bundle, GrothProofBytes, OutputDescription, SpendDescription,
|
|
||||||
ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
fn arb_extended_point()(rng_seed in prop::array::uniform32(any::<u8>())) -> jubjub::ExtendedPoint {
|
|
||||||
let mut rng = StdRng::from_seed(rng_seed);
|
|
||||||
let scalar = jubjub::Scalar::random(&mut rng);
|
|
||||||
jubjub::ExtendedPoint::generator() * scalar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// produce a spend description with invalid data (useful only for serialization
|
|
||||||
/// roundtrip testing).
|
|
||||||
fn arb_spend_description(n_spends: usize)(
|
|
||||||
value in arb_note_value_bounded(MAX_NOTE_VALUE.checked_div(n_spends as u64).unwrap_or(0)),
|
|
||||||
rcv in arb_trapdoor(),
|
|
||||||
anchor in vec(any::<u8>(), 64)
|
|
||||||
.prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
|
|
||||||
.prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
|
|
||||||
nullifier in prop::array::uniform32(any::<u8>())
|
|
||||||
.prop_map(|v| Nullifier::from_slice(&v).unwrap()),
|
|
||||||
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
|
|
||||||
.prop_map(|v| <[u8;GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
|
|
||||||
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
|
|
||||||
fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY),
|
|
||||||
) -> SpendDescription<Authorized> {
|
|
||||||
let mut rng = StdRng::from_seed(rng_seed);
|
|
||||||
let sk1 = redjubjub::SigningKey::new(&mut rng);
|
|
||||||
let rk = redjubjub::VerificationKey::from(&sk1);
|
|
||||||
let cv = ValueCommitment::derive(value, rcv);
|
|
||||||
SpendDescription {
|
|
||||||
cv,
|
|
||||||
anchor,
|
|
||||||
nullifier,
|
|
||||||
rk,
|
|
||||||
zkproof,
|
|
||||||
spend_auth_sig: sk1.sign(&mut rng, &fake_sighash_bytes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// produce an output description with invalid data (useful only for serialization
|
|
||||||
/// roundtrip testing).
|
|
||||||
pub fn arb_output_description(n_outputs: usize)(
|
|
||||||
value in arb_note_value_bounded(MAX_NOTE_VALUE.checked_div(n_outputs as u64).unwrap_or(0)),
|
|
||||||
rcv in arb_trapdoor(),
|
|
||||||
cmu in arb_cmu(),
|
|
||||||
enc_ciphertext in vec(any::<u8>(), ENC_CIPHERTEXT_SIZE)
|
|
||||||
.prop_map(|v| <[u8; ENC_CIPHERTEXT_SIZE]>::try_from(v.as_slice()).unwrap()),
|
|
||||||
epk in arb_extended_point(),
|
|
||||||
out_ciphertext in vec(any::<u8>(), OUT_CIPHERTEXT_SIZE)
|
|
||||||
.prop_map(|v| <[u8; OUT_CIPHERTEXT_SIZE]>::try_from(v.as_slice()).unwrap()),
|
|
||||||
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
|
|
||||||
.prop_map(|v| <[u8; GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
|
|
||||||
) -> OutputDescription<GrothProofBytes> {
|
|
||||||
let cv = ValueCommitment::derive(value, rcv);
|
|
||||||
OutputDescription {
|
|
||||||
cv,
|
|
||||||
cmu,
|
|
||||||
ephemeral_key: epk.to_bytes().into(),
|
|
||||||
enc_ciphertext,
|
|
||||||
out_ciphertext,
|
|
||||||
zkproof,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arb_bundle<V: Copy + fmt::Debug + 'static>(
|
|
||||||
value_balance: V,
|
|
||||||
) -> impl Strategy<Value = Option<Bundle<Authorized, V>>> {
|
|
||||||
(0usize..30, 0usize..30)
|
|
||||||
.prop_flat_map(|(n_spends, n_outputs)| {
|
|
||||||
(
|
|
||||||
vec(arb_spend_description(n_spends), n_spends),
|
|
||||||
vec(arb_output_description(n_outputs), n_outputs),
|
|
||||||
prop::array::uniform32(prop::num::u8::ANY),
|
|
||||||
prop::array::uniform32(prop::num::u8::ANY),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.prop_map(
|
|
||||||
move |(shielded_spends, shielded_outputs, rng_seed, fake_bvk_bytes)| {
|
|
||||||
if shielded_spends.is_empty() && shielded_outputs.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut rng = StdRng::from_seed(rng_seed);
|
|
||||||
let bsk = redjubjub::SigningKey::new(&mut rng);
|
|
||||||
|
|
||||||
Some(Bundle {
|
|
||||||
shielded_spends,
|
|
||||||
shielded_outputs,
|
|
||||||
value_balance,
|
|
||||||
authorization: Authorized {
|
|
||||||
binding_sig: bsk.sign(&mut rng, &fake_bvk_bytes),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,195 +0,0 @@
|
||||||
//! Various constants used for the Zcash proofs.
|
|
||||||
|
|
||||||
use crate::sapling::constants::{PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_GENERATORS};
|
|
||||||
use bls12_381::Scalar;
|
|
||||||
use group::{ff::Field, Curve, Group};
|
|
||||||
use jubjub::ExtendedPoint;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
/// The `d` constant of the twisted Edwards curve.
|
|
||||||
pub(crate) const EDWARDS_D: Scalar = Scalar::from_raw([
|
|
||||||
0x0106_5fd6_d634_3eb1,
|
|
||||||
0x292d_7f6d_3757_9d26,
|
|
||||||
0xf5fd_9207_e6bd_7fd4,
|
|
||||||
0x2a93_18e7_4bfa_2b48,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/// The `A` constant of the birationally equivalent Montgomery curve.
|
|
||||||
pub(crate) const MONTGOMERY_A: Scalar = Scalar::from_raw([
|
|
||||||
0x0000_0000_0000_a002,
|
|
||||||
0x0000_0000_0000_0000,
|
|
||||||
0x0000_0000_0000_0000,
|
|
||||||
0x0000_0000_0000_0000,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/// The scaling factor used for conversion to and from the Montgomery form.
|
|
||||||
pub(crate) const MONTGOMERY_SCALE: Scalar = Scalar::from_raw([
|
|
||||||
0x8f45_35f7_cf82_b8d9,
|
|
||||||
0xce40_6970_3da8_8abd,
|
|
||||||
0x31de_341e_77d7_64e5,
|
|
||||||
0x2762_de61_e862_645e,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/// The number of chunks needed to represent a full scalar during fixed-base
|
|
||||||
/// exponentiation.
|
|
||||||
const FIXED_BASE_CHUNKS_PER_GENERATOR: usize = 84;
|
|
||||||
|
|
||||||
/// Reference to a circuit version of a generator for fixed-base salar multiplication.
|
|
||||||
pub type FixedGenerator = &'static [Vec<(Scalar, Scalar)>];
|
|
||||||
|
|
||||||
/// Circuit version of a generator for fixed-base salar multiplication.
|
|
||||||
pub type FixedGeneratorOwned = Vec<Vec<(Scalar, Scalar)>>;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref PROOF_GENERATION_KEY_GENERATOR: FixedGeneratorOwned =
|
|
||||||
generate_circuit_generator(crate::sapling::constants::PROOF_GENERATION_KEY_GENERATOR);
|
|
||||||
|
|
||||||
pub static ref NOTE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned =
|
|
||||||
generate_circuit_generator(crate::sapling::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR);
|
|
||||||
|
|
||||||
pub static ref NULLIFIER_POSITION_GENERATOR: FixedGeneratorOwned =
|
|
||||||
generate_circuit_generator(crate::sapling::constants::NULLIFIER_POSITION_GENERATOR);
|
|
||||||
|
|
||||||
pub static ref VALUE_COMMITMENT_VALUE_GENERATOR: FixedGeneratorOwned =
|
|
||||||
generate_circuit_generator(crate::sapling::constants::VALUE_COMMITMENT_VALUE_GENERATOR);
|
|
||||||
|
|
||||||
pub static ref VALUE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned =
|
|
||||||
generate_circuit_generator(crate::sapling::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR);
|
|
||||||
|
|
||||||
pub static ref SPENDING_KEY_GENERATOR: FixedGeneratorOwned =
|
|
||||||
generate_circuit_generator(crate::sapling::constants::SPENDING_KEY_GENERATOR);
|
|
||||||
|
|
||||||
/// The pre-computed window tables `[-4, 3, 2, 1, 1, 2, 3, 4]` of different magnitudes
|
|
||||||
/// of the Pedersen hash segment generators.
|
|
||||||
pub(crate) static ref PEDERSEN_CIRCUIT_GENERATORS: Vec<Vec<Vec<(Scalar, Scalar)>>> =
|
|
||||||
generate_pedersen_circuit_generators();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the 3-bit window table `[0, 1, ..., 8]` for different magnitudes of a fixed
|
|
||||||
/// generator.
|
|
||||||
pub fn generate_circuit_generator(mut gen: jubjub::SubgroupPoint) -> FixedGeneratorOwned {
|
|
||||||
let mut windows = vec![];
|
|
||||||
|
|
||||||
for _ in 0..FIXED_BASE_CHUNKS_PER_GENERATOR {
|
|
||||||
let mut coeffs = vec![(Scalar::zero(), Scalar::one())];
|
|
||||||
let mut g = gen;
|
|
||||||
for _ in 0..7 {
|
|
||||||
let g_affine = jubjub::ExtendedPoint::from(g).to_affine();
|
|
||||||
coeffs.push((g_affine.get_u(), g_affine.get_v()));
|
|
||||||
g += gen;
|
|
||||||
}
|
|
||||||
windows.push(coeffs);
|
|
||||||
|
|
||||||
// gen = gen * 8
|
|
||||||
gen = g;
|
|
||||||
}
|
|
||||||
|
|
||||||
windows
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the coordinates of this point's Montgomery curve representation, or `None` if
|
|
||||||
/// it is the point at infinity.
|
|
||||||
#[allow(clippy::many_single_char_names)]
|
|
||||||
pub(crate) fn to_montgomery_coords(g: ExtendedPoint) -> Option<(Scalar, Scalar)> {
|
|
||||||
let g = g.to_affine();
|
|
||||||
let (x, y) = (g.get_u(), g.get_v());
|
|
||||||
|
|
||||||
if y == Scalar::one() {
|
|
||||||
// The only solution for y = 1 is x = 0. (0, 1) is the neutral element, so we map
|
|
||||||
// this to the point at infinity.
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// The map from a twisted Edwards curve is defined as
|
|
||||||
// (x, y) -> (u, v) where
|
|
||||||
// u = (1 + y) / (1 - y)
|
|
||||||
// v = u / x
|
|
||||||
//
|
|
||||||
// This mapping is not defined for y = 1 and for x = 0.
|
|
||||||
//
|
|
||||||
// We have that y != 1 above. If x = 0, the only
|
|
||||||
// solutions for y are 1 (contradiction) or -1.
|
|
||||||
if x.is_zero_vartime() {
|
|
||||||
// (0, -1) is the point of order two which is not
|
|
||||||
// the neutral element, so we map it to (0, 0) which is
|
|
||||||
// the only affine point of order 2.
|
|
||||||
Some((Scalar::zero(), Scalar::zero()))
|
|
||||||
} else {
|
|
||||||
// The mapping is defined as above.
|
|
||||||
//
|
|
||||||
// (x, y) -> (u, v) where
|
|
||||||
// u = (1 + y) / (1 - y)
|
|
||||||
// v = u / x
|
|
||||||
|
|
||||||
let u = (Scalar::one() + y) * (Scalar::one() - y).invert().unwrap();
|
|
||||||
let v = u * x.invert().unwrap();
|
|
||||||
|
|
||||||
// Scale it into the correct curve constants
|
|
||||||
// scaling factor = sqrt(4 / (a - d))
|
|
||||||
Some((u, v * MONTGOMERY_SCALE))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the 2-bit window table lookups for each 4-bit "chunk" in each segment of the
|
|
||||||
/// Pedersen hash.
|
|
||||||
fn generate_pedersen_circuit_generators() -> Vec<Vec<Vec<(Scalar, Scalar)>>> {
|
|
||||||
// Process each segment
|
|
||||||
PEDERSEN_HASH_GENERATORS
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|mut gen| {
|
|
||||||
let mut windows = vec![];
|
|
||||||
|
|
||||||
for _ in 0..PEDERSEN_HASH_CHUNKS_PER_GENERATOR {
|
|
||||||
// Create (x, y) coeffs for this chunk
|
|
||||||
let mut coeffs = vec![];
|
|
||||||
let mut g = gen;
|
|
||||||
|
|
||||||
// coeffs = g, g*2, g*3, g*4
|
|
||||||
for _ in 0..4 {
|
|
||||||
coeffs.push(
|
|
||||||
to_montgomery_coords(g.into())
|
|
||||||
.expect("we never encounter the point at infinity"),
|
|
||||||
);
|
|
||||||
g += gen;
|
|
||||||
}
|
|
||||||
windows.push(coeffs);
|
|
||||||
|
|
||||||
// Our chunks are separated by 2 bits to prevent overlap.
|
|
||||||
for _ in 0..4 {
|
|
||||||
gen = gen.double();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
windows
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn edwards_d() {
|
|
||||||
// d = -(10240/10241)
|
|
||||||
assert_eq!(
|
|
||||||
-Scalar::from(10240) * Scalar::from(10241).invert().unwrap(),
|
|
||||||
EDWARDS_D
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn montgomery_a() {
|
|
||||||
assert_eq!(Scalar::from(40962), MONTGOMERY_A);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn montgomery_scale() {
|
|
||||||
// scaling factor = sqrt(4 / (a - d))
|
|
||||||
assert_eq!(
|
|
||||||
MONTGOMERY_SCALE.square() * (-Scalar::one() - EDWARDS_D),
|
|
||||||
Scalar::from(4),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,302 +0,0 @@
|
||||||
//! Gadget for Zcash's Pedersen hash.
|
|
||||||
|
|
||||||
use super::ecc::{EdwardsPoint, MontgomeryPoint};
|
|
||||||
pub use crate::sapling::pedersen_hash::Personalization;
|
|
||||||
use bellman::gadgets::boolean::Boolean;
|
|
||||||
use bellman::gadgets::lookup::*;
|
|
||||||
use bellman::{ConstraintSystem, SynthesisError};
|
|
||||||
|
|
||||||
use super::constants::PEDERSEN_CIRCUIT_GENERATORS;
|
|
||||||
|
|
||||||
fn get_constant_bools(person: &Personalization) -> Vec<Boolean> {
|
|
||||||
person
|
|
||||||
.get_bits()
|
|
||||||
.into_iter()
|
|
||||||
.map(Boolean::constant)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pedersen_hash<CS>(
|
|
||||||
mut cs: CS,
|
|
||||||
personalization: Personalization,
|
|
||||||
bits: &[Boolean],
|
|
||||||
) -> Result<EdwardsPoint, SynthesisError>
|
|
||||||
where
|
|
||||||
CS: ConstraintSystem<bls12_381::Scalar>,
|
|
||||||
{
|
|
||||||
let personalization = get_constant_bools(&personalization);
|
|
||||||
assert_eq!(personalization.len(), 6);
|
|
||||||
|
|
||||||
let mut edwards_result = None;
|
|
||||||
let mut bits = personalization.iter().chain(bits.iter()).peekable();
|
|
||||||
let mut segment_generators = PEDERSEN_CIRCUIT_GENERATORS.iter();
|
|
||||||
let boolean_false = Boolean::constant(false);
|
|
||||||
|
|
||||||
let mut segment_i = 0;
|
|
||||||
while bits.peek().is_some() {
|
|
||||||
let mut segment_result = None;
|
|
||||||
let mut segment_windows = &segment_generators.next().expect("enough segments")[..];
|
|
||||||
|
|
||||||
let mut window_i = 0;
|
|
||||||
while let Some(a) = bits.next() {
|
|
||||||
let b = bits.next().unwrap_or(&boolean_false);
|
|
||||||
let c = bits.next().unwrap_or(&boolean_false);
|
|
||||||
|
|
||||||
let tmp = lookup3_xy_with_conditional_negation(
|
|
||||||
cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)),
|
|
||||||
&[a.clone(), b.clone(), c.clone()],
|
|
||||||
&segment_windows[0],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1);
|
|
||||||
|
|
||||||
match segment_result {
|
|
||||||
None => {
|
|
||||||
segment_result = Some(tmp);
|
|
||||||
}
|
|
||||||
Some(ref mut segment_result) => {
|
|
||||||
*segment_result = tmp.add(
|
|
||||||
cs.namespace(|| {
|
|
||||||
format!("addition of segment {}, window {}", segment_i, window_i)
|
|
||||||
}),
|
|
||||||
segment_result,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
segment_windows = &segment_windows[1..];
|
|
||||||
|
|
||||||
if segment_windows.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
window_i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let segment_result = segment_result.expect(
|
|
||||||
"bits is not exhausted due to while condition;
|
|
||||||
thus there must be a segment window;
|
|
||||||
thus there must be a segment result",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert this segment into twisted Edwards form.
|
|
||||||
let segment_result = segment_result.into_edwards(
|
|
||||||
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match edwards_result {
|
|
||||||
Some(ref mut edwards_result) => {
|
|
||||||
*edwards_result = segment_result.add(
|
|
||||||
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)),
|
|
||||||
edwards_result,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
edwards_result = Some(segment_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
segment_i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(edwards_result.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::sapling::pedersen_hash;
|
|
||||||
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
|
|
||||||
use bellman::gadgets::test::*;
|
|
||||||
use group::{ff::PrimeField, Curve};
|
|
||||||
use rand_core::{RngCore, SeedableRng};
|
|
||||||
use rand_xorshift::XorShiftRng;
|
|
||||||
|
|
||||||
/// Predict the number of constraints of a Pedersen hash
|
|
||||||
fn ph_num_constraints(input_bits: usize) -> usize {
|
|
||||||
// Account for the 6 personalization bits.
|
|
||||||
let personalized_bits = 6 + input_bits;
|
|
||||||
// Constant booleans in the personalization and padding don't need lookup "precomp" constraints.
|
|
||||||
let precomputed_booleans = 2 + (personalized_bits % 3 == 1) as usize;
|
|
||||||
|
|
||||||
// Count chunks and segments with ceiling division
|
|
||||||
let chunks = (personalized_bits + 3 - 1) / 3;
|
|
||||||
let segments = (chunks + 63 - 1) / 63;
|
|
||||||
let all_but_last_segments = segments - 1;
|
|
||||||
let last_chunks = chunks - all_but_last_segments * 63;
|
|
||||||
|
|
||||||
// Constraints per operation
|
|
||||||
let lookup_chunk = 2;
|
|
||||||
let add_chunks = 3; // Montgomery addition
|
|
||||||
let convert_segment = 2; // Conversion to Edwards
|
|
||||||
let add_segments = 6; // Edwards addition
|
|
||||||
|
|
||||||
(chunks) * lookup_chunk - precomputed_booleans
|
|
||||||
+ segments * convert_segment
|
|
||||||
+ all_but_last_segments * ((63 - 1) * add_chunks + add_segments)
|
|
||||||
+ (last_chunks - 1) * add_chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pedersen_hash_constraints() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let leaves_len = 2 * 255;
|
|
||||||
let note_len = 64 + 256 + 256;
|
|
||||||
|
|
||||||
for &n_bits in [
|
|
||||||
0,
|
|
||||||
3 * 63 - 6,
|
|
||||||
3 * 63 - 6 + 1,
|
|
||||||
3 * 63 - 6 + 2,
|
|
||||||
leaves_len,
|
|
||||||
note_len,
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
{
|
|
||||||
let mut cs = TestConstraintSystem::new();
|
|
||||||
|
|
||||||
let input: Vec<bool> = (0..n_bits).map(|_| rng.next_u32() % 2 != 0).collect();
|
|
||||||
|
|
||||||
let input_bools: Vec<Boolean> = input
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, b)| {
|
|
||||||
Boolean::from(
|
|
||||||
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b))
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
pedersen_hash(
|
|
||||||
cs.namespace(|| "pedersen hash"),
|
|
||||||
Personalization::NoteCommitment,
|
|
||||||
&input_bools,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
|
||||||
|
|
||||||
let bitness_constraints = n_bits;
|
|
||||||
let ph_constraints = ph_num_constraints(n_bits);
|
|
||||||
assert_eq!(cs.num_constraints(), bitness_constraints + ph_constraints);
|
|
||||||
// The actual usages
|
|
||||||
if n_bits == leaves_len {
|
|
||||||
assert_eq!(cs.num_constraints(), leaves_len + 867)
|
|
||||||
};
|
|
||||||
if n_bits == note_len {
|
|
||||||
assert_eq!(cs.num_constraints(), note_len + 982)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pedersen_hash() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
|
|
||||||
for length in 0..751 {
|
|
||||||
for _ in 0..5 {
|
|
||||||
let input: Vec<bool> = (0..length).map(|_| rng.next_u32() % 2 != 0).collect();
|
|
||||||
|
|
||||||
let mut cs = TestConstraintSystem::new();
|
|
||||||
|
|
||||||
let input_bools: Vec<Boolean> = input
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, b)| {
|
|
||||||
Boolean::from(
|
|
||||||
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b))
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let res = pedersen_hash(
|
|
||||||
cs.namespace(|| "pedersen hash"),
|
|
||||||
Personalization::MerkleTree(1),
|
|
||||||
&input_bools,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
|
||||||
|
|
||||||
let expected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
|
|
||||||
Personalization::MerkleTree(1),
|
|
||||||
input.clone().into_iter(),
|
|
||||||
))
|
|
||||||
.to_affine();
|
|
||||||
|
|
||||||
assert_eq!(res.get_u().get_value().unwrap(), expected.get_u());
|
|
||||||
assert_eq!(res.get_v().get_value().unwrap(), expected.get_v());
|
|
||||||
|
|
||||||
// Test against the output of a different personalization
|
|
||||||
let unexpected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
|
|
||||||
Personalization::MerkleTree(0),
|
|
||||||
input.into_iter(),
|
|
||||||
))
|
|
||||||
.to_affine();
|
|
||||||
|
|
||||||
assert!(res.get_u().get_value().unwrap() != unexpected.get_u());
|
|
||||||
assert!(res.get_v().get_value().unwrap() != unexpected.get_v());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pedersen_hash_external_test_vectors() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let expected_us = [
|
|
||||||
"28161926966428986673895580777285905189725480206811328272001879986576840909576",
|
|
||||||
"39669831794597628158501766225645040955899576179071014703006420393381978263045",
|
|
||||||
];
|
|
||||||
let expected_vs = [
|
|
||||||
"26869991781071974894722407757894142583682396277979904369818887810555917099932",
|
|
||||||
"2112827187110048608327330788910224944044097981650120385961435904443901436107",
|
|
||||||
];
|
|
||||||
for length in 300..302 {
|
|
||||||
let input: Vec<bool> = (0..length).map(|_| rng.next_u32() % 2 != 0).collect();
|
|
||||||
|
|
||||||
let mut cs = TestConstraintSystem::new();
|
|
||||||
|
|
||||||
let input_bools: Vec<Boolean> = input
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, b)| {
|
|
||||||
Boolean::from(
|
|
||||||
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b))
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let res = pedersen_hash(
|
|
||||||
cs.namespace(|| "pedersen hash"),
|
|
||||||
Personalization::MerkleTree(1),
|
|
||||||
&input_bools,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
res.get_u().get_value().unwrap(),
|
|
||||||
bls12_381::Scalar::from_str_vartime(expected_us[length - 300]).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
res.get_v().get_value().unwrap(),
|
|
||||||
bls12_381::Scalar::from_str_vartime(expected_vs[length - 300]).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,436 +0,0 @@
|
||||||
//! Various constants used by the Sapling protocol.
|
|
||||||
|
|
||||||
use ff::PrimeField;
|
|
||||||
use group::Group;
|
|
||||||
use jubjub::SubgroupPoint;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
/// First 64 bytes of the BLAKE2s input during group hash.
|
|
||||||
/// This is chosen to be some random string that we couldn't have anticipated when we designed
|
|
||||||
/// the algorithm, for rigidity purposes.
|
|
||||||
/// We deliberately use an ASCII hex string of 32 bytes here.
|
|
||||||
pub const GH_FIRST_BLOCK: &[u8; 64] =
|
|
||||||
b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
|
|
||||||
|
|
||||||
// BLAKE2s invocation personalizations
|
|
||||||
/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk)
|
|
||||||
pub const CRH_IVK_PERSONALIZATION: &[u8; 8] = b"Zcashivk";
|
|
||||||
|
|
||||||
/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
|
|
||||||
pub const PRF_NF_PERSONALIZATION: &[u8; 8] = b"Zcash_nf";
|
|
||||||
|
|
||||||
// Group hash personalizations
|
|
||||||
/// BLAKE2s Personalization for Pedersen hash generators.
|
|
||||||
pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &[u8; 8] = b"Zcash_PH";
|
|
||||||
|
|
||||||
/// BLAKE2s Personalization for the group hash for key diversification
|
|
||||||
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &[u8; 8] = b"Zcash_gd";
|
|
||||||
|
|
||||||
/// BLAKE2s Personalization for the spending key base point
|
|
||||||
pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_G_";
|
|
||||||
|
|
||||||
/// BLAKE2s Personalization for the proof generation key base point
|
|
||||||
pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_H_";
|
|
||||||
|
|
||||||
/// BLAKE2s Personalization for the value commitment generator for the value
|
|
||||||
pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv";
|
|
||||||
|
|
||||||
/// BLAKE2s Personalization for the nullifier position generator (for computing rho)
|
|
||||||
pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_";
|
|
||||||
|
|
||||||
/// The prover will demonstrate knowledge of discrete log with respect to this base when
|
|
||||||
/// they are constructing a proof, in order to authorize proof construction.
|
|
||||||
pub const PROOF_GENERATION_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x3af2_dbef_b96e_2571,
|
|
||||||
0xadf2_d038_f2fb_b820,
|
|
||||||
0x7043_03f1_e890_6081,
|
|
||||||
0x1457_a502_31cd_e2df,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x467a_f9f7_e05d_e8e7,
|
|
||||||
0x50df_51ea_f5a1_49d2,
|
|
||||||
0xdec9_0184_0f49_48cc,
|
|
||||||
0x54b6_d107_18df_2a7a,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The note commitment is randomized over this generator.
|
|
||||||
pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0xa514_3b34_a8e3_6462,
|
|
||||||
0xf091_9d06_ffb1_ecda,
|
|
||||||
0xa140_9aa1_f33b_ec2c,
|
|
||||||
0x26eb_9f8a_9ec7_2a8c,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0xd4fc_6365_796c_77ac,
|
|
||||||
0x96b7_8bea_fa9c_c44c,
|
|
||||||
0x949d_7747_6e26_2c95,
|
|
||||||
0x114b_7501_ad10_4c57,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The node commitment is randomized again by the position in order to supply the
|
|
||||||
/// nullifier computation with a unique input w.r.t. the note being spent, to prevent
|
|
||||||
/// Faerie gold attacks.
|
|
||||||
pub const NULLIFIER_POSITION_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x2ce3_3921_888d_30db,
|
|
||||||
0xe81c_ee09_a561_229e,
|
|
||||||
0xdb56_b6db_8d80_75ed,
|
|
||||||
0x2400_c2e2_e336_2644,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0xa3f7_fa36_c72b_0065,
|
|
||||||
0xe155_b8e8_ffff_2e42,
|
|
||||||
0xfc9e_8a15_a096_ba8f,
|
|
||||||
0x6136_9d54_40bf_84a5,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The value commitment is used to check balance between inputs and outputs. The value is
|
|
||||||
/// placed over this generator.
|
|
||||||
pub const VALUE_COMMITMENT_VALUE_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x3618_3b2c_b4d7_ef51,
|
|
||||||
0x9472_c89a_c043_042d,
|
|
||||||
0xd861_8ed1_d15f_ef4e,
|
|
||||||
0x273f_910d_9ecc_1615,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0xa77a_81f5_0667_c8d7,
|
|
||||||
0xbc33_32d0_fa1c_cd18,
|
|
||||||
0xd322_94fd_8977_4ad6,
|
|
||||||
0x466a_7e3a_82f6_7ab1,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The value commitment is randomized over this generator, for privacy.
|
|
||||||
pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x3bce_3b77_9366_4337,
|
|
||||||
0xd1d8_da41_af03_744e,
|
|
||||||
0x7ff6_826a_d580_04b4,
|
|
||||||
0x6800_f4fa_0f00_1cfc,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x3cae_fab9_380b_6a8b,
|
|
||||||
0xad46_f1b0_473b_803b,
|
|
||||||
0xe6fb_2a6e_1e22_ab50,
|
|
||||||
0x6d81_d3a9_cb45_dedb,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The spender proves discrete log with respect to this base at spend time.
|
|
||||||
pub const SPENDING_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x47bf_4692_0a95_a753,
|
|
||||||
0xd5b9_a7d3_ef8e_2827,
|
|
||||||
0xd418_a7ff_2675_3b6a,
|
|
||||||
0x0926_d4f3_2059_c712,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x3056_32ad_aaf2_b530,
|
|
||||||
0x6d65_674d_cedb_ddbc,
|
|
||||||
0x53bb_37d0_c21c_fd05,
|
|
||||||
0x57a1_019e_6de9_b675,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The generators (for each segment) used in all Pedersen commitments.
|
|
||||||
pub const PEDERSEN_HASH_GENERATORS: &[SubgroupPoint] = &[
|
|
||||||
SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x194e_4292_6f66_1b51,
|
|
||||||
0x2f0c_718f_6f0f_badd,
|
|
||||||
0xb5ea_25de_7ec0_e378,
|
|
||||||
0x73c0_16a4_2ded_9578,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x77bf_abd4_3224_3cca,
|
|
||||||
0xf947_2e8b_c04e_4632,
|
|
||||||
0x79c9_166b_837e_dc5e,
|
|
||||||
0x289e_87a2_d352_1b57,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0xb981_9dc8_2d90_607e,
|
|
||||||
0xa361_ee3f_d48f_df77,
|
|
||||||
0x52a3_5a8c_1908_dd87,
|
|
||||||
0x15a3_6d1f_0f39_0d88,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x7b0d_c53c_4ebf_1891,
|
|
||||||
0x1f3a_beeb_98fa_d3e8,
|
|
||||||
0xf789_1142_c001_d925,
|
|
||||||
0x015d_8c7f_5b43_fe33,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x76d6_f7c2_b67f_c475,
|
|
||||||
0xbae8_e5c4_6641_ae5c,
|
|
||||||
0xeb69_ae39_f5c8_4210,
|
|
||||||
0x6643_21a5_8246_e2f6,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x80ed_502c_9793_d457,
|
|
||||||
0x8bb2_2a7f_1784_b498,
|
|
||||||
0xe000_a46c_8e8c_e853,
|
|
||||||
0x362e_1500_d24e_ee9e,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x4c76_7804_c1c4_a2cc,
|
|
||||||
0x7d02_d50e_654b_87f2,
|
|
||||||
0xedc5_f4a9_cff2_9fd5,
|
|
||||||
0x323a_6548_ce9d_9876,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x8471_4bec_a335_70e9,
|
|
||||||
0x5103_afa1_a11f_6a85,
|
|
||||||
0x9107_0acb_d8d9_47b7,
|
|
||||||
0x2f7e_e40c_4b56_cad8,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x4680_9430_657f_82d1,
|
|
||||||
0xefd5_9313_05f2_f0bf,
|
|
||||||
0x89b6_4b4e_0336_2796,
|
|
||||||
0x3bd2_6660_00b5_4796,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x9996_8299_c365_8aef,
|
|
||||||
0xb3b9_d809_5859_d14c,
|
|
||||||
0x3978_3238_1406_c9e5,
|
|
||||||
0x494b_c521_03ab_9d0a,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
SubgroupPoint::from_raw_unchecked(
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0xcb3c_0232_58d3_2079,
|
|
||||||
0x1d9e_5ca2_1135_ff6f,
|
|
||||||
0xda04_9746_d76d_3ee5,
|
|
||||||
0x6344_7b2b_a31b_b28a,
|
|
||||||
]),
|
|
||||||
bls12_381::Scalar::from_raw([
|
|
||||||
0x4360_8211_9f8d_629a,
|
|
||||||
0xa802_00d2_c66b_13a7,
|
|
||||||
0x64cd_b107_0a13_6a28,
|
|
||||||
0x64ec_4689_e8bf_b6e5,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// The maximum number of chunks per segment of the Pedersen hash.
|
|
||||||
pub const PEDERSEN_HASH_CHUNKS_PER_GENERATOR: usize = 63;
|
|
||||||
|
|
||||||
/// The window size for exponentiation of Pedersen hash generators outside the circuit.
|
|
||||||
pub const PEDERSEN_HASH_EXP_WINDOW_SIZE: u32 = 8;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
/// The exp table for [`PEDERSEN_HASH_GENERATORS`].
|
|
||||||
pub static ref PEDERSEN_HASH_EXP_TABLE: Vec<Vec<Vec<SubgroupPoint>>> =
|
|
||||||
generate_pedersen_hash_exp_table();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the exp table for the Pedersen hash generators.
|
|
||||||
fn generate_pedersen_hash_exp_table() -> Vec<Vec<Vec<SubgroupPoint>>> {
|
|
||||||
let window = PEDERSEN_HASH_EXP_WINDOW_SIZE;
|
|
||||||
|
|
||||||
PEDERSEN_HASH_GENERATORS
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|mut g| {
|
|
||||||
let mut tables = vec![];
|
|
||||||
|
|
||||||
let mut num_bits = 0;
|
|
||||||
while num_bits <= jubjub::Fr::NUM_BITS {
|
|
||||||
let mut table = Vec::with_capacity(1 << window);
|
|
||||||
let mut base = SubgroupPoint::identity();
|
|
||||||
|
|
||||||
for _ in 0..(1 << window) {
|
|
||||||
table.push(base);
|
|
||||||
base += g;
|
|
||||||
}
|
|
||||||
|
|
||||||
tables.push(table);
|
|
||||||
num_bits += window;
|
|
||||||
|
|
||||||
for _ in 0..window {
|
|
||||||
g = g.double();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tables
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use jubjub::SubgroupPoint;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::sapling::group_hash::group_hash;
|
|
||||||
|
|
||||||
fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> SubgroupPoint {
|
|
||||||
let mut tag = m.to_vec();
|
|
||||||
let i = tag.len();
|
|
||||||
tag.push(0u8);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let gh = group_hash(&tag, personalization);
|
|
||||||
|
|
||||||
// We don't want to overflow and start reusing generators
|
|
||||||
assert!(tag[i] != u8::max_value());
|
|
||||||
tag[i] += 1;
|
|
||||||
|
|
||||||
if let Some(gh) = gh {
|
|
||||||
break gh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn proof_generation_key_base_generator() {
|
|
||||||
assert_eq!(
|
|
||||||
find_group_hash(&[], PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION),
|
|
||||||
PROOF_GENERATION_KEY_GENERATOR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn note_commitment_randomness_generator() {
|
|
||||||
assert_eq!(
|
|
||||||
find_group_hash(b"r", PEDERSEN_HASH_GENERATORS_PERSONALIZATION),
|
|
||||||
NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nullifier_position_generator() {
|
|
||||||
assert_eq!(
|
|
||||||
find_group_hash(&[], NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION),
|
|
||||||
NULLIFIER_POSITION_GENERATOR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn value_commitment_value_generator() {
|
|
||||||
assert_eq!(
|
|
||||||
find_group_hash(b"v", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION),
|
|
||||||
VALUE_COMMITMENT_VALUE_GENERATOR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn value_commitment_randomness_generator() {
|
|
||||||
assert_eq!(
|
|
||||||
find_group_hash(b"r", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION),
|
|
||||||
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn spending_key_generator() {
|
|
||||||
assert_eq!(
|
|
||||||
find_group_hash(&[], SPENDING_KEY_GENERATOR_PERSONALIZATION),
|
|
||||||
SPENDING_KEY_GENERATOR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pedersen_hash_generators() {
|
|
||||||
for (m, actual) in PEDERSEN_HASH_GENERATORS.iter().enumerate() {
|
|
||||||
assert_eq!(
|
|
||||||
&find_group_hash(
|
|
||||||
&(m as u32).to_le_bytes(),
|
|
||||||
PEDERSEN_HASH_GENERATORS_PERSONALIZATION
|
|
||||||
),
|
|
||||||
actual
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_duplicate_fixed_base_generators() {
|
|
||||||
let fixed_base_generators = [
|
|
||||||
PROOF_GENERATION_KEY_GENERATOR,
|
|
||||||
NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
NULLIFIER_POSITION_GENERATOR,
|
|
||||||
VALUE_COMMITMENT_VALUE_GENERATOR,
|
|
||||||
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
SPENDING_KEY_GENERATOR,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Check for duplicates, far worse than spec inconsistencies!
|
|
||||||
for (i, p1) in fixed_base_generators.iter().enumerate() {
|
|
||||||
if p1.is_identity().into() {
|
|
||||||
panic!("Neutral element!");
|
|
||||||
}
|
|
||||||
|
|
||||||
for p2 in fixed_base_generators.iter().skip(i + 1) {
|
|
||||||
if p1 == p2 {
|
|
||||||
panic!("Duplicate generator!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check for simple relations between the generators, that make finding collisions easy;
|
|
||||||
/// far worse than spec inconsistencies!
|
|
||||||
fn check_consistency_of_pedersen_hash_generators(
|
|
||||||
pedersen_hash_generators: &[jubjub::SubgroupPoint],
|
|
||||||
) {
|
|
||||||
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
|
|
||||||
if p1.is_identity().into() {
|
|
||||||
panic!("Neutral element!");
|
|
||||||
}
|
|
||||||
for p2 in pedersen_hash_generators.iter().skip(i + 1) {
|
|
||||||
if p1 == p2 {
|
|
||||||
panic!("Duplicate generator!");
|
|
||||||
}
|
|
||||||
if *p1 == -p2 {
|
|
||||||
panic!("Inverse generator!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for a generator being the sum of any other two
|
|
||||||
for (j, p2) in pedersen_hash_generators.iter().enumerate() {
|
|
||||||
if j == i {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (k, p3) in pedersen_hash_generators.iter().enumerate() {
|
|
||||||
if k == j || k == i {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let sum = p2 + p3;
|
|
||||||
if sum == *p1 {
|
|
||||||
panic!("Linear relation between generators!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pedersen_hash_generators_consistency() {
|
|
||||||
check_consistency_of_pedersen_hash_generators(PEDERSEN_HASH_GENERATORS);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Linear relation between generators!")]
|
|
||||||
fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() {
|
|
||||||
let mut pedersen_hash_generators = PEDERSEN_HASH_GENERATORS.to_vec();
|
|
||||||
|
|
||||||
// Test for linear relation
|
|
||||||
pedersen_hash_generators.push(PEDERSEN_HASH_GENERATORS[0] + PEDERSEN_HASH_GENERATORS[1]);
|
|
||||||
|
|
||||||
check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
//! Implementation of [group hashing into Jubjub][grouphash].
|
|
||||||
//!
|
|
||||||
//! [grouphash]: https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub
|
|
||||||
|
|
||||||
use ff::PrimeField;
|
|
||||||
use group::{cofactor::CofactorGroup, Group, GroupEncoding};
|
|
||||||
|
|
||||||
use super::constants;
|
|
||||||
use blake2s_simd::Params;
|
|
||||||
|
|
||||||
/// Produces a random point in the Jubjub curve.
|
|
||||||
/// The point is guaranteed to be prime order
|
|
||||||
/// and not the identity.
|
|
||||||
#[allow(clippy::assertions_on_constants)]
|
|
||||||
pub fn group_hash(tag: &[u8], personalization: &[u8]) -> Option<jubjub::SubgroupPoint> {
|
|
||||||
assert_eq!(personalization.len(), 8);
|
|
||||||
|
|
||||||
// Check to see that scalar field is 255 bits
|
|
||||||
assert!(bls12_381::Scalar::NUM_BITS == 255);
|
|
||||||
|
|
||||||
let h = Params::new()
|
|
||||||
.hash_length(32)
|
|
||||||
.personal(personalization)
|
|
||||||
.to_state()
|
|
||||||
.update(constants::GH_FIRST_BLOCK)
|
|
||||||
.update(tag)
|
|
||||||
.finalize();
|
|
||||||
|
|
||||||
let p = jubjub::ExtendedPoint::from_bytes(h.as_array());
|
|
||||||
if p.is_some().into() {
|
|
||||||
// <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
|
|
||||||
// ExtendedPoint::mul_by_cofactor in the jubjub crate.
|
|
||||||
let p = CofactorGroup::clear_cofactor(&p.unwrap());
|
|
||||||
|
|
||||||
if p.is_identity().into() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(p)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,746 +0,0 @@
|
||||||
//! Sapling key components.
|
|
||||||
//!
|
|
||||||
//! Implements [section 4.2.2] of the Zcash Protocol Specification.
|
|
||||||
//!
|
|
||||||
//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
address::PaymentAddress,
|
|
||||||
constants::{self, PROOF_GENERATION_KEY_GENERATOR},
|
|
||||||
note_encryption::KDF_SAPLING_PERSONALIZATION,
|
|
||||||
spec::{
|
|
||||||
crh_ivk, diversify_hash, ka_sapling_agree, ka_sapling_agree_prepared,
|
|
||||||
ka_sapling_derive_public, ka_sapling_derive_public_subgroup_prepared, PreparedBase,
|
|
||||||
PreparedBaseSubgroup, PreparedScalar,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
|
|
||||||
use ff::{Field, PrimeField};
|
|
||||||
use group::{Curve, Group, GroupEncoding};
|
|
||||||
use redjubjub::SpendAuth;
|
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
|
||||||
use zcash_note_encryption::EphemeralKeyBytes;
|
|
||||||
use zcash_spec::PrfExpand;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use rand_core::RngCore;
|
|
||||||
|
|
||||||
/// Errors that can occur in the decoding of Sapling spending keys.
|
|
||||||
pub enum DecodingError {
|
|
||||||
/// The length of the byte slice provided for decoding was incorrect.
|
|
||||||
LengthInvalid { expected: usize, actual: usize },
|
|
||||||
/// Could not decode the `ask` bytes to a jubjub field element.
|
|
||||||
InvalidAsk,
|
|
||||||
/// Could not decode the `nsk` bytes to a jubjub field element.
|
|
||||||
InvalidNsk,
|
|
||||||
/// An extended spending key had an unsupported child index: either a non-hardened
|
|
||||||
/// index, or a non-zero index at depth 0.
|
|
||||||
UnsupportedChildIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A spend authorizing key, used to create spend authorization signatures.
|
|
||||||
///
|
|
||||||
/// $\mathsf{ask}$ as defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
|
|
||||||
///
|
|
||||||
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpendAuthorizingKey(redjubjub::SigningKey<SpendAuth>);
|
|
||||||
|
|
||||||
impl PartialEq for SpendAuthorizingKey {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
<[u8; 32]>::from(self.0)
|
|
||||||
.ct_eq(&<[u8; 32]>::from(other.0))
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for SpendAuthorizingKey {}
|
|
||||||
|
|
||||||
impl From<&SpendValidatingKey> for jubjub::ExtendedPoint {
|
|
||||||
fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint {
|
|
||||||
jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpendAuthorizingKey {
|
|
||||||
/// Derives ask from sk. Internal use only, does not enforce all constraints.
|
|
||||||
fn derive_inner(sk: &[u8]) -> jubjub::Scalar {
|
|
||||||
jubjub::Scalar::from_bytes_wide(&PrfExpand::SAPLING_ASK.with(sk))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a `SpendAuthorizingKey` from a raw scalar.
|
|
||||||
pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option<Self> {
|
|
||||||
if ask.is_zero().into() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(SpendAuthorizingKey(ask.to_bytes().try_into().unwrap()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives a `SpendAuthorizingKey` from a spending key.
|
|
||||||
fn from_spending_key(sk: &[u8]) -> Option<Self> {
|
|
||||||
Self::from_scalar(Self::derive_inner(sk))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a `SpendAuthorizingKey` from its encoded form.
|
|
||||||
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
|
||||||
<[u8; 32]>::try_from(bytes)
|
|
||||||
.ok()
|
|
||||||
.and_then(|b| {
|
|
||||||
// RedJubjub.Private permits the full set of Jubjub scalars including
|
|
||||||
// zero. However, a SpendAuthorizingKey is further restricted within the
|
|
||||||
// Sapling key tree to be a non-zero scalar.
|
|
||||||
jubjub::Scalar::from_repr(b)
|
|
||||||
.and_then(|s| {
|
|
||||||
CtOption::new(
|
|
||||||
redjubjub::SigningKey::try_from(b)
|
|
||||||
.expect("RedJubjub permits the set of valid SpendAuthorizingKeys"),
|
|
||||||
!s.is_zero(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.map(SpendAuthorizingKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this spend authorizing key to its serialized form.
|
|
||||||
pub(crate) fn to_bytes(&self) -> [u8; 32] {
|
|
||||||
<[u8; 32]>::from(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this spend authorizing key to a raw scalar.
|
|
||||||
///
|
|
||||||
/// Only used for ZIP 32 child derivation.
|
|
||||||
pub(crate) fn to_scalar(&self) -> jubjub::Scalar {
|
|
||||||
jubjub::Scalar::from_repr(self.0.into()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Randomizes this spend authorizing key with the given `randomizer`.
|
|
||||||
///
|
|
||||||
/// The resulting key can be used to actually sign a spend.
|
|
||||||
pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey<SpendAuth> {
|
|
||||||
self.0.randomize(randomizer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A key used to validate spend authorization signatures.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
|
|
||||||
///
|
|
||||||
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpendValidatingKey(redjubjub::VerificationKey<SpendAuth>);
|
|
||||||
|
|
||||||
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
|
||||||
fn from(ask: &SpendAuthorizingKey) -> Self {
|
|
||||||
SpendValidatingKey((&ask.0).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for SpendValidatingKey {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
<[u8; 32]>::from(self.0)
|
|
||||||
.ct_eq(&<[u8; 32]>::from(other.0))
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for SpendValidatingKey {}
|
|
||||||
|
|
||||||
impl SpendValidatingKey {
|
|
||||||
/// For circuit tests only.
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn fake_random<R: RngCore>(mut rng: R) -> Self {
|
|
||||||
loop {
|
|
||||||
if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) {
|
|
||||||
break k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only exposed for `zcashd` unit tests.
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
|
||||||
pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option<Self> {
|
|
||||||
Self::from_bytes(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a `SpendValidatingKey` from its encoded form.
|
|
||||||
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
|
||||||
<[u8; 32]>::try_from(bytes)
|
|
||||||
.ok()
|
|
||||||
.and_then(|b| {
|
|
||||||
// RedJubjub.Public permits the full set of Jubjub points including the
|
|
||||||
// identity and cofactors; this is the type used for `rk` in Spend
|
|
||||||
// descriptions. However, a SpendValidatingKey is further restricted
|
|
||||||
// within the Sapling key tree to be a non-identity element of the
|
|
||||||
// prime-order subgroup.
|
|
||||||
jubjub::SubgroupPoint::from_bytes(&b)
|
|
||||||
.and_then(|p| {
|
|
||||||
CtOption::new(
|
|
||||||
redjubjub::VerificationKey::try_from(b)
|
|
||||||
.expect("RedJubjub permits the set of valid SpendValidatingKeys"),
|
|
||||||
!p.is_identity(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.map(SpendValidatingKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this spend validating key to its serialized form,
|
|
||||||
/// `LEBS2OSP_256(repr_J(ak))`.
|
|
||||||
pub(crate) fn to_bytes(&self) -> [u8; 32] {
|
|
||||||
<[u8; 32]>::from(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Randomizes this spend validating key with the given `randomizer`.
|
|
||||||
pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey<SpendAuth> {
|
|
||||||
self.0.randomize(randomizer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An outgoing viewing key
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct OutgoingViewingKey(pub [u8; 32]);
|
|
||||||
|
|
||||||
/// A Sapling expanded spending key
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ExpandedSpendingKey {
|
|
||||||
pub ask: SpendAuthorizingKey,
|
|
||||||
pub nsk: jubjub::Fr,
|
|
||||||
pub ovk: OutgoingViewingKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ExpandedSpendingKey {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("ExpandedSpendingKey")
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExpandedSpendingKey {
|
|
||||||
/// Expands a spending key into its components.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if this spending key expands to `ask = 0`. This has a negligible
|
|
||||||
/// probability of occurring.
|
|
||||||
pub fn from_spending_key(sk: &[u8]) -> Self {
|
|
||||||
let ask =
|
|
||||||
SpendAuthorizingKey::from_spending_key(sk).expect("negligible chance of ask == 0");
|
|
||||||
let nsk = jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_NSK.with(sk));
|
|
||||||
let mut ovk = OutgoingViewingKey([0u8; 32]);
|
|
||||||
ovk.0
|
|
||||||
.copy_from_slice(&PrfExpand::SAPLING_OVK.with(sk)[..32]);
|
|
||||||
ExpandedSpendingKey { ask, nsk, ovk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn proof_generation_key(&self) -> ProofGenerationKey {
|
|
||||||
ProofGenerationKey {
|
|
||||||
ak: (&self.ask).into(),
|
|
||||||
nsk: self.nsk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes the expanded spending key from its serialized representation
|
|
||||||
/// as part of the encoding of the extended spending key as defined in
|
|
||||||
/// [ZIP 32](https://zips.z.cash/zip-0032)
|
|
||||||
pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
|
|
||||||
if b.len() != 96 {
|
|
||||||
return Err(DecodingError::LengthInvalid {
|
|
||||||
expected: 96,
|
|
||||||
actual: b.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?;
|
|
||||||
let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
|
|
||||||
.ok_or(DecodingError::InvalidNsk)?;
|
|
||||||
let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
|
|
||||||
|
|
||||||
Ok(ExpandedSpendingKey { ask, nsk, ovk })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
|
||||||
let mut repr = [0u8; 96];
|
|
||||||
reader.read_exact(repr.as_mut())?;
|
|
||||||
Self::from_bytes(&repr).map_err(|e| match e {
|
|
||||||
DecodingError::InvalidAsk => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidData, "ask not in field")
|
|
||||||
}
|
|
||||||
DecodingError::InvalidNsk => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidData, "nsk not in field")
|
|
||||||
}
|
|
||||||
DecodingError::LengthInvalid { .. } | DecodingError::UnsupportedChildIndex => {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
|
||||||
writer.write_all(&self.to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encodes the expanded spending key to the its seralized representation
|
|
||||||
/// as part of the encoding of the extended spending key as defined in
|
|
||||||
/// [ZIP 32](https://zips.z.cash/zip-0032)
|
|
||||||
pub fn to_bytes(&self) -> [u8; 96] {
|
|
||||||
let mut result = [0u8; 96];
|
|
||||||
result[0..32].copy_from_slice(&self.ask.to_bytes());
|
|
||||||
result[32..64].copy_from_slice(&self.nsk.to_repr());
|
|
||||||
result[64..96].copy_from_slice(&self.ovk.0);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ProofGenerationKey {
|
|
||||||
pub ak: SpendValidatingKey,
|
|
||||||
pub nsk: jubjub::Fr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ProofGenerationKey {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("ProofGenerationKey")
|
|
||||||
.field("ak", &self.ak)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProofGenerationKey {
|
|
||||||
pub fn to_viewing_key(&self) -> ViewingKey {
|
|
||||||
ViewingKey {
|
|
||||||
ak: self.ak.clone(),
|
|
||||||
nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A key used to derive the nullifier for a Sapling note.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ViewingKey {
|
|
||||||
pub ak: SpendValidatingKey,
|
|
||||||
pub nk: NullifierDerivingKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ViewingKey {
|
|
||||||
pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
|
|
||||||
self.ak.randomize(&ar)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ivk(&self) -> SaplingIvk {
|
|
||||||
SaplingIvk(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
|
|
||||||
self.ivk().to_payment_address(diversifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Sapling key that provides the capability to view incoming and outgoing transactions.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FullViewingKey {
|
|
||||||
pub vk: ViewingKey,
|
|
||||||
pub ovk: OutgoingViewingKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for FullViewingKey {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
FullViewingKey {
|
|
||||||
vk: ViewingKey {
|
|
||||||
ak: self.vk.ak.clone(),
|
|
||||||
nk: self.vk.nk,
|
|
||||||
},
|
|
||||||
ovk: self.ovk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FullViewingKey {
|
|
||||||
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
|
|
||||||
FullViewingKey {
|
|
||||||
vk: ViewingKey {
|
|
||||||
ak: (&expsk.ask).into(),
|
|
||||||
nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk),
|
|
||||||
},
|
|
||||||
ovk: expsk.ovk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
|
||||||
let ak = {
|
|
||||||
let mut buf = [0u8; 32];
|
|
||||||
reader.read_exact(&mut buf)?;
|
|
||||||
SpendValidatingKey::from_bytes(&buf)
|
|
||||||
};
|
|
||||||
let nk = {
|
|
||||||
let mut buf = [0u8; 32];
|
|
||||||
reader.read_exact(&mut buf)?;
|
|
||||||
jubjub::SubgroupPoint::from_bytes(&buf)
|
|
||||||
};
|
|
||||||
if ak.is_none() {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"ak not of prime order",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if nk.is_none().into() {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"nk not in prime-order subgroup",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let ak = ak.unwrap();
|
|
||||||
let nk = NullifierDerivingKey(nk.unwrap());
|
|
||||||
|
|
||||||
let mut ovk = [0u8; 32];
|
|
||||||
reader.read_exact(&mut ovk)?;
|
|
||||||
|
|
||||||
Ok(FullViewingKey {
|
|
||||||
vk: ViewingKey { ak, nk },
|
|
||||||
ovk: OutgoingViewingKey(ovk),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
|
||||||
writer.write_all(&self.vk.ak.to_bytes())?;
|
|
||||||
writer.write_all(&self.vk.nk.0.to_bytes())?;
|
|
||||||
writer.write_all(&self.ovk.0)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 96] {
|
|
||||||
let mut result = [0u8; 96];
|
|
||||||
self.write(&mut result[..])
|
|
||||||
.expect("should be able to serialize a FullViewingKey");
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SaplingIvk(pub jubjub::Fr);
|
|
||||||
|
|
||||||
impl SaplingIvk {
|
|
||||||
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
|
|
||||||
let prepared_ivk = PreparedIncomingViewingKey::new(self);
|
|
||||||
DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier)
|
|
||||||
.and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_repr(&self) -> [u8; 32] {
|
|
||||||
self.0.to_repr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Sapling incoming viewing key that has been precomputed for trial decryption.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PreparedIncomingViewingKey(PreparedScalar);
|
|
||||||
|
|
||||||
impl memuse::DynamicUsage for PreparedIncomingViewingKey {
|
|
||||||
fn dynamic_usage(&self) -> usize {
|
|
||||||
self.0.dynamic_usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
|
||||||
self.0.dynamic_usage_bounds()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PreparedIncomingViewingKey {
|
|
||||||
/// Performs the necessary precomputations to use a `SaplingIvk` for note decryption.
|
|
||||||
pub fn new(ivk: &SaplingIvk) -> Self {
|
|
||||||
Self(PreparedScalar::new(&ivk.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Diversifier(pub [u8; 11]);
|
|
||||||
|
|
||||||
impl Diversifier {
|
|
||||||
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
|
|
||||||
diversify_hash(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The diversified transmission key for a given payment address.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
|
|
||||||
///
|
|
||||||
/// Note that this type is allowed to be the identity in the protocol, but we reject this
|
|
||||||
/// in [`PaymentAddress::from_parts`].
|
|
||||||
///
|
|
||||||
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint);
|
|
||||||
|
|
||||||
impl DiversifiedTransmissionKey {
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
|
|
||||||
///
|
|
||||||
/// Returns `None` if `d` is an invalid diversifier.
|
|
||||||
///
|
|
||||||
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
|
|
||||||
pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option<Self> {
|
|
||||||
d.g_d()
|
|
||||||
.map(PreparedBaseSubgroup::new)
|
|
||||||
.map(|g_d| ka_sapling_derive_public_subgroup_prepared(&ivk.0, &g_d))
|
|
||||||
.map(DiversifiedTransmissionKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $abst_J(bytes)$
|
|
||||||
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
|
||||||
jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $repr_J(self)$
|
|
||||||
pub(crate) fn to_bytes(self) -> [u8; 32] {
|
|
||||||
self.0.to_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this is the identity.
|
|
||||||
pub(crate) fn is_identity(&self) -> bool {
|
|
||||||
self.0.is_identity().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exposes the inner Jubjub point.
|
|
||||||
///
|
|
||||||
/// This API is exposed for `zcash_proof` usage, and will be removed when this type is
|
|
||||||
/// refactored into the `sapling-crypto` crate.
|
|
||||||
pub fn inner(&self) -> jubjub::SubgroupPoint {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConditionallySelectable for DiversifiedTransmissionKey {
|
|
||||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
|
||||||
DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select(
|
|
||||||
&a.0, &b.0, choice,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An ephemeral secret key used to encrypt an output note on-chain.
|
|
||||||
///
|
|
||||||
/// `esk` is "ephemeral" in the sense that each secret key is only used once. In
|
|
||||||
/// practice, `esk` is derived deterministically from the note that it is encrypting.
|
|
||||||
///
|
|
||||||
/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{Private} := \mathbb{F}_{r_J}$
|
|
||||||
///
|
|
||||||
/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EphemeralSecretKey(pub(crate) jubjub::Scalar);
|
|
||||||
|
|
||||||
impl ConstantTimeEq for EphemeralSecretKey {
|
|
||||||
fn ct_eq(&self, other: &Self) -> subtle::Choice {
|
|
||||||
self.0.ct_eq(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EphemeralSecretKey {
|
|
||||||
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
|
||||||
jubjub::Scalar::from_bytes(bytes).map(EphemeralSecretKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn derive_public(&self, g_d: jubjub::ExtendedPoint) -> EphemeralPublicKey {
|
|
||||||
EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
|
|
||||||
SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An ephemeral public key used to encrypt an output note on-chain.
|
|
||||||
///
|
|
||||||
/// `epk` is "ephemeral" in the sense that each public key is only used once. In practice,
|
|
||||||
/// `epk` is derived deterministically from the note that it is encrypting.
|
|
||||||
///
|
|
||||||
/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{Public} := \mathbb{J}$
|
|
||||||
///
|
|
||||||
/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EphemeralPublicKey(jubjub::ExtendedPoint);
|
|
||||||
|
|
||||||
impl EphemeralPublicKey {
|
|
||||||
pub(crate) fn from_affine(epk: jubjub::AffinePoint) -> Self {
|
|
||||||
EphemeralPublicKey(epk.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
|
||||||
jubjub::ExtendedPoint::from_bytes(bytes).map(EphemeralPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
|
|
||||||
EphemeralKeyBytes(self.0.to_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Sapling ephemeral public key that has been precomputed for trial decryption.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PreparedEphemeralPublicKey(PreparedBase);
|
|
||||||
|
|
||||||
impl PreparedEphemeralPublicKey {
|
|
||||||
pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
|
|
||||||
PreparedEphemeralPublicKey(PreparedBase::new(epk.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
|
|
||||||
SharedSecret(ka_sapling_agree_prepared(&ivk.0, &self.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{SharedSecret} := \mathbb{J}^{(r)}$
|
|
||||||
///
|
|
||||||
/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SharedSecret(jubjub::SubgroupPoint);
|
|
||||||
|
|
||||||
impl SharedSecret {
|
|
||||||
/// For checking test vectors only.
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn to_bytes(&self) -> [u8; 32] {
|
|
||||||
self.0.to_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only for use in batched note encryption.
|
|
||||||
pub(crate) fn batch_to_affine(
|
|
||||||
shared_secrets: Vec<Option<Self>>,
|
|
||||||
) -> impl Iterator<Item = Option<jubjub::AffinePoint>> {
|
|
||||||
// Filter out the positions for which ephemeral_key was not a valid encoding.
|
|
||||||
let secrets: Vec<_> = shared_secrets
|
|
||||||
.iter()
|
|
||||||
.filter_map(|s| s.as_ref().map(|s| jubjub::ExtendedPoint::from(s.0)))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Batch-normalize the shared secrets.
|
|
||||||
let mut secrets_affine = vec![jubjub::AffinePoint::identity(); secrets.len()];
|
|
||||||
group::Curve::batch_normalize(&secrets, &mut secrets_affine);
|
|
||||||
|
|
||||||
// Re-insert the invalid ephemeral_key positions.
|
|
||||||
let mut secrets_affine = secrets_affine.into_iter();
|
|
||||||
shared_secrets
|
|
||||||
.into_iter()
|
|
||||||
.map(move |s| s.and_then(|_| secrets_affine.next()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.5.4: Sapling Key Agreement][concretesaplingkdf].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkdf]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkdf
|
|
||||||
pub(crate) fn kdf_sapling(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
|
|
||||||
Self::kdf_sapling_inner(
|
|
||||||
jubjub::ExtendedPoint::from(self.0).to_affine(),
|
|
||||||
ephemeral_key,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only for direct use in batched note encryption.
|
|
||||||
pub(crate) fn kdf_sapling_inner(
|
|
||||||
secret: jubjub::AffinePoint,
|
|
||||||
ephemeral_key: &EphemeralKeyBytes,
|
|
||||||
) -> Blake2bHash {
|
|
||||||
Blake2bParams::new()
|
|
||||||
.hash_length(32)
|
|
||||||
.personal(KDF_SAPLING_PERSONALIZATION)
|
|
||||||
.to_state()
|
|
||||||
.update(&secret.to_bytes())
|
|
||||||
.update(ephemeral_key.as_ref())
|
|
||||||
.finalize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub mod testing {
|
|
||||||
use proptest::collection::vec;
|
|
||||||
use proptest::prelude::*;
|
|
||||||
|
|
||||||
use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk};
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
pub fn arb_expanded_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExpandedSpendingKey {
|
|
||||||
ExpandedSpendingKey::from_spending_key(&v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
pub fn arb_full_viewing_key()(sk in arb_expanded_spending_key()) -> FullViewingKey {
|
|
||||||
FullViewingKey::from_expanded_spending_key(&sk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
|
|
||||||
fvk.vk.ivk()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use group::{Group, GroupEncoding};
|
|
||||||
|
|
||||||
use super::{FullViewingKey, SpendAuthorizingKey, SpendValidatingKey};
|
|
||||||
use crate::sapling::{constants::SPENDING_KEY_GENERATOR, test_vectors};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ak_must_be_prime_order() {
|
|
||||||
let mut buf = [0; 96];
|
|
||||||
let identity = jubjub::SubgroupPoint::identity();
|
|
||||||
|
|
||||||
// Set both ak and nk to the identity.
|
|
||||||
buf[0..32].copy_from_slice(&identity.to_bytes());
|
|
||||||
buf[32..64].copy_from_slice(&identity.to_bytes());
|
|
||||||
|
|
||||||
// ak is not allowed to be the identity.
|
|
||||||
assert_eq!(
|
|
||||||
FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
|
|
||||||
"ak not of prime order"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set ak to a basepoint.
|
|
||||||
let basepoint = SPENDING_KEY_GENERATOR;
|
|
||||||
buf[0..32].copy_from_slice(&basepoint.to_bytes());
|
|
||||||
|
|
||||||
// nk is allowed to be the identity.
|
|
||||||
assert!(FullViewingKey::read(&buf[..]).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn spend_auth_sig_test_vectors() {
|
|
||||||
for tv in test_vectors::signatures::make_test_vectors() {
|
|
||||||
let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap();
|
|
||||||
let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap();
|
|
||||||
let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap();
|
|
||||||
let sig = redjubjub::Signature::from(tv.sig);
|
|
||||||
let rsig = redjubjub::Signature::from(tv.rsig);
|
|
||||||
|
|
||||||
let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(<[u8; 32]>::from(sk.randomize(&alpha)), tv.rsk);
|
|
||||||
assert_eq!(vk.randomize(&alpha), rvk);
|
|
||||||
|
|
||||||
// assert_eq!(vk.0.verify(&tv.m, &sig), Ok(()));
|
|
||||||
// assert_eq!(rvk.verify(&tv.m, &rsig), Ok(()));
|
|
||||||
assert_eq!(
|
|
||||||
vk.0.verify(&tv.m, &rsig),
|
|
||||||
Err(redjubjub::Error::InvalidSignature),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
rvk.verify(&tv.m, &sig),
|
|
||||||
Err(redjubjub::Error::InvalidSignature),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,184 +0,0 @@
|
||||||
use group::{ff::Field, GroupEncoding};
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
use zcash_spec::PrfExpand;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
keys::EphemeralSecretKey, value::NoteValue, Nullifier, NullifierDerivingKey, PaymentAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod commitment;
|
|
||||||
pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
|
|
||||||
|
|
||||||
pub(super) mod nullifier;
|
|
||||||
|
|
||||||
/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212).
|
|
||||||
///
|
|
||||||
/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value.
|
|
||||||
/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive
|
|
||||||
/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`.
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Rseed {
|
|
||||||
BeforeZip212(jubjub::Fr),
|
|
||||||
AfterZip212([u8; 32]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rseed {
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.7.2: Sending Notes (Sapling)][saplingsend].
|
|
||||||
///
|
|
||||||
/// [saplingsend]: https://zips.z.cash/protocol/protocol.pdf#saplingsend
|
|
||||||
pub(crate) fn rcm(&self) -> commitment::NoteCommitTrapdoor {
|
|
||||||
commitment::NoteCommitTrapdoor(match self {
|
|
||||||
Rseed::BeforeZip212(rcm) => *rcm,
|
|
||||||
Rseed::AfterZip212(rseed) => {
|
|
||||||
jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_RCM.with(rseed))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A discrete amount of funds received by an address.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Note {
|
|
||||||
/// The recipient of the funds.
|
|
||||||
recipient: PaymentAddress,
|
|
||||||
/// The value of this note.
|
|
||||||
value: NoteValue,
|
|
||||||
/// The seed randomness for various note components.
|
|
||||||
rseed: Rseed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Note {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
// Notes are canonically defined by their commitments.
|
|
||||||
self.cmu().eq(&other.cmu())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Note {}
|
|
||||||
|
|
||||||
impl Note {
|
|
||||||
/// Creates a note from its component parts.
|
|
||||||
///
|
|
||||||
/// # Caveats
|
|
||||||
///
|
|
||||||
/// This low-level constructor enforces that the provided arguments produce an
|
|
||||||
/// internally valid `Note`. However, it allows notes to be constructed in a way that
|
|
||||||
/// violates required security checks for note decryption, as specified in
|
|
||||||
/// [Section 4.19] of the Zcash Protocol Specification. Users of this constructor
|
|
||||||
/// should only call it with note components that have been fully validated by
|
|
||||||
/// decrypting a received note according to [Section 4.19].
|
|
||||||
///
|
|
||||||
/// [Section 4.19]: https://zips.z.cash/protocol/protocol.pdf#saplingandorchardinband
|
|
||||||
pub fn from_parts(recipient: PaymentAddress, value: NoteValue, rseed: Rseed) -> Self {
|
|
||||||
Note {
|
|
||||||
recipient,
|
|
||||||
value,
|
|
||||||
rseed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the recipient of this note.
|
|
||||||
pub fn recipient(&self) -> PaymentAddress {
|
|
||||||
self.recipient
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the value of this note.
|
|
||||||
pub fn value(&self) -> NoteValue {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the rseed value of this note.
|
|
||||||
pub fn rseed(&self) -> &Rseed {
|
|
||||||
&self.rseed
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the note commitment, returning the full point.
|
|
||||||
fn cm_full_point(&self) -> NoteCommitment {
|
|
||||||
NoteCommitment::derive(
|
|
||||||
self.recipient.g_d().to_bytes(),
|
|
||||||
self.recipient.pk_d().to_bytes(),
|
|
||||||
self.value,
|
|
||||||
self.rseed.rcm(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the nullifier given the nullifier deriving key and
|
|
||||||
/// note position
|
|
||||||
pub fn nf(&self, nk: &NullifierDerivingKey, position: u64) -> Nullifier {
|
|
||||||
Nullifier::derive(nk, self.cm_full_point(), position)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the note commitment
|
|
||||||
pub fn cmu(&self) -> ExtractedNoteCommitment {
|
|
||||||
self.cm_full_point().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.7.2: Sending Notes (Sapling)][saplingsend].
|
|
||||||
///
|
|
||||||
/// [saplingsend]: https://zips.z.cash/protocol/protocol.pdf#saplingsend
|
|
||||||
pub fn rcm(&self) -> jubjub::Fr {
|
|
||||||
self.rseed.rcm().0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives `esk` from the internal `Rseed` value, or generates a random value if this
|
|
||||||
/// note was created with a v1 (i.e. pre-ZIP 212) note plaintext.
|
|
||||||
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(
|
|
||||||
&self,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> EphemeralSecretKey {
|
|
||||||
self.generate_or_derive_esk_internal(rng)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(
|
|
||||||
&self,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> EphemeralSecretKey {
|
|
||||||
match self.derive_esk() {
|
|
||||||
None => EphemeralSecretKey(jubjub::Fr::random(rng)),
|
|
||||||
Some(esk) => esk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the derived `esk` if this note was created after ZIP 212 activated.
|
|
||||||
pub(crate) fn derive_esk(&self) -> Option<EphemeralSecretKey> {
|
|
||||||
match self.rseed {
|
|
||||||
Rseed::BeforeZip212(_) => None,
|
|
||||||
Rseed::AfterZip212(rseed) => Some(EphemeralSecretKey(jubjub::Fr::from_bytes_wide(
|
|
||||||
&PrfExpand::SAPLING_ESK.with(&rseed),
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub(super) mod testing {
|
|
||||||
use proptest::{collection::vec, prelude::*};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
super::{testing::arb_payment_address, value::NoteValue},
|
|
||||||
ExtractedNoteCommitment, Note, Rseed,
|
|
||||||
};
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
pub fn arb_note(value: NoteValue)(
|
|
||||||
recipient in arb_payment_address(),
|
|
||||||
rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212)
|
|
||||||
) -> Note {
|
|
||||||
Note {
|
|
||||||
recipient,
|
|
||||||
value,
|
|
||||||
rseed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
pub(crate) fn arb_cmu()(
|
|
||||||
cmu in vec(any::<u8>(), 64)
|
|
||||||
.prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
|
|
||||||
.prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
|
|
||||||
) -> ExtractedNoteCommitment {
|
|
||||||
ExtractedNoteCommitment(cmu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
use core::iter;
|
|
||||||
|
|
||||||
use bitvec::{array::BitArray, order::Lsb0};
|
|
||||||
use group::ff::PrimeField;
|
|
||||||
use subtle::{ConstantTimeEq, CtOption};
|
|
||||||
|
|
||||||
use crate::sapling::{
|
|
||||||
pedersen_hash::Personalization,
|
|
||||||
spec::{extract_p, windowed_pedersen_commit},
|
|
||||||
value::NoteValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The trapdoor for a Sapling note commitment.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) struct NoteCommitTrapdoor(pub(super) jubjub::Fr);
|
|
||||||
|
|
||||||
/// A commitment to a note.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct NoteCommitment(jubjub::SubgroupPoint);
|
|
||||||
|
|
||||||
impl NoteCommitment {
|
|
||||||
pub(crate) fn inner(&self) -> jubjub::SubgroupPoint {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NoteCommitment {
|
|
||||||
/// Derives a Sapling note commitment.
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
|
||||||
pub fn temporary_zcashd_derive(
|
|
||||||
g_d: [u8; 32],
|
|
||||||
pk_d: [u8; 32],
|
|
||||||
v: NoteValue,
|
|
||||||
rcm: jubjub::Fr,
|
|
||||||
) -> Self {
|
|
||||||
Self::derive(g_d, pk_d, v, NoteCommitTrapdoor(rcm))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $NoteCommit^Sapling$.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.8.2: Windowed Pedersen commitments][concretewindowedcommit].
|
|
||||||
///
|
|
||||||
/// [concretewindowedcommit]: https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
|
|
||||||
pub(super) fn derive(
|
|
||||||
g_d: [u8; 32],
|
|
||||||
pk_d: [u8; 32],
|
|
||||||
v: NoteValue,
|
|
||||||
rcm: NoteCommitTrapdoor,
|
|
||||||
) -> Self {
|
|
||||||
NoteCommitment(windowed_pedersen_commit(
|
|
||||||
Personalization::NoteCommitment,
|
|
||||||
iter::empty()
|
|
||||||
.chain(v.to_le_bits().iter().by_vals())
|
|
||||||
.chain(BitArray::<_, Lsb0>::new(g_d).iter().by_vals())
|
|
||||||
.chain(BitArray::<_, Lsb0>::new(pk_d).iter().by_vals()),
|
|
||||||
rcm.0,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The u-coordinate of the commitment to a note.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct ExtractedNoteCommitment(pub(super) bls12_381::Scalar);
|
|
||||||
|
|
||||||
impl ExtractedNoteCommitment {
|
|
||||||
/// Deserialize the extracted note commitment from a byte array.
|
|
||||||
///
|
|
||||||
/// This method enforces the [consensus rule][cmucanon] that the byte representation
|
|
||||||
/// of cmu MUST be canonical.
|
|
||||||
///
|
|
||||||
/// [cmucanon]: https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus
|
|
||||||
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
|
||||||
bls12_381::Scalar::from_repr(*bytes).map(ExtractedNoteCommitment)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize the value commitment to its canonical byte representation.
|
|
||||||
pub fn to_bytes(self) -> [u8; 32] {
|
|
||||||
self.0.to_repr()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn inner(&self) -> jubjub::Base {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NoteCommitment> for ExtractedNoteCommitment {
|
|
||||||
fn from(cm: NoteCommitment) -> Self {
|
|
||||||
ExtractedNoteCommitment(extract_p(&cm.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&ExtractedNoteCommitment> for [u8; 32] {
|
|
||||||
fn from(cmu: &ExtractedNoteCommitment) -> Self {
|
|
||||||
cmu.to_bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConstantTimeEq for ExtractedNoteCommitment {
|
|
||||||
fn ct_eq(&self, other: &Self) -> subtle::Choice {
|
|
||||||
self.0.ct_eq(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for ExtractedNoteCommitment {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.ct_eq(other).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for ExtractedNoteCommitment {}
|
|
|
@ -1,54 +0,0 @@
|
||||||
use std::array::TryFromSliceError;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use subtle::{Choice, ConstantTimeEq};
|
|
||||||
|
|
||||||
use super::NoteCommitment;
|
|
||||||
use crate::sapling::{
|
|
||||||
keys::NullifierDerivingKey,
|
|
||||||
spec::{mixing_pedersen_hash, prf_nf},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Typesafe wrapper for nullifier values.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub struct Nullifier(pub [u8; 32]);
|
|
||||||
|
|
||||||
impl fmt::Debug for Nullifier {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("Nullifier")
|
|
||||||
.field(&hex::encode(self.0))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Nullifier {
|
|
||||||
pub fn from_slice(bytes: &[u8]) -> Result<Nullifier, TryFromSliceError> {
|
|
||||||
bytes.try_into().map(Nullifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_vec(&self) -> Vec<u8> {
|
|
||||||
self.0.to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $DeriveNullifier$.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers].
|
|
||||||
///
|
|
||||||
/// [commitmentsandnullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers
|
|
||||||
pub(super) fn derive(nk: &NullifierDerivingKey, cm: NoteCommitment, position: u64) -> Self {
|
|
||||||
let rho = mixing_pedersen_hash(cm.inner(), position);
|
|
||||||
Nullifier(prf_nf(&nk.0, &rho))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for Nullifier {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConstantTimeEq for Nullifier {
|
|
||||||
fn ct_eq(&self, other: &Self) -> Choice {
|
|
||||||
self.0.ct_eq(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,160 +0,0 @@
|
||||||
//! Implementation of the Pedersen hash function used in Sapling.
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) mod test_vectors;
|
|
||||||
|
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
|
||||||
use ff::PrimeField;
|
|
||||||
use group::Group;
|
|
||||||
use std::ops::{AddAssign, Neg};
|
|
||||||
|
|
||||||
use super::constants::{
|
|
||||||
PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_EXP_TABLE, PEDERSEN_HASH_EXP_WINDOW_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum Personalization {
|
|
||||||
NoteCommitment,
|
|
||||||
MerkleTree(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Personalization {
|
|
||||||
pub fn get_bits(&self) -> Vec<bool> {
|
|
||||||
match *self {
|
|
||||||
Personalization::NoteCommitment => vec![true, true, true, true, true, true],
|
|
||||||
Personalization::MerkleTree(num) => {
|
|
||||||
assert!(num < 63);
|
|
||||||
|
|
||||||
(0..6).map(|i| (num >> i) & 1 == 1).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pedersen_hash<I>(personalization: Personalization, bits: I) -> jubjub::SubgroupPoint
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = bool>,
|
|
||||||
{
|
|
||||||
let mut bits = personalization
|
|
||||||
.get_bits()
|
|
||||||
.into_iter()
|
|
||||||
.chain(bits.into_iter());
|
|
||||||
|
|
||||||
let mut result = jubjub::SubgroupPoint::identity();
|
|
||||||
let mut generators = PEDERSEN_HASH_EXP_TABLE.iter();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut acc = jubjub::Fr::zero();
|
|
||||||
let mut cur = jubjub::Fr::one();
|
|
||||||
let mut chunks_remaining = PEDERSEN_HASH_CHUNKS_PER_GENERATOR;
|
|
||||||
let mut encountered_bits = false;
|
|
||||||
|
|
||||||
// Grab three bits from the input
|
|
||||||
while let Some(a) = bits.next() {
|
|
||||||
encountered_bits = true;
|
|
||||||
|
|
||||||
let b = bits.next().unwrap_or(false);
|
|
||||||
let c = bits.next().unwrap_or(false);
|
|
||||||
|
|
||||||
// Start computing this portion of the scalar
|
|
||||||
let mut tmp = cur;
|
|
||||||
if a {
|
|
||||||
tmp.add_assign(&cur);
|
|
||||||
}
|
|
||||||
cur = cur.double(); // 2^1 * cur
|
|
||||||
if b {
|
|
||||||
tmp.add_assign(&cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
// conditionally negate
|
|
||||||
if c {
|
|
||||||
tmp = tmp.neg();
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.add_assign(&tmp);
|
|
||||||
|
|
||||||
chunks_remaining -= 1;
|
|
||||||
|
|
||||||
if chunks_remaining == 0 {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
cur = cur.double().double().double(); // 2^4 * cur
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !encountered_bits {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut table: &[Vec<jubjub::SubgroupPoint>] =
|
|
||||||
generators.next().expect("we don't have enough generators");
|
|
||||||
let window = PEDERSEN_HASH_EXP_WINDOW_SIZE as usize;
|
|
||||||
let window_mask = (1u64 << window) - 1;
|
|
||||||
|
|
||||||
let acc = acc.to_repr();
|
|
||||||
let num_limbs: usize = acc.as_ref().len() / 8;
|
|
||||||
let mut limbs = vec![0u64; num_limbs + 1];
|
|
||||||
LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]);
|
|
||||||
|
|
||||||
let mut tmp = jubjub::SubgroupPoint::identity();
|
|
||||||
|
|
||||||
let mut pos = 0;
|
|
||||||
while pos < jubjub::Fr::NUM_BITS as usize {
|
|
||||||
let u64_idx = pos / 64;
|
|
||||||
let bit_idx = pos % 64;
|
|
||||||
let i = (if bit_idx + window < 64 {
|
|
||||||
// This window's bits are contained in a single u64.
|
|
||||||
limbs[u64_idx] >> bit_idx
|
|
||||||
} else {
|
|
||||||
// Combine the current u64's bits with the bits from the next u64.
|
|
||||||
(limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx))
|
|
||||||
} & window_mask) as usize;
|
|
||||||
|
|
||||||
tmp += table[0][i];
|
|
||||||
|
|
||||||
pos += window;
|
|
||||||
table = &table[1..];
|
|
||||||
}
|
|
||||||
|
|
||||||
result += tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod test {
|
|
||||||
use group::Curve;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub struct TestVector<'a> {
|
|
||||||
pub personalization: Personalization,
|
|
||||||
pub input_bits: Vec<u8>,
|
|
||||||
pub hash_u: &'a str,
|
|
||||||
pub hash_v: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pedersen_hash_points() {
|
|
||||||
let test_vectors = test_vectors::get_vectors();
|
|
||||||
|
|
||||||
assert!(!test_vectors.is_empty());
|
|
||||||
|
|
||||||
for v in test_vectors.iter() {
|
|
||||||
let input_bools: Vec<bool> = v.input_bits.iter().map(|&i| i == 1).collect();
|
|
||||||
|
|
||||||
// The 6 bits prefix is handled separately
|
|
||||||
assert_eq!(v.personalization.get_bits(), &input_bools[..6]);
|
|
||||||
|
|
||||||
let p = jubjub::ExtendedPoint::from(pedersen_hash(
|
|
||||||
v.personalization,
|
|
||||||
input_bools.into_iter().skip(6),
|
|
||||||
))
|
|
||||||
.to_affine();
|
|
||||||
|
|
||||||
assert_eq!(p.get_u().to_string(), v.hash_u);
|
|
||||||
assert_eq!(p.get_v().to_string(), v.hash_v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,715 +0,0 @@
|
||||||
//! Test vectors from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_pedersen.py
|
|
||||||
|
|
||||||
use super::{test::TestVector, Personalization};
|
|
||||||
|
|
||||||
pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
|
|
||||||
vec![
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![1, 1, 1, 1, 1, 1],
|
|
||||||
hash_u: "0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b",
|
|
||||||
hash_v: "0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![1, 1, 1, 1, 1, 1, 0],
|
|
||||||
hash_u: "0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a",
|
|
||||||
hash_v: "0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![1, 1, 1, 1, 1, 1, 1],
|
|
||||||
hash_u: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97",
|
|
||||||
hash_v: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
||||||
hash_u: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97",
|
|
||||||
hash_v: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1,
|
|
||||||
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1,
|
|
||||||
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
|
|
||||||
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f",
|
|
||||||
hash_v: "0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
|
|
||||||
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0,
|
|
||||||
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
|
|
||||||
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
|
||||||
0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd",
|
|
||||||
hash_v: "0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40",
|
|
||||||
hash_v: "0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0,
|
|
||||||
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1,
|
|
||||||
1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1,
|
|
||||||
1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0,
|
|
||||||
0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
|
|
||||||
1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1,
|
|
||||||
0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
|
|
||||||
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1,
|
|
||||||
1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
|
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
|
|
||||||
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27",
|
|
||||||
hash_v: "0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
|
|
||||||
0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
|
|
||||||
0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0,
|
|
||||||
1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
|
|
||||||
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0,
|
|
||||||
1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0,
|
|
||||||
1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
|
|
||||||
1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
|
|
||||||
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
|
|
||||||
0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
|
|
||||||
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
hash_u: "0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e",
|
|
||||||
hash_v: "0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
||||||
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0,
|
|
||||||
0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
|
|
||||||
1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
|
|
||||||
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
|
|
||||||
1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0,
|
|
||||||
1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1,
|
|
||||||
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1,
|
|
||||||
1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1,
|
|
||||||
0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1,
|
|
||||||
1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
||||||
1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
|
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1,
|
|
||||||
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0,
|
|
||||||
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f",
|
|
||||||
hash_v: "0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1,
|
|
||||||
0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
||||||
1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
|
|
||||||
1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
|
|
||||||
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0,
|
|
||||||
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1,
|
|
||||||
1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1,
|
|
||||||
0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1,
|
|
||||||
1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1,
|
|
||||||
1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
|
|
||||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7",
|
|
||||||
hash_v: "0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![0, 0, 0, 0, 0, 0],
|
|
||||||
hash_u: "0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d",
|
|
||||||
hash_v: "0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![0, 0, 0, 0, 0, 0, 0],
|
|
||||||
hash_u: "0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea",
|
|
||||||
hash_v: "0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![0, 0, 0, 0, 0, 0, 1],
|
|
||||||
hash_u: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d",
|
|
||||||
hash_v: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
||||||
hash_u: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d",
|
|
||||||
hash_v: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
|
|
||||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1,
|
|
||||||
0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1,
|
|
||||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd",
|
|
||||||
hash_v: "0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
|
|
||||||
1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555",
|
|
||||||
hash_v: "0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1,
|
|
||||||
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
|
|
||||||
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1,
|
|
||||||
1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
|
|
||||||
0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d",
|
|
||||||
hash_v: "0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1,
|
|
||||||
0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
|
|
||||||
1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1,
|
|
||||||
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
|
|
||||||
0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0,
|
|
||||||
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,
|
|
||||||
0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
|
|
||||||
0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85",
|
|
||||||
hash_v: "0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
|
|
||||||
1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1,
|
|
||||||
1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
|
|
||||||
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1,
|
|
||||||
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1,
|
|
||||||
1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
hash_u: "0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68",
|
|
||||||
hash_v: "0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
|
||||||
0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0,
|
|
||||||
0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1,
|
|
||||||
1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,
|
|
||||||
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1,
|
|
||||||
1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
|
|
||||||
1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
|
||||||
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
|
|
||||||
0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
|
|
||||||
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1,
|
|
||||||
1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1,
|
|
||||||
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1,
|
|
||||||
0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0,
|
|
||||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
|
|
||||||
0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37",
|
|
||||||
hash_v: "0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1,
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0,
|
|
||||||
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
|
|
||||||
0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0,
|
|
||||||
1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1,
|
|
||||||
0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
|
|
||||||
1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05",
|
|
||||||
hash_v: "0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![0, 1, 0, 0, 0, 1],
|
|
||||||
hash_u: "0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a",
|
|
||||||
hash_v: "0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![0, 1, 0, 0, 0, 1, 0],
|
|
||||||
hash_u: "0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c",
|
|
||||||
hash_v: "0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![0, 1, 0, 0, 0, 1, 1],
|
|
||||||
hash_u: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc",
|
|
||||||
hash_v: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0, 0],
|
|
||||||
hash_u: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc",
|
|
||||||
hash_v: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1,
|
|
||||||
1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0,
|
|
||||||
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2",
|
|
||||||
hash_v: "0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
|
|
||||||
1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb",
|
|
||||||
hash_v: "0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
|
|
||||||
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
|
|
||||||
1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2",
|
|
||||||
hash_v: "0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
||||||
1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1,
|
|
||||||
1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0,
|
|
||||||
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1,
|
|
||||||
1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1,
|
|
||||||
1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0,
|
|
||||||
1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
|
|
||||||
1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1,
|
|
||||||
1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0,
|
|
||||||
1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
|
||||||
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
|
|
||||||
1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1,
|
|
||||||
0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc",
|
|
||||||
hash_v: "0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1,
|
|
||||||
1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
|
|
||||||
1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
|
|
||||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
|
|
||||||
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1,
|
|
||||||
1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
|
|
||||||
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0,
|
|
||||||
1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1,
|
|
||||||
0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1,
|
|
||||||
1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
|
|
||||||
1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
||||||
1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
hash_u: "0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda",
|
|
||||||
hash_v: "0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
|
|
||||||
1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0,
|
|
||||||
0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0,
|
|
||||||
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
|
|
||||||
1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
|
|
||||||
0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1,
|
|
||||||
1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0,
|
|
||||||
1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0,
|
|
||||||
0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0,
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
|
|
||||||
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666",
|
|
||||||
hash_v: "0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(34),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1,
|
|
||||||
0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0,
|
|
||||||
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
|
|
||||||
0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0,
|
|
||||||
1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
|
|
||||||
0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,
|
|
||||||
0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
|
|
||||||
1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1,
|
|
||||||
1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0,
|
|
||||||
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1,
|
|
||||||
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1,
|
|
||||||
1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
|
|
||||||
0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
|
||||||
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
|
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1,
|
|
||||||
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7",
|
|
||||||
hash_v: "0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(27),
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
|
|
||||||
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
|
|
||||||
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
|
|
||||||
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
|
|
||||||
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4",
|
|
||||||
hash_v: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(36),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024",
|
|
||||||
hash_v: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::MerkleTree(0),
|
|
||||||
input_bits: vec![
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
],
|
|
||||||
hash_u: "0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd",
|
|
||||||
hash_v: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1",
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
personalization: Personalization::NoteCommitment,
|
|
||||||
input_bits: vec![
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
],
|
|
||||||
hash_u: "0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d",
|
|
||||||
hash_v: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,279 +0,0 @@
|
||||||
//! Abstractions over the proving system and parameters.
|
|
||||||
|
|
||||||
use bellman::groth16::{create_random_proof, Proof};
|
|
||||||
use bls12_381::Bls12;
|
|
||||||
use rand_core::RngCore;
|
|
||||||
|
|
||||||
use crate::sapling::{
|
|
||||||
self,
|
|
||||||
bundle::GrothProofBytes,
|
|
||||||
circuit::GROTH_PROOF_SIZE,
|
|
||||||
value::{NoteValue, ValueCommitTrapdoor},
|
|
||||||
MerklePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
circuit::{Output, OutputParameters, Spend, SpendParameters, ValueCommitmentOpening},
|
|
||||||
Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Interface for creating Sapling Spend proofs.
|
|
||||||
pub trait SpendProver {
|
|
||||||
/// The proof type created by this prover.
|
|
||||||
type Proof;
|
|
||||||
|
|
||||||
/// Prepares an instance of the Sapling Spend circuit for the given inputs.
|
|
||||||
///
|
|
||||||
/// Returns `None` if `diversifier` is not a valid Sapling diversifier.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn prepare_circuit(
|
|
||||||
proof_generation_key: ProofGenerationKey,
|
|
||||||
diversifier: Diversifier,
|
|
||||||
rseed: Rseed,
|
|
||||||
value: NoteValue,
|
|
||||||
alpha: jubjub::Fr,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
merkle_path: MerklePath,
|
|
||||||
) -> Option<sapling::circuit::Spend>;
|
|
||||||
|
|
||||||
/// Create the proof for a Sapling [`SpendDescription`].
|
|
||||||
///
|
|
||||||
/// [`SpendDescription`]: crate::transaction::components::SpendDescription
|
|
||||||
fn create_proof<R: RngCore>(
|
|
||||||
&self,
|
|
||||||
circuit: sapling::circuit::Spend,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Self::Proof;
|
|
||||||
|
|
||||||
/// Encodes the given Sapling [`SpendDescription`] proof, erasing its type.
|
|
||||||
///
|
|
||||||
/// [`SpendDescription`]: crate::transaction::components::SpendDescription
|
|
||||||
fn encode_proof(proof: Self::Proof) -> GrothProofBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interface for creating Sapling Output proofs.
|
|
||||||
pub trait OutputProver {
|
|
||||||
/// The proof type created by this prover.
|
|
||||||
type Proof;
|
|
||||||
|
|
||||||
/// Prepares an instance of the Sapling Output circuit for the given inputs.
|
|
||||||
///
|
|
||||||
/// Returns `None` if `diversifier` is not a valid Sapling diversifier.
|
|
||||||
fn prepare_circuit(
|
|
||||||
esk: jubjub::Fr,
|
|
||||||
payment_address: PaymentAddress,
|
|
||||||
rcm: jubjub::Fr,
|
|
||||||
value: NoteValue,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
) -> sapling::circuit::Output;
|
|
||||||
|
|
||||||
/// Create the proof for a Sapling [`OutputDescription`].
|
|
||||||
///
|
|
||||||
/// [`OutputDescription`]: crate::transaction::components::OutputDescription
|
|
||||||
fn create_proof<R: RngCore>(
|
|
||||||
&self,
|
|
||||||
circuit: sapling::circuit::Output,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Self::Proof;
|
|
||||||
|
|
||||||
/// Encodes the given Sapling [`OutputDescription`] proof, erasing its type.
|
|
||||||
///
|
|
||||||
/// [`OutputDescription`]: crate::transaction::components::OutputDescription
|
|
||||||
fn encode_proof(proof: Self::Proof) -> GrothProofBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpendProver for SpendParameters {
|
|
||||||
type Proof = Proof<Bls12>;
|
|
||||||
|
|
||||||
fn prepare_circuit(
|
|
||||||
proof_generation_key: ProofGenerationKey,
|
|
||||||
diversifier: Diversifier,
|
|
||||||
rseed: Rseed,
|
|
||||||
value: NoteValue,
|
|
||||||
alpha: jubjub::Fr,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
merkle_path: MerklePath,
|
|
||||||
) -> Option<Spend> {
|
|
||||||
// Construct the value commitment
|
|
||||||
let value_commitment_opening = ValueCommitmentOpening {
|
|
||||||
value,
|
|
||||||
randomness: rcv.inner(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Construct the viewing key
|
|
||||||
let viewing_key = proof_generation_key.to_viewing_key();
|
|
||||||
|
|
||||||
// Construct the payment address with the viewing key / diversifier
|
|
||||||
let payment_address = viewing_key.to_payment_address(diversifier)?;
|
|
||||||
|
|
||||||
let note = Note::from_parts(payment_address, value, rseed);
|
|
||||||
|
|
||||||
// We now have the full witness for our circuit
|
|
||||||
let pos: u64 = merkle_path.position().into();
|
|
||||||
Some(Spend {
|
|
||||||
value_commitment_opening: Some(value_commitment_opening),
|
|
||||||
proof_generation_key: Some(proof_generation_key),
|
|
||||||
payment_address: Some(payment_address),
|
|
||||||
commitment_randomness: Some(note.rcm()),
|
|
||||||
ar: Some(alpha),
|
|
||||||
auth_path: merkle_path
|
|
||||||
.path_elems()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, node)| Some(((*node).into(), pos >> i & 0x1 == 1)))
|
|
||||||
.collect(),
|
|
||||||
anchor: Some(anchor),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_proof<R: RngCore>(&self, circuit: Spend, rng: &mut R) -> Self::Proof {
|
|
||||||
create_random_proof(circuit, &self.0, rng).expect("proving should not fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
|
|
||||||
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
|
|
||||||
proof
|
|
||||||
.write(&mut zkproof[..])
|
|
||||||
.expect("should be able to serialize a proof");
|
|
||||||
zkproof
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputProver for OutputParameters {
|
|
||||||
type Proof = Proof<Bls12>;
|
|
||||||
|
|
||||||
fn prepare_circuit(
|
|
||||||
esk: jubjub::Fr,
|
|
||||||
payment_address: PaymentAddress,
|
|
||||||
rcm: jubjub::Fr,
|
|
||||||
value: NoteValue,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
) -> Output {
|
|
||||||
// Construct the value commitment for the proof instance
|
|
||||||
let value_commitment_opening = ValueCommitmentOpening {
|
|
||||||
value,
|
|
||||||
randomness: rcv.inner(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// We now have a full witness for the output proof.
|
|
||||||
Output {
|
|
||||||
value_commitment_opening: Some(value_commitment_opening),
|
|
||||||
payment_address: Some(payment_address),
|
|
||||||
commitment_randomness: Some(rcm),
|
|
||||||
esk: Some(esk),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_proof<R: RngCore>(&self, circuit: Output, rng: &mut R) -> Self::Proof {
|
|
||||||
create_random_proof(circuit, &self.0, rng).expect("proving should not fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
|
|
||||||
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
|
|
||||||
proof
|
|
||||||
.write(&mut zkproof[..])
|
|
||||||
.expect("should be able to serialize a proof");
|
|
||||||
zkproof
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub mod mock {
|
|
||||||
use ff::Field;
|
|
||||||
|
|
||||||
use super::{OutputProver, SpendProver};
|
|
||||||
use crate::{
|
|
||||||
sapling::{
|
|
||||||
self,
|
|
||||||
bundle::GrothProofBytes,
|
|
||||||
circuit::ValueCommitmentOpening,
|
|
||||||
value::{NoteValue, ValueCommitTrapdoor},
|
|
||||||
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
|
|
||||||
},
|
|
||||||
transaction::components::GROTH_PROOF_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct MockSpendProver;
|
|
||||||
|
|
||||||
impl SpendProver for MockSpendProver {
|
|
||||||
type Proof = GrothProofBytes;
|
|
||||||
|
|
||||||
fn prepare_circuit(
|
|
||||||
proof_generation_key: ProofGenerationKey,
|
|
||||||
diversifier: Diversifier,
|
|
||||||
_rseed: Rseed,
|
|
||||||
value: NoteValue,
|
|
||||||
alpha: jubjub::Fr,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
_merkle_path: sapling::MerklePath,
|
|
||||||
) -> Option<sapling::circuit::Spend> {
|
|
||||||
let payment_address = proof_generation_key
|
|
||||||
.to_viewing_key()
|
|
||||||
.ivk()
|
|
||||||
.to_payment_address(diversifier);
|
|
||||||
Some(sapling::circuit::Spend {
|
|
||||||
value_commitment_opening: Some(ValueCommitmentOpening {
|
|
||||||
value,
|
|
||||||
randomness: rcv.inner(),
|
|
||||||
}),
|
|
||||||
proof_generation_key: Some(proof_generation_key),
|
|
||||||
payment_address,
|
|
||||||
commitment_randomness: Some(jubjub::Scalar::ZERO),
|
|
||||||
ar: Some(alpha),
|
|
||||||
auth_path: vec![],
|
|
||||||
anchor: Some(anchor),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_proof<R: rand_core::RngCore>(
|
|
||||||
&self,
|
|
||||||
_circuit: sapling::circuit::Spend,
|
|
||||||
_rng: &mut R,
|
|
||||||
) -> Self::Proof {
|
|
||||||
[0u8; GROTH_PROOF_SIZE]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
|
|
||||||
proof
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MockOutputProver;
|
|
||||||
|
|
||||||
impl OutputProver for MockOutputProver {
|
|
||||||
type Proof = GrothProofBytes;
|
|
||||||
|
|
||||||
fn prepare_circuit(
|
|
||||||
esk: jubjub::Fr,
|
|
||||||
payment_address: PaymentAddress,
|
|
||||||
rcm: jubjub::Fr,
|
|
||||||
value: NoteValue,
|
|
||||||
rcv: ValueCommitTrapdoor,
|
|
||||||
) -> sapling::circuit::Output {
|
|
||||||
sapling::circuit::Output {
|
|
||||||
value_commitment_opening: Some(ValueCommitmentOpening {
|
|
||||||
value,
|
|
||||||
randomness: rcv.inner(),
|
|
||||||
}),
|
|
||||||
payment_address: Some(payment_address),
|
|
||||||
commitment_randomness: Some(rcm),
|
|
||||||
esk: Some(esk),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_proof<R: rand_core::RngCore>(
|
|
||||||
&self,
|
|
||||||
_circuit: sapling::circuit::Output,
|
|
||||||
_rng: &mut R,
|
|
||||||
) -> Self::Proof {
|
|
||||||
[0u8; GROTH_PROOF_SIZE]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
|
|
||||||
proof
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,165 +0,0 @@
|
||||||
//! Helper functions defined in the Zcash Protocol Specification.
|
|
||||||
|
|
||||||
use blake2s_simd::Params as Blake2sParams;
|
|
||||||
use group::{cofactor::CofactorGroup, ff::PrimeField, Curve, GroupEncoding, WnafBase, WnafScalar};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
constants::{
|
|
||||||
CRH_IVK_PERSONALIZATION, KEY_DIVERSIFICATION_PERSONALIZATION,
|
|
||||||
NOTE_COMMITMENT_RANDOMNESS_GENERATOR, NULLIFIER_POSITION_GENERATOR, PRF_NF_PERSONALIZATION,
|
|
||||||
},
|
|
||||||
group_hash::group_hash,
|
|
||||||
pedersen_hash::{pedersen_hash, Personalization},
|
|
||||||
};
|
|
||||||
|
|
||||||
const PREPARED_WINDOW_SIZE: usize = 4;
|
|
||||||
pub(crate) type PreparedBase = WnafBase<jubjub::ExtendedPoint, PREPARED_WINDOW_SIZE>;
|
|
||||||
pub(crate) type PreparedBaseSubgroup = WnafBase<jubjub::SubgroupPoint, PREPARED_WINDOW_SIZE>;
|
|
||||||
pub(crate) type PreparedScalar = WnafScalar<jubjub::Scalar, PREPARED_WINDOW_SIZE>;
|
|
||||||
|
|
||||||
/// $CRH^\mathsf{ivk}(ak, nk)$
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.1.5: CRH^ivk Hash Function][concretecrhivk].
|
|
||||||
///
|
|
||||||
/// [concretecrhivk]: https://zips.z.cash/protocol/protocol.pdf#concretecrhivk
|
|
||||||
pub(crate) fn crh_ivk(ak: [u8; 32], nk: [u8; 32]) -> jubjub::Scalar {
|
|
||||||
let mut h: [u8; 32] = Blake2sParams::new()
|
|
||||||
.hash_length(32)
|
|
||||||
.personal(CRH_IVK_PERSONALIZATION)
|
|
||||||
.to_state()
|
|
||||||
.update(&ak)
|
|
||||||
.update(&nk)
|
|
||||||
.finalize()
|
|
||||||
.as_bytes()
|
|
||||||
.try_into()
|
|
||||||
.expect("output length is correct");
|
|
||||||
|
|
||||||
// Drop the most significant five bits, so it can be interpreted as a scalar.
|
|
||||||
h[31] &= 0b0000_0111;
|
|
||||||
|
|
||||||
jubjub::Fr::from_repr(h).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
|
||||||
///
|
|
||||||
/// [concretediversifyhash]: https://zips.z.cash/protocol/protocol.pdf#concretediversifyhash
|
|
||||||
pub(crate) fn diversify_hash(d: &[u8; 11]) -> Option<jubjub::SubgroupPoint> {
|
|
||||||
group_hash(d, KEY_DIVERSIFICATION_PERSONALIZATION)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $MixingPedersenHash$.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.1.8: Mixing Pedersen Hash Function][concretemixinghash].
|
|
||||||
///
|
|
||||||
/// [concretemixinghash]: https://zips.z.cash/protocol/protocol.pdf#concretemixinghash
|
|
||||||
pub(crate) fn mixing_pedersen_hash(
|
|
||||||
cm: jubjub::SubgroupPoint,
|
|
||||||
position: u64,
|
|
||||||
) -> jubjub::SubgroupPoint {
|
|
||||||
cm + (NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $PRF^\mathsf{nfSapling}_{nk}(\rho)$
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
|
|
||||||
///
|
|
||||||
/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs
|
|
||||||
pub(crate) fn prf_nf(nk: &jubjub::SubgroupPoint, rho: &jubjub::SubgroupPoint) -> [u8; 32] {
|
|
||||||
Blake2sParams::new()
|
|
||||||
.hash_length(32)
|
|
||||||
.personal(PRF_NF_PERSONALIZATION)
|
|
||||||
.to_state()
|
|
||||||
.update(&nk.to_bytes())
|
|
||||||
.update(&rho.to_bytes())
|
|
||||||
.finalize()
|
|
||||||
.as_bytes()
|
|
||||||
.try_into()
|
|
||||||
.expect("output length is correct")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
pub(crate) fn ka_sapling_derive_public(
|
|
||||||
sk: &jubjub::Scalar,
|
|
||||||
b: &jubjub::ExtendedPoint,
|
|
||||||
) -> jubjub::ExtendedPoint {
|
|
||||||
ka_sapling_derive_public_prepared(&PreparedScalar::new(sk), &PreparedBase::new(*b))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
pub(crate) fn ka_sapling_derive_public_prepared(
|
|
||||||
sk: &PreparedScalar,
|
|
||||||
b: &PreparedBase,
|
|
||||||
) -> jubjub::ExtendedPoint {
|
|
||||||
// [sk] b
|
|
||||||
b * sk
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is defined implicitly by [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents]
|
|
||||||
/// which uses $KA^\mathsf{Sapling}.\mathsf{DerivePublic}$ to produce a diversified
|
|
||||||
/// transmission key with type $KA^\mathsf{Sapling}.\mathsf{PublicPrimeSubgroup}$.
|
|
||||||
///
|
|
||||||
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
|
|
||||||
pub(crate) fn ka_sapling_derive_public_subgroup_prepared(
|
|
||||||
sk: &PreparedScalar,
|
|
||||||
b: &PreparedBaseSubgroup,
|
|
||||||
) -> jubjub::SubgroupPoint {
|
|
||||||
// [sk] b
|
|
||||||
b * sk
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
pub(crate) fn ka_sapling_agree(
|
|
||||||
sk: &jubjub::Scalar,
|
|
||||||
b: &jubjub::ExtendedPoint,
|
|
||||||
) -> jubjub::SubgroupPoint {
|
|
||||||
ka_sapling_agree_prepared(&PreparedScalar::new(sk), &PreparedBase::new(*b))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
|
|
||||||
///
|
|
||||||
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
|
|
||||||
pub(crate) fn ka_sapling_agree_prepared(
|
|
||||||
sk: &PreparedScalar,
|
|
||||||
b: &PreparedBase,
|
|
||||||
) -> jubjub::SubgroupPoint {
|
|
||||||
// [8 sk] b
|
|
||||||
// <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
|
|
||||||
// ExtendedPoint::mul_by_cofactor in the jubjub crate.
|
|
||||||
|
|
||||||
(b * sk).clear_cofactor()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// $WindowedPedersenCommit_r(s)$
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.8.2: Windowed Pedersen commitments][concretewindowedcommit].
|
|
||||||
///
|
|
||||||
/// [concretewindowedcommit]: https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
|
|
||||||
pub(crate) fn windowed_pedersen_commit<I>(
|
|
||||||
personalization: Personalization,
|
|
||||||
s: I,
|
|
||||||
r: jubjub::Scalar,
|
|
||||||
) -> jubjub::SubgroupPoint
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = bool>,
|
|
||||||
{
|
|
||||||
pedersen_hash(personalization, s) + (NOTE_COMMITMENT_RANDOMNESS_GENERATOR * r)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Coordinate extractor for Jubjub.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.9.4: Coordinate Extractor for Jubjub][concreteextractorjubjub].
|
|
||||||
///
|
|
||||||
/// [concreteextractorjubjub]: https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub
|
|
||||||
pub(crate) fn extract_p(point: &jubjub::SubgroupPoint) -> bls12_381::Scalar {
|
|
||||||
// The commitment is in the prime order subgroup, so mapping the
|
|
||||||
// commitment to the u-coordinate is an injective encoding.
|
|
||||||
Into::<&jubjub::ExtendedPoint>::into(point)
|
|
||||||
.to_affine()
|
|
||||||
.get_u()
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub(crate) mod signatures;
|
|
|
@ -1,476 +0,0 @@
|
||||||
pub(crate) struct TestVector {
|
|
||||||
pub(crate) sk: [u8; 32],
|
|
||||||
pub(crate) vk: [u8; 32],
|
|
||||||
pub(crate) alpha: [u8; 32],
|
|
||||||
pub(crate) rsk: [u8; 32],
|
|
||||||
pub(crate) rvk: [u8; 32],
|
|
||||||
pub(crate) m: [u8; 32],
|
|
||||||
pub(crate) sig: [u8; 64],
|
|
||||||
pub(crate) rsig: [u8; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn make_test_vectors() -> Vec<TestVector> {
|
|
||||||
// From https://github.com/zcash/zcash-test-vectors/blob/master/zcash_test_vectors/sapling/redjubjub.py
|
|
||||||
vec![
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0x18, 0xe2, 0x8d, 0xea, 0x5c, 0x11, 0x81, 0x7a, 0xee, 0xb2, 0x1a, 0x19, 0x98, 0x1d,
|
|
||||||
0x28, 0x36, 0x8e, 0xc4, 0x38, 0xaf, 0xc2, 0x5a, 0x8d, 0xb9, 0x4e, 0xbe, 0x08, 0xd7,
|
|
||||||
0xa0, 0x28, 0x8e, 0x09,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0x9b, 0x01, 0x53, 0xb0, 0x3d, 0x32, 0x0f, 0xe2, 0x3e, 0x28, 0x34, 0xd5, 0xd6, 0x1d,
|
|
||||||
0xbb, 0x1f, 0x51, 0x9b, 0x3f, 0x41, 0xf8, 0xf9, 0x46, 0x15, 0x2b, 0xf0, 0xc3, 0xf2,
|
|
||||||
0x47, 0xd1, 0x18, 0x07,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0xff, 0xd1, 0xa1, 0x27, 0x32, 0x52, 0xb1, 0x87, 0xf4, 0xed, 0x32, 0x6d, 0xfc, 0x98,
|
|
||||||
0x85, 0x3e, 0x29, 0x17, 0xc2, 0xb3, 0x63, 0x79, 0xb1, 0x75, 0xda, 0x63, 0xb9, 0xef,
|
|
||||||
0x6d, 0xda, 0x6c, 0x08,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0x60, 0x87, 0x38, 0x3b, 0x30, 0x55, 0x9b, 0x31, 0x60, 0x90, 0x85, 0xb9, 0x00, 0x96,
|
|
||||||
0x45, 0xce, 0xb6, 0xa0, 0xc6, 0x61, 0x25, 0x99, 0xd7, 0x28, 0x80, 0x72, 0x8e, 0x61,
|
|
||||||
0x24, 0x4e, 0x7d, 0x03,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0xc1, 0xba, 0xbc, 0xb6, 0xea, 0xe2, 0xb9, 0x94, 0xee, 0x6d, 0x65, 0xc1, 0x0b, 0x9d,
|
|
||||||
0xad, 0x59, 0x40, 0xdc, 0x73, 0x5b, 0x07, 0x50, 0x4d, 0xae, 0xd1, 0xe4, 0x6b, 0x07,
|
|
||||||
0x09, 0xb4, 0x51, 0x36,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0xdc, 0xa3, 0xbb, 0x2c, 0xb8, 0xf0, 0x48, 0xcc, 0xab, 0x10, 0xae, 0xd7, 0x75, 0x46,
|
|
||||||
0xc1, 0xdb, 0xb1, 0x0c, 0xc4, 0xfb, 0x15, 0xab, 0x02, 0xac, 0xae, 0xf9, 0x44, 0xdd,
|
|
||||||
0xab, 0x8b, 0x67, 0x22, 0x54, 0x5f, 0xda, 0x4c, 0x62, 0x04, 0x6d, 0x69, 0xd9, 0x8f,
|
|
||||||
0x92, 0x2f, 0x4e, 0x8c, 0x21, 0x0b, 0xc4, 0x7b, 0x4f, 0xdd, 0xe0, 0xa1, 0x94, 0x71,
|
|
||||||
0x79, 0x80, 0x4c, 0x1a, 0xce, 0x56, 0x90, 0x05,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0x70, 0xc2, 0x84, 0x50, 0x4e, 0x90, 0xf0, 0x00, 0x8e, 0x8e, 0xd2, 0x20, 0x8f, 0x49,
|
|
||||||
0x69, 0x72, 0x7a, 0x41, 0x5e, 0xc3, 0x10, 0x2c, 0x29, 0x9e, 0x39, 0x8b, 0x6c, 0x16,
|
|
||||||
0x57, 0x2b, 0xd9, 0x64, 0x3e, 0xe1, 0x01, 0x17, 0x66, 0x68, 0x1e, 0x40, 0x6e, 0xe6,
|
|
||||||
0xbe, 0xe3, 0xd0, 0x3e, 0xe8, 0xf2, 0x71, 0x76, 0xe3, 0x2f, 0xba, 0xbd, 0xde, 0xd2,
|
|
||||||
0x0b, 0x0d, 0x17, 0x86, 0xa4, 0xee, 0x18, 0x01,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0x05, 0x96, 0x54, 0xf9, 0x61, 0x27, 0x3d, 0xaf, 0xda, 0x3b, 0x26, 0x77, 0xb3, 0x5c,
|
|
||||||
0x18, 0xaf, 0x6b, 0x11, 0xad, 0xfb, 0x9e, 0xe9, 0x0b, 0x48, 0x93, 0x5e, 0x55, 0x7c,
|
|
||||||
0x8d, 0x5d, 0x9c, 0x04,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0xfa, 0xf6, 0xc3, 0xb7, 0x37, 0xe8, 0xe6, 0x11, 0xaa, 0xfe, 0xa5, 0x2f, 0x03, 0xbb,
|
|
||||||
0x27, 0x86, 0xe1, 0x83, 0x53, 0xeb, 0xe0, 0xd3, 0x13, 0x9e, 0x3c, 0x54, 0x49, 0x87,
|
|
||||||
0x80, 0xc8, 0xc1, 0x99,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0xc3, 0x0b, 0x96, 0x20, 0x8d, 0xa8, 0x00, 0xe1, 0x0a, 0xf0, 0x25, 0x42, 0xce, 0x69,
|
|
||||||
0x4b, 0x7e, 0xd7, 0x6a, 0x28, 0x29, 0x9f, 0x85, 0x99, 0x8e, 0x5d, 0x61, 0x08, 0x12,
|
|
||||||
0x68, 0x1b, 0xf0, 0x03,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0xc8, 0xa1, 0xea, 0x19, 0xef, 0xcf, 0x3d, 0x90, 0xe5, 0x2b, 0x4c, 0xb9, 0x81, 0xc6,
|
|
||||||
0x63, 0x2d, 0x43, 0x7c, 0xd5, 0x24, 0x3e, 0x6f, 0xa5, 0xd6, 0xf0, 0xbf, 0x5d, 0x8e,
|
|
||||||
0xf5, 0x78, 0x8c, 0x08,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0xd5, 0x24, 0xdc, 0xe7, 0x73, 0x40, 0x69, 0x75, 0x8a, 0x91, 0xf0, 0x07, 0xa8, 0x69,
|
|
||||||
0x50, 0x5d, 0xfc, 0x4a, 0xba, 0x17, 0x20, 0x59, 0x4d, 0x4d, 0x74, 0xf0, 0x07, 0x70,
|
|
||||||
0x0e, 0x62, 0xee, 0x00,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
||||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
||||||
0x01, 0x01, 0x01, 0x01,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0xb5, 0xa1, 0xf3, 0x2d, 0x3d, 0x50, 0xfc, 0x73, 0x8b, 0x5c, 0x3b, 0x4e, 0x99, 0x60,
|
|
||||||
0x72, 0x9c, 0xe4, 0x31, 0x6b, 0xa7, 0x72, 0x1a, 0x12, 0x68, 0x66, 0x04, 0xfe, 0xba,
|
|
||||||
0x6b, 0xd7, 0x48, 0x45, 0x00, 0x70, 0xcb, 0x92, 0x24, 0x06, 0xfd, 0xfc, 0x5d, 0x60,
|
|
||||||
0xde, 0xa9, 0xbe, 0x3a, 0x52, 0x6a, 0x16, 0xcf, 0xeb, 0x87, 0x77, 0x79, 0xfb, 0x78,
|
|
||||||
0x2d, 0x5d, 0x41, 0x39, 0x5b, 0x45, 0x5f, 0x04,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0x5a, 0x5a, 0x20, 0xd2, 0x00, 0xef, 0xdd, 0xd4, 0x98, 0xdf, 0xae, 0x2a, 0x9e, 0xf8,
|
|
||||||
0xcf, 0x01, 0x28, 0x1a, 0x89, 0x19, 0x01, 0x8a, 0x82, 0x4c, 0xc7, 0xa4, 0x98, 0x3b,
|
|
||||||
0x9a, 0x0d, 0x4a, 0x06, 0xff, 0x17, 0x20, 0x79, 0xe0, 0x13, 0xd4, 0x2a, 0x2a, 0x3a,
|
|
||||||
0x88, 0xa6, 0x52, 0x0c, 0x86, 0xfc, 0xe3, 0xb9, 0x8e, 0x1e, 0xfa, 0xa3, 0x25, 0x83,
|
|
||||||
0x2a, 0x6a, 0x56, 0x58, 0xd8, 0xdd, 0x7c, 0x0a,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0xad, 0xe7, 0xab, 0xb5, 0x51, 0xc7, 0x9d, 0x0f, 0x0e, 0x42, 0xef, 0x7f, 0x12, 0x06,
|
|
||||||
0xb8, 0x77, 0x12, 0xa8, 0x4a, 0x61, 0xde, 0xa3, 0xf3, 0x7b, 0x42, 0x49, 0x6d, 0x7e,
|
|
||||||
0xfd, 0x12, 0x52, 0x0c,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0x36, 0x9e, 0xa7, 0x51, 0x76, 0x2f, 0x83, 0x9d, 0x25, 0x70, 0x1a, 0x5e, 0xeb, 0x55,
|
|
||||||
0x1e, 0xc4, 0xf0, 0x6c, 0x12, 0x90, 0xb3, 0xb9, 0xc3, 0xa7, 0x24, 0x40, 0x2d, 0xec,
|
|
||||||
0x02, 0x73, 0x92, 0x21,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0x81, 0x92, 0x25, 0x29, 0xa6, 0x3e, 0xe7, 0x43, 0xfc, 0x4f, 0xbb, 0xac, 0x45, 0xc4,
|
|
||||||
0x98, 0x83, 0x16, 0xbc, 0x9b, 0x6e, 0x42, 0x8b, 0x01, 0xa8, 0xd3, 0x1f, 0xc1, 0xc2,
|
|
||||||
0xa6, 0xca, 0x62, 0x05,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0x77, 0x4d, 0xda, 0x07, 0x99, 0xf7, 0xed, 0x82, 0x87, 0x81, 0xe2, 0x5f, 0xc4, 0xa9,
|
|
||||||
0xe8, 0x54, 0x28, 0x29, 0xb2, 0xce, 0x1f, 0xf4, 0x8d, 0x1d, 0x6d, 0xb9, 0xfa, 0xdb,
|
|
||||||
0xb9, 0x28, 0x37, 0x03,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0x0d, 0x92, 0xad, 0x6d, 0x46, 0xed, 0xac, 0xd0, 0x23, 0xd4, 0xd2, 0xef, 0x70, 0x3a,
|
|
||||||
0x6c, 0xa0, 0xa7, 0x92, 0xcf, 0xc4, 0xb7, 0xda, 0x11, 0xc2, 0x35, 0x3b, 0xc8, 0x45,
|
|
||||||
0xa2, 0x7a, 0x97, 0x4d,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
|
||||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
|
||||||
0x02, 0x02, 0x02, 0x02,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0x1f, 0x3e, 0x8a, 0x94, 0x31, 0x0c, 0x20, 0x71, 0xa7, 0x0f, 0x9d, 0xf5, 0xe7, 0x9a,
|
|
||||||
0xa9, 0xe8, 0x48, 0x5d, 0xec, 0xcb, 0x17, 0x8b, 0xdf, 0xf9, 0x80, 0x5f, 0xcb, 0xe6,
|
|
||||||
0xf7, 0xd5, 0x51, 0xee, 0xe3, 0xc3, 0x54, 0x2c, 0xa7, 0x5c, 0x9d, 0x8d, 0x4a, 0xdc,
|
|
||||||
0x54, 0xd7, 0x2c, 0x3d, 0xbe, 0x28, 0x62, 0x6d, 0x20, 0x78, 0x5b, 0xb7, 0xf5, 0x88,
|
|
||||||
0xc1, 0xa5, 0x82, 0xb8, 0x93, 0xdb, 0xb6, 0x01,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0xd1, 0x36, 0x21, 0x4c, 0x5d, 0x52, 0x8e, 0xa3, 0xd4, 0xcb, 0x7b, 0x63, 0x1a, 0x6b,
|
|
||||||
0xb0, 0x36, 0x06, 0x49, 0x73, 0xa1, 0x08, 0xb7, 0x33, 0xa5, 0xe3, 0xa4, 0x52, 0xab,
|
|
||||||
0x52, 0xa6, 0x59, 0xe5, 0x67, 0xcb, 0x55, 0xd2, 0x64, 0x4e, 0x74, 0xb6, 0xe8, 0x42,
|
|
||||||
0x6f, 0x2a, 0x7d, 0xd2, 0xa0, 0x4d, 0x2d, 0xda, 0x49, 0x35, 0xcc, 0x38, 0x20, 0xb7,
|
|
||||||
0x7a, 0x9c, 0x1a, 0xb6, 0x19, 0x86, 0x3c, 0x05,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0xc9, 0xd2, 0xae, 0x1f, 0x6d, 0x32, 0xa6, 0x75, 0xd0, 0x9e, 0xb0, 0x82, 0x3f, 0x46,
|
|
||||||
0x7f, 0xa9, 0x21, 0xb3, 0x28, 0x4a, 0xcb, 0x35, 0xfa, 0xbd, 0xfc, 0x99, 0x4d, 0xe5,
|
|
||||||
0x49, 0xb8, 0x59, 0x0d,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0x2d, 0x2f, 0x31, 0x6e, 0x5c, 0x36, 0x9a, 0xe4, 0xdd, 0x2c, 0x82, 0x5f, 0x3d, 0x86,
|
|
||||||
0x46, 0x00, 0x58, 0x40, 0x71, 0x84, 0x60, 0x3b, 0x21, 0x2c, 0xf3, 0x45, 0x9f, 0x36,
|
|
||||||
0xc8, 0x69, 0x7f, 0xd8,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0xeb, 0xbc, 0x89, 0x03, 0x11, 0x07, 0xc4, 0x4f, 0x47, 0x88, 0x9e, 0xd4, 0xd4, 0x37,
|
|
||||||
0x5a, 0x41, 0x14, 0xcf, 0x8a, 0x75, 0xdd, 0x33, 0xb9, 0x62, 0xf2, 0xd7, 0x59, 0xd3,
|
|
||||||
0xf4, 0xc6, 0xdf, 0x06,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0xfd, 0x62, 0x41, 0x4c, 0x1f, 0x2b, 0xd3, 0xf4, 0x94, 0x16, 0x87, 0x8a, 0x80, 0x5d,
|
|
||||||
0x71, 0x44, 0x35, 0x47, 0x7f, 0xbe, 0xa7, 0x2e, 0x4c, 0x1a, 0x46, 0xc2, 0x73, 0x53,
|
|
||||||
0x54, 0xca, 0xbb, 0x05,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0xf0, 0x43, 0x0e, 0x95, 0x3b, 0xe6, 0x0b, 0xf4, 0x38, 0xdb, 0xdc, 0xc2, 0x30, 0x3f,
|
|
||||||
0x0e, 0x32, 0xa6, 0xf7, 0xce, 0x2f, 0xbe, 0xdf, 0xb1, 0x3a, 0xc5, 0x18, 0xf7, 0x5a,
|
|
||||||
0x3f, 0xd1, 0x0e, 0xb5,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
||||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
||||||
0x03, 0x03, 0x03, 0x03,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0x12, 0xc7, 0x8d, 0xdd, 0x20, 0xd3, 0x0a, 0x61, 0xf8, 0x93, 0x0c, 0x6f, 0xe0, 0x85,
|
|
||||||
0x0f, 0xd1, 0x12, 0xbb, 0x7b, 0xe8, 0x8b, 0x12, 0x38, 0xea, 0x33, 0xd6, 0xbe, 0xf8,
|
|
||||||
0x81, 0xc1, 0x02, 0xd1, 0x04, 0xaa, 0x36, 0x54, 0x4a, 0x78, 0x47, 0x1c, 0x9e, 0x28,
|
|
||||||
0x42, 0xe6, 0xfd, 0x42, 0x55, 0x83, 0x46, 0xcf, 0xf4, 0x31, 0x27, 0x03, 0x26, 0x66,
|
|
||||||
0xeb, 0x11, 0x6f, 0x44, 0x2a, 0x28, 0x48, 0x0c,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0x01, 0xba, 0xaa, 0x26, 0x27, 0x4c, 0x14, 0x9a, 0xcf, 0x12, 0xe1, 0xcc, 0xf5, 0x50,
|
|
||||||
0x7d, 0x56, 0x79, 0x04, 0x82, 0xf0, 0x67, 0xe5, 0xc9, 0x2b, 0x32, 0x19, 0xad, 0x6b,
|
|
||||||
0xf9, 0x11, 0x18, 0xcc, 0x3f, 0xce, 0x8d, 0x2a, 0x23, 0x19, 0x8a, 0x3b, 0x29, 0x0a,
|
|
||||||
0x7b, 0xf6, 0x8c, 0x2a, 0xc0, 0x7b, 0x5d, 0x90, 0x62, 0xb9, 0xf8, 0x68, 0x66, 0x2b,
|
|
||||||
0xb2, 0x52, 0x49, 0x12, 0xd4, 0x85, 0x6e, 0x0c,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0x33, 0xbc, 0xd2, 0x86, 0x45, 0x41, 0xb8, 0xbb, 0x7f, 0xdc, 0x77, 0xa1, 0x9d, 0x97,
|
|
||||||
0x0f, 0x92, 0x4e, 0xae, 0xec, 0xf4, 0x10, 0x3c, 0x38, 0xc8, 0xd2, 0xb0, 0x66, 0x81,
|
|
||||||
0x42, 0xf2, 0x7d, 0x09,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0x74, 0x17, 0x94, 0xe6, 0x2c, 0xf9, 0x32, 0x0c, 0x58, 0xba, 0xc5, 0x94, 0xa2, 0xb9,
|
|
||||||
0x0e, 0x34, 0x0a, 0x6d, 0x8a, 0x68, 0x05, 0x6f, 0x6e, 0xd5, 0xc7, 0x86, 0x8c, 0x5f,
|
|
||||||
0xf3, 0xe4, 0xd6, 0x16,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0x7c, 0xe7, 0x25, 0xa5, 0xfe, 0xf6, 0x1b, 0xd4, 0xa1, 0xe9, 0xc7, 0x73, 0x28, 0xe8,
|
|
||||||
0x21, 0x0e, 0xb7, 0x29, 0x2d, 0x95, 0x4c, 0x64, 0xe9, 0x9e, 0x8b, 0xed, 0xd0, 0x7a,
|
|
||||||
0xb3, 0xab, 0x0e, 0x0d,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0xf8, 0x76, 0x01, 0x55, 0xe5, 0x29, 0x3d, 0xbf, 0x9e, 0xb5, 0x77, 0x48, 0x32, 0x5f,
|
|
||||||
0xc9, 0xf9, 0x04, 0x9d, 0xe5, 0x88, 0x5c, 0x65, 0xba, 0x60, 0xb5, 0xee, 0x03, 0x97,
|
|
||||||
0x0b, 0xe9, 0x0e, 0x08,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0x66, 0x62, 0xba, 0x09, 0x95, 0x0a, 0xcc, 0xd2, 0xce, 0xa3, 0xc7, 0xa8, 0x12, 0x90,
|
|
||||||
0xcd, 0x59, 0x78, 0xa6, 0x2b, 0x5a, 0xc5, 0xbb, 0xc4, 0x8d, 0x9f, 0x58, 0x19, 0xcd,
|
|
||||||
0xc9, 0x64, 0x6f, 0x0a,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
||||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
||||||
0x04, 0x04, 0x04, 0x04,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0x77, 0x4a, 0xc4, 0x67, 0x3f, 0x09, 0xf3, 0xac, 0x57, 0x89, 0xb2, 0x86, 0xb5, 0xee,
|
|
||||||
0xcb, 0xed, 0xb2, 0x57, 0x23, 0x4e, 0x8c, 0xdf, 0xd9, 0x3f, 0x02, 0x89, 0x09, 0x78,
|
|
||||||
0xa6, 0xbb, 0xa6, 0x11, 0x69, 0xed, 0x48, 0xf9, 0xe1, 0xc9, 0xfd, 0x13, 0x19, 0xbd,
|
|
||||||
0x33, 0x0d, 0x2c, 0xf5, 0xb4, 0x91, 0x01, 0x0d, 0x69, 0xb0, 0x43, 0xf4, 0x64, 0x8b,
|
|
||||||
0xff, 0x55, 0x41, 0x62, 0xc6, 0xa6, 0xdc, 0x09,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0x7c, 0x6c, 0x49, 0x8d, 0xe0, 0x01, 0x78, 0x61, 0x09, 0xb3, 0x03, 0xa4, 0xc5, 0xdc,
|
|
||||||
0xb7, 0xfd, 0x07, 0x57, 0x50, 0xa0, 0xb9, 0xdf, 0x5e, 0x1e, 0x2a, 0x8e, 0x75, 0x47,
|
|
||||||
0xb7, 0xed, 0x70, 0xcc, 0x0b, 0x56, 0xa5, 0xbf, 0xa9, 0x65, 0x78, 0x43, 0xef, 0xd8,
|
|
||||||
0x9c, 0x66, 0xa8, 0x4f, 0x41, 0xd2, 0xb1, 0xb5, 0x07, 0x51, 0x19, 0x6b, 0x1e, 0x8c,
|
|
||||||
0x0c, 0x44, 0x98, 0x60, 0x06, 0x96, 0xa4, 0x04,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0xca, 0x35, 0x06, 0xd6, 0xaf, 0x77, 0x67, 0xb5, 0x79, 0x0e, 0xf0, 0xc5, 0x19, 0x0f,
|
|
||||||
0xb3, 0xf3, 0x87, 0x7c, 0x4a, 0xab, 0x40, 0xe0, 0xdd, 0x65, 0x1a, 0xbb, 0xda, 0xcb,
|
|
||||||
0x54, 0x4e, 0xd0, 0x05,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0xba, 0xb6, 0xcf, 0xb5, 0xc8, 0xea, 0x34, 0x91, 0x25, 0x1b, 0x46, 0xd5, 0x2a, 0xca,
|
|
||||||
0x25, 0xd9, 0xe9, 0xaf, 0x69, 0xfa, 0xa9, 0xb4, 0xe4, 0x0b, 0x03, 0xad, 0x00, 0x86,
|
|
||||||
0xde, 0x59, 0xb5, 0x1f,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0xbe, 0xa3, 0x87, 0x20, 0x3f, 0x43, 0x76, 0x0a, 0xd3, 0x7d, 0x61, 0xde, 0x0e, 0xb5,
|
|
||||||
0x9f, 0xca, 0x6c, 0xab, 0x75, 0x60, 0xdf, 0x64, 0xfa, 0xbb, 0x95, 0x11, 0x57, 0x9f,
|
|
||||||
0x6f, 0x68, 0x26, 0x06,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0x88, 0xd9, 0x8d, 0xf6, 0xee, 0xba, 0xdd, 0xbf, 0x4c, 0x8c, 0x51, 0xa4, 0x28, 0xc4,
|
|
||||||
0x52, 0xbe, 0xf4, 0x27, 0xc0, 0x0b, 0x20, 0x45, 0xd8, 0x21, 0xb0, 0xcc, 0x31, 0x6b,
|
|
||||||
0xc4, 0xb6, 0xf6, 0x0b,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0x11, 0x26, 0x7d, 0x14, 0xd5, 0xe0, 0xb2, 0xbb, 0x3c, 0xe0, 0x99, 0xe8, 0xef, 0x84,
|
|
||||||
0x49, 0x47, 0x1c, 0xbc, 0xfc, 0x69, 0x39, 0xa4, 0xb3, 0x48, 0xde, 0xa2, 0xc1, 0x73,
|
|
||||||
0x56, 0xa1, 0xe8, 0xdd,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
||||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
||||||
0x05, 0x05, 0x05, 0x05,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0x9a, 0x25, 0x42, 0x9f, 0x3e, 0xfd, 0x9b, 0x2f, 0x7d, 0xe2, 0x9e, 0x45, 0x12, 0x8d,
|
|
||||||
0xd7, 0xb7, 0x60, 0xf0, 0x50, 0x8c, 0xd9, 0x58, 0x21, 0x82, 0xab, 0xaf, 0x53, 0xdd,
|
|
||||||
0x76, 0xc0, 0x34, 0x2c, 0xe4, 0x1b, 0x4a, 0xcf, 0x8e, 0x0a, 0x48, 0x24, 0xe4, 0x11,
|
|
||||||
0x08, 0xc2, 0x02, 0x65, 0x73, 0x11, 0x4b, 0x60, 0xbe, 0xec, 0xb1, 0x74, 0x01, 0x2a,
|
|
||||||
0x2b, 0xdb, 0xee, 0xcb, 0xaa, 0x00, 0xb5, 0x06,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0xcf, 0xf5, 0x83, 0x57, 0x13, 0xbe, 0x07, 0xfb, 0xe1, 0x25, 0xbb, 0xf2, 0x7a, 0x63,
|
|
||||||
0x6a, 0xdd, 0x13, 0x1c, 0x90, 0x81, 0x71, 0x6c, 0x52, 0xfd, 0xa8, 0x75, 0x42, 0x6d,
|
|
||||||
0x03, 0x98, 0x2c, 0xd2, 0x7e, 0xbd, 0x14, 0xb4, 0x22, 0x7b, 0x83, 0x96, 0x15, 0xfd,
|
|
||||||
0x03, 0x71, 0xbf, 0xdb, 0x8a, 0x30, 0xab, 0xdd, 0xff, 0x74, 0xd7, 0x95, 0xf3, 0xe2,
|
|
||||||
0x7d, 0x1d, 0x47, 0xc6, 0x29, 0x46, 0x9b, 0x08,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0xbc, 0x27, 0x83, 0x8d, 0xe2, 0xa6, 0x14, 0xcf, 0xba, 0x6c, 0x3e, 0x92, 0x2a, 0x8f,
|
|
||||||
0x84, 0x24, 0xd9, 0x85, 0x6f, 0x68, 0x16, 0xf3, 0xbc, 0x61, 0x02, 0x31, 0x3b, 0x7f,
|
|
||||||
0xaf, 0x5c, 0x3a, 0x0c,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0xd7, 0x9b, 0xe9, 0xff, 0x22, 0x9a, 0x2e, 0x35, 0xf5, 0xbc, 0xa4, 0x48, 0xe5, 0xeb,
|
|
||||||
0x4a, 0x8a, 0xa9, 0x7f, 0xb4, 0x18, 0x02, 0x91, 0x25, 0xcf, 0xba, 0xa7, 0x8a, 0x91,
|
|
||||||
0xa3, 0x82, 0xb0, 0x94,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0x21, 0xa7, 0x15, 0x0e, 0x19, 0x4f, 0xed, 0xfe, 0xf9, 0x0c, 0x5d, 0x10, 0xe4, 0x20,
|
|
||||||
0x85, 0x8b, 0xca, 0x40, 0x04, 0x04, 0x0e, 0xb6, 0x81, 0xd1, 0x4e, 0x75, 0xc4, 0x47,
|
|
||||||
0x13, 0x51, 0xcb, 0x02,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0x26, 0xa2, 0xa1, 0xc4, 0x9c, 0xe7, 0x6a, 0xfd, 0x31, 0x69, 0xd3, 0xd5, 0x7a, 0x8f,
|
|
||||||
0xa1, 0x09, 0xa3, 0x8b, 0x3f, 0x6b, 0x23, 0x6e, 0xd7, 0x2c, 0xa8, 0xf6, 0xcb, 0x61,
|
|
||||||
0xd8, 0xf8, 0x87, 0x00,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0x54, 0xbf, 0x1b, 0xe7, 0x2e, 0x6d, 0x41, 0x20, 0x8b, 0x8a, 0xec, 0x11, 0x61, 0xd3,
|
|
||||||
0xba, 0x59, 0x51, 0x9f, 0xb9, 0x3d, 0xa0, 0x1a, 0x55, 0xe6, 0x78, 0xe2, 0x75, 0x20,
|
|
||||||
0x06, 0x60, 0x36, 0xc9,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
||||||
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
||||||
0x06, 0x06, 0x06, 0x06,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0xbb, 0xe0, 0x23, 0x59, 0x87, 0xc6, 0xe0, 0xec, 0x68, 0x6d, 0xdb, 0x8a, 0x65, 0x72,
|
|
||||||
0x66, 0xad, 0x60, 0x5f, 0x7b, 0x75, 0x95, 0x5b, 0xb0, 0xe8, 0x02, 0xf8, 0x81, 0x64,
|
|
||||||
0xa0, 0xff, 0xe1, 0x0c, 0x3b, 0x73, 0x85, 0x04, 0xab, 0xb3, 0xd1, 0x05, 0x62, 0xb9,
|
|
||||||
0x27, 0xb3, 0xd2, 0x9f, 0xe9, 0xb0, 0xd3, 0x56, 0x28, 0x6a, 0xea, 0xe5, 0xa2, 0xac,
|
|
||||||
0x9e, 0x43, 0x5f, 0x20, 0x79, 0x1a, 0xf8, 0x00,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0x6d, 0xe3, 0x2b, 0x54, 0x15, 0xd7, 0x7a, 0x90, 0x5f, 0x09, 0x03, 0x90, 0x2a, 0x11,
|
|
||||||
0x7e, 0xda, 0x79, 0x3c, 0x70, 0x8e, 0x23, 0xa5, 0x42, 0x45, 0xba, 0x8a, 0x8d, 0x1f,
|
|
||||||
0xe0, 0x26, 0x75, 0x23, 0x23, 0x15, 0x65, 0xe0, 0x57, 0x09, 0xae, 0xd9, 0x6c, 0x22,
|
|
||||||
0x1f, 0xb1, 0xf3, 0xd0, 0x42, 0x04, 0x35, 0x03, 0xff, 0x33, 0x85, 0x85, 0xa9, 0xbb,
|
|
||||||
0x98, 0x9c, 0x9d, 0xd4, 0x30, 0xd6, 0xd6, 0x0b,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0xb2, 0x08, 0x59, 0xb8, 0x8e, 0xe3, 0x33, 0x8a, 0x64, 0x95, 0x4f, 0x8a, 0x9e, 0x8e,
|
|
||||||
0x9b, 0xf3, 0xe7, 0x11, 0x5a, 0xcf, 0x7c, 0x6e, 0x7f, 0x01, 0x43, 0x2c, 0x5f, 0x76,
|
|
||||||
0x96, 0xd2, 0xd0, 0x05,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0xa8, 0x1f, 0xe6, 0x84, 0x6d, 0xbe, 0x0a, 0x75, 0xc0, 0xf4, 0x9b, 0x21, 0x32, 0x32,
|
|
||||||
0xbe, 0xad, 0xd1, 0xf9, 0xa5, 0x64, 0x67, 0x3d, 0x25, 0xb9, 0x1e, 0xe0, 0xf1, 0x7c,
|
|
||||||
0xe9, 0xca, 0xa3, 0x63,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0x44, 0xd9, 0x08, 0xe1, 0xc1, 0x5e, 0x6b, 0xd9, 0x38, 0x0a, 0x8b, 0x23, 0x5a, 0xce,
|
|
||||||
0x02, 0xfa, 0xc1, 0xc0, 0x87, 0x94, 0x45, 0x4b, 0xcd, 0xb4, 0xa6, 0xf4, 0x8c, 0xea,
|
|
||||||
0x78, 0xa7, 0x4a, 0x04,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0xf6, 0xe1, 0x61, 0x99, 0x50, 0x42, 0x9f, 0x63, 0x9d, 0x9f, 0xda, 0xad, 0xf8, 0x5c,
|
|
||||||
0x9e, 0xed, 0xa9, 0xd2, 0xe1, 0x63, 0xc2, 0xb9, 0x4c, 0xb6, 0xe9, 0x20, 0xec, 0x60,
|
|
||||||
0x0f, 0x7a, 0x1b, 0x0a,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0x0b, 0x68, 0xd5, 0x0f, 0x91, 0x3c, 0xd1, 0xb7, 0x8b, 0x59, 0x92, 0x1e, 0x16, 0x56,
|
|
||||||
0xd5, 0x76, 0xb0, 0xeb, 0x17, 0x1e, 0xd3, 0x87, 0x0d, 0x39, 0xfe, 0xc6, 0x94, 0x41,
|
|
||||||
0xb3, 0x4b, 0x25, 0x38,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
||||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
||||||
0x07, 0x07, 0x07, 0x07,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0x44, 0x6d, 0x67, 0x7c, 0x4c, 0xfe, 0xfd, 0x02, 0x4b, 0x0a, 0xeb, 0x37, 0xa5, 0x98,
|
|
||||||
0xcc, 0x2e, 0xb3, 0xd2, 0x9b, 0x02, 0x94, 0xfe, 0x5b, 0xb6, 0x97, 0x8e, 0x8b, 0x43,
|
|
||||||
0xd3, 0x2b, 0x2e, 0x4f, 0x09, 0x56, 0xac, 0xd1, 0x3e, 0x7e, 0x3a, 0x63, 0xa1, 0x8f,
|
|
||||||
0xca, 0x32, 0xd6, 0xab, 0x94, 0xb9, 0x4e, 0xd0, 0x33, 0xe9, 0xa1, 0x0f, 0xc5, 0x69,
|
|
||||||
0x28, 0xbc, 0x8a, 0x0f, 0x4f, 0x8e, 0x95, 0x00,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0x8d, 0xe0, 0x41, 0xe7, 0x09, 0xdb, 0x62, 0x4a, 0xe2, 0xbe, 0x16, 0x48, 0xb6, 0x62,
|
|
||||||
0x23, 0x9c, 0xde, 0xdf, 0x85, 0xec, 0xd3, 0x82, 0x26, 0x8b, 0x0e, 0x35, 0x54, 0xbf,
|
|
||||||
0xa0, 0xf2, 0x08, 0x1c, 0xd6, 0x41, 0xbc, 0xa0, 0x40, 0x78, 0xaa, 0x89, 0xf7, 0xdd,
|
|
||||||
0x25, 0x40, 0x58, 0x7c, 0xed, 0x6b, 0x45, 0x89, 0x16, 0xb1, 0x3e, 0x4b, 0x6a, 0x36,
|
|
||||||
0x30, 0xda, 0x69, 0x76, 0x46, 0xdb, 0xbf, 0x09,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0x32, 0x16, 0xae, 0x47, 0xe9, 0xf5, 0x3e, 0x8a, 0x52, 0x79, 0x6f, 0x24, 0xb6, 0x24,
|
|
||||||
0x60, 0x77, 0x6b, 0xd5, 0xf2, 0x05, 0xa7, 0x8e, 0x15, 0x95, 0xbc, 0x8e, 0xfe, 0xdc,
|
|
||||||
0x51, 0x9d, 0x36, 0x0b,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0xdf, 0x74, 0xbf, 0x04, 0x79, 0x61, 0xcc, 0x5c, 0xda, 0xc8, 0x28, 0x90, 0xc7, 0x6e,
|
|
||||||
0xc6, 0x75, 0xbd, 0x4e, 0x89, 0xea, 0xd2, 0x80, 0xc9, 0x52, 0xd7, 0xc3, 0x3e, 0xea,
|
|
||||||
0xf2, 0xb5, 0xa6, 0x6b,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0xc9, 0x61, 0xf2, 0xdd, 0x93, 0x68, 0x2a, 0xdb, 0x93, 0xf5, 0xc0, 0x5a, 0x73, 0xfd,
|
|
||||||
0xbc, 0x6d, 0x43, 0xc7, 0x0e, 0x1b, 0x15, 0xe8, 0xd5, 0x3e, 0x3f, 0x17, 0xa8, 0x24,
|
|
||||||
0x94, 0xe3, 0xf2, 0x09,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0x44, 0x4b, 0xa9, 0x4e, 0x1e, 0x50, 0xd2, 0x94, 0x63, 0x5e, 0x68, 0xb2, 0x95, 0x01,
|
|
||||||
0xb5, 0x3e, 0xae, 0x61, 0xcd, 0x1f, 0xbb, 0x3b, 0x84, 0xcd, 0x52, 0xf6, 0x72, 0x9c,
|
|
||||||
0xfb, 0xcb, 0xab, 0x06,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0x0a, 0xfb, 0xe4, 0x06, 0xa8, 0x91, 0xc3, 0xb8, 0xc3, 0x10, 0xc2, 0x15, 0xbc, 0x68,
|
|
||||||
0xa9, 0x13, 0xde, 0x7c, 0xda, 0x06, 0xaf, 0x29, 0x42, 0x00, 0x56, 0x46, 0x8d, 0x0c,
|
|
||||||
0x08, 0x85, 0x5b, 0x28,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
|
||||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
|
||||||
0x08, 0x08, 0x08, 0x08,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0x99, 0x35, 0x80, 0xef, 0x93, 0x34, 0x9a, 0x1c, 0x9e, 0xe9, 0x60, 0xca, 0x3e, 0x7c,
|
|
||||||
0xd0, 0x4c, 0x13, 0xb4, 0xa0, 0xec, 0x4f, 0xd1, 0x80, 0x53, 0xa1, 0x9c, 0xff, 0x77,
|
|
||||||
0x63, 0x62, 0x09, 0x65, 0xfb, 0xee, 0x96, 0xc1, 0x64, 0x72, 0x30, 0xe3, 0x73, 0xcb,
|
|
||||||
0x82, 0xb8, 0x1d, 0x00, 0x03, 0x92, 0x23, 0xd3, 0x0b, 0x39, 0x3e, 0xd1, 0x72, 0xc9,
|
|
||||||
0xb3, 0xc5, 0x63, 0xc6, 0x11, 0x79, 0x22, 0x05,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0xcc, 0x7a, 0xae, 0x1c, 0xed, 0xad, 0x2d, 0x7f, 0x6c, 0xe0, 0x4c, 0x19, 0xc5, 0xa5,
|
|
||||||
0xb6, 0xb7, 0xa6, 0xa0, 0x82, 0x78, 0x5c, 0x54, 0x0c, 0x14, 0xf6, 0x30, 0x9b, 0x06,
|
|
||||||
0x4d, 0x1f, 0xfa, 0x68, 0x17, 0x29, 0x53, 0xfb, 0xa0, 0xc2, 0xfc, 0xfb, 0x87, 0x5c,
|
|
||||||
0xa7, 0xf7, 0xea, 0x98, 0xef, 0x55, 0xa0, 0x40, 0x2f, 0xd5, 0x29, 0xcf, 0xcd, 0xdf,
|
|
||||||
0x99, 0x6c, 0xa2, 0xb8, 0xca, 0x89, 0x90, 0x0a,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
TestVector {
|
|
||||||
sk: [
|
|
||||||
0x85, 0x83, 0x6f, 0x98, 0x32, 0xb2, 0x8d, 0xe7, 0xc6, 0x36, 0x13, 0xe2, 0xa6, 0xed,
|
|
||||||
0x36, 0xfb, 0x1a, 0xb4, 0x4f, 0xb0, 0xc1, 0x3f, 0xa8, 0x79, 0x8c, 0xd9, 0xcd, 0x30,
|
|
||||||
0x30, 0xd4, 0x55, 0x03,
|
|
||||||
],
|
|
||||||
vk: [
|
|
||||||
0xbf, 0xd5, 0xbc, 0x00, 0xc7, 0xc0, 0x22, 0xaa, 0x89, 0x01, 0xae, 0x08, 0x3c, 0x12,
|
|
||||||
0xd5, 0x4b, 0x82, 0xf0, 0xdd, 0xff, 0x8e, 0xd6, 0xdb, 0x9a, 0x12, 0xd5, 0x9a, 0x5e,
|
|
||||||
0xf6, 0xa5, 0xa2, 0xe0,
|
|
||||||
],
|
|
||||||
alpha: [
|
|
||||||
0xa2, 0xe8, 0xb9, 0xe1, 0x6d, 0x6f, 0xf3, 0xca, 0x6c, 0x53, 0xd4, 0xe8, 0x8a, 0xbb,
|
|
||||||
0xb9, 0x9b, 0xe7, 0xaf, 0x7e, 0x36, 0x59, 0x63, 0x1f, 0x1e, 0xae, 0x1e, 0xff, 0x23,
|
|
||||||
0x87, 0x4d, 0x8e, 0x0c,
|
|
||||||
],
|
|
||||||
rsk: [
|
|
||||||
0x70, 0x3f, 0x32, 0xa3, 0x41, 0x13, 0xea, 0xe1, 0xb0, 0x79, 0x1f, 0xfe, 0x9d, 0x88,
|
|
||||||
0x88, 0xf0, 0x01, 0x29, 0x9a, 0xe5, 0x19, 0x68, 0x60, 0x91, 0x91, 0x48, 0x99, 0xef,
|
|
||||||
0xcc, 0x6c, 0x66, 0x01,
|
|
||||||
],
|
|
||||||
rvk: [
|
|
||||||
0xeb, 0x92, 0x97, 0x03, 0x6c, 0xf5, 0x17, 0xe1, 0x5e, 0x9e, 0xfe, 0x39, 0x75, 0x32,
|
|
||||||
0x8d, 0xb4, 0x8e, 0xe7, 0xc2, 0x69, 0x4e, 0x94, 0x6d, 0xb2, 0x5f, 0x52, 0x87, 0x88,
|
|
||||||
0xf6, 0xa1, 0xdb, 0x14,
|
|
||||||
],
|
|
||||||
m: [
|
|
||||||
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
|
|
||||||
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
|
|
||||||
0x09, 0x09, 0x09, 0x09,
|
|
||||||
],
|
|
||||||
sig: [
|
|
||||||
0xce, 0x90, 0xdd, 0xf4, 0xaf, 0x21, 0xaa, 0xc4, 0xd9, 0x41, 0x93, 0xea, 0x16, 0xff,
|
|
||||||
0x35, 0xcd, 0x93, 0x79, 0x20, 0x4e, 0x7d, 0x8f, 0xf4, 0xc0, 0xf5, 0x41, 0x17, 0xab,
|
|
||||||
0xb1, 0x6b, 0x7c, 0x85, 0xa0, 0xb1, 0x97, 0xcf, 0x13, 0xab, 0x14, 0xd7, 0xc3, 0xba,
|
|
||||||
0x68, 0x01, 0x0a, 0xb8, 0x05, 0x12, 0x25, 0x91, 0x3b, 0xdb, 0xc3, 0x9a, 0x51, 0xf6,
|
|
||||||
0x03, 0x7a, 0xfc, 0x6c, 0xee, 0xcb, 0x0b, 0x06,
|
|
||||||
],
|
|
||||||
rsig: [
|
|
||||||
0xa8, 0x47, 0x74, 0x2e, 0x94, 0x01, 0xcf, 0x22, 0x39, 0x21, 0x3d, 0xc8, 0x81, 0x3e,
|
|
||||||
0x97, 0x72, 0xe9, 0x7a, 0xf8, 0xd6, 0x7a, 0xdf, 0xfe, 0xab, 0xc8, 0xe6, 0x7f, 0x5d,
|
|
||||||
0x2d, 0x90, 0xd0, 0xb4, 0x1b, 0xc2, 0x5b, 0x05, 0xf9, 0x4a, 0xce, 0x16, 0x8a, 0xec,
|
|
||||||
0xc6, 0x58, 0x3e, 0x18, 0xf7, 0x63, 0x74, 0x92, 0xf3, 0x7a, 0x9c, 0xa3, 0x00, 0x20,
|
|
||||||
0x2b, 0xc0, 0x65, 0xab, 0xd3, 0x80, 0xec, 0x00,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
use bitvec::{order::Lsb0, view::AsBits};
|
|
||||||
use group::{ff::PrimeField, Curve};
|
|
||||||
use incrementalmerkletree::{Hashable, Level};
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use subtle::CtOption;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
note::ExtractedNoteCommitment,
|
|
||||||
pedersen_hash::{pedersen_hash, Personalization},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const NOTE_COMMITMENT_TREE_DEPTH: u8 = 32;
|
|
||||||
pub type CommitmentTree =
|
|
||||||
incrementalmerkletree::frontier::CommitmentTree<Node, NOTE_COMMITMENT_TREE_DEPTH>;
|
|
||||||
pub type IncrementalWitness =
|
|
||||||
incrementalmerkletree::witness::IncrementalWitness<Node, NOTE_COMMITMENT_TREE_DEPTH>;
|
|
||||||
pub type MerklePath = incrementalmerkletree::MerklePath<Node, NOTE_COMMITMENT_TREE_DEPTH>;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref UNCOMMITTED_SAPLING: bls12_381::Scalar = bls12_381::Scalar::one();
|
|
||||||
static ref EMPTY_ROOTS: Vec<Node> = {
|
|
||||||
let mut v = vec![Node::empty_leaf()];
|
|
||||||
for d in 0..NOTE_COMMITMENT_TREE_DEPTH {
|
|
||||||
let next = Node::combine(d.into(), &v[usize::from(d)], &v[usize::from(d)]);
|
|
||||||
v.push(next);
|
|
||||||
}
|
|
||||||
v
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute a parent node in the Sapling commitment tree given its two children.
|
|
||||||
pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] {
|
|
||||||
merkle_hash_field(depth, lhs, rhs).to_repr()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merkle_hash_field(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> jubjub::Base {
|
|
||||||
let lhs = {
|
|
||||||
let mut tmp = [false; 256];
|
|
||||||
for (a, b) in tmp.iter_mut().zip(lhs.as_bits::<Lsb0>()) {
|
|
||||||
*a = *b;
|
|
||||||
}
|
|
||||||
tmp
|
|
||||||
};
|
|
||||||
|
|
||||||
let rhs = {
|
|
||||||
let mut tmp = [false; 256];
|
|
||||||
for (a, b) in tmp.iter_mut().zip(rhs.as_bits::<Lsb0>()) {
|
|
||||||
*a = *b;
|
|
||||||
}
|
|
||||||
tmp
|
|
||||||
};
|
|
||||||
|
|
||||||
jubjub::ExtendedPoint::from(pedersen_hash(
|
|
||||||
Personalization::MerkleTree(depth),
|
|
||||||
lhs.iter()
|
|
||||||
.copied()
|
|
||||||
.take(bls12_381::Scalar::NUM_BITS as usize)
|
|
||||||
.chain(
|
|
||||||
rhs.iter()
|
|
||||||
.copied()
|
|
||||||
.take(bls12_381::Scalar::NUM_BITS as usize),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.to_affine()
|
|
||||||
.get_u()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A node within the Sapling commitment tree.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct Node(jubjub::Base);
|
|
||||||
|
|
||||||
impl fmt::Debug for Node {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("Node")
|
|
||||||
.field("repr", &hex::encode(self.0.to_bytes()))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
/// Creates a tree leaf from the given Sapling note commitment.
|
|
||||||
pub fn from_cmu(value: &ExtractedNoteCommitment) -> Self {
|
|
||||||
Node(value.inner())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a new note commitment tree node from a [`bls12_381::Scalar`]
|
|
||||||
pub fn from_scalar(cmu: bls12_381::Scalar) -> Self {
|
|
||||||
Self(cmu)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a tree leaf from the bytes of a Sapling note commitment.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the provided bytes represent a non-canonical encoding.
|
|
||||||
pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Self> {
|
|
||||||
jubjub::Base::from_repr(bytes).map(Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the canonical byte representation of this node.
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
|
||||||
self.0.to_repr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hashable for Node {
|
|
||||||
fn empty_leaf() -> Self {
|
|
||||||
Node(*UNCOMMITTED_SAPLING)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn combine(level: Level, lhs: &Self, rhs: &Self) -> Self {
|
|
||||||
Node(merkle_hash_field(
|
|
||||||
level.into(),
|
|
||||||
&lhs.0.to_bytes(),
|
|
||||||
&rhs.0.to_bytes(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn empty_root(level: Level) -> Self {
|
|
||||||
EMPTY_ROOTS[<usize>::from(level)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Node> for bls12_381::Scalar {
|
|
||||||
fn from(node: Node) -> Self {
|
|
||||||
node.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
pub(super) mod testing {
|
|
||||||
use proptest::prelude::*;
|
|
||||||
|
|
||||||
use super::Node;
|
|
||||||
use crate::sapling::note::testing::arb_cmu;
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
pub fn arb_node()(cmu in arb_cmu()) -> Node {
|
|
||||||
Node::from_cmu(&cmu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
use blake2b_simd::Params;
|
|
||||||
use ff::Field;
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use super::{note_encryption::Zip212Enforcement, Rseed};
|
|
||||||
|
|
||||||
pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr {
|
|
||||||
let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
|
|
||||||
hasher.update(a);
|
|
||||||
hasher.update(b);
|
|
||||||
let ret = hasher.finalize();
|
|
||||||
jubjub::Fr::from_bytes_wide(ret.as_array())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_random_rseed<R: RngCore + CryptoRng>(
|
|
||||||
zip212_enforcement: Zip212Enforcement,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Rseed {
|
|
||||||
generate_random_rseed_internal(zip212_enforcement, rng)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn generate_random_rseed_internal<R: RngCore>(
|
|
||||||
zip212_enforcement: Zip212Enforcement,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Rseed {
|
|
||||||
match zip212_enforcement {
|
|
||||||
Zip212Enforcement::Off => Rseed::BeforeZip212(jubjub::Fr::random(rng)),
|
|
||||||
Zip212Enforcement::GracePeriod | Zip212Enforcement::On => {
|
|
||||||
let mut buffer = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut buffer);
|
|
||||||
Rseed::AfterZip212(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,264 +0,0 @@
|
||||||
//! Monetary values within the Sapling shielded pool.
|
|
||||||
//!
|
|
||||||
//! Values are represented in three places within the Sapling protocol:
|
|
||||||
//! - [`NoteValue`], the value of an individual note. It is an unsigned 64-bit integer
|
|
||||||
//! (with maximum value [`MAX_NOTE_VALUE`]), and is serialized in a note plaintext.
|
|
||||||
//! - [`ValueSum`], the sum of note values within a Sapling [`Bundle`]. It is represented
|
|
||||||
//! as an `i128` and places an upper bound on the maximum number of notes within a
|
|
||||||
//! single [`Bundle`].
|
|
||||||
//! - `valueBalanceSapling`, which is a signed 63-bit integer. This is represented
|
|
||||||
//! by a user-defined type parameter on [`Bundle`], returned by
|
|
||||||
//! [`Bundle::value_balance`] and [`SaplingBuilder::value_balance`].
|
|
||||||
//!
|
|
||||||
//! If your specific instantiation of the Sapling protocol requires a smaller bound on
|
|
||||||
//! valid note values (for example, Zcash's `MAX_MONEY` fits into a 51-bit integer), you
|
|
||||||
//! should enforce this in two ways:
|
|
||||||
//!
|
|
||||||
//! - Define your `valueBalanceSapling` type to enforce your valid value range. This can
|
|
||||||
//! be checked in its `TryFrom<i64>` implementation.
|
|
||||||
//! - Define your own "amount" type for note values, and convert it to `NoteValue` prior
|
|
||||||
//! to calling [`SaplingBuilder::add_output`].
|
|
||||||
//!
|
|
||||||
//! Inside the circuit, note values are constrained to be unsigned 64-bit integers.
|
|
||||||
//!
|
|
||||||
//! # Caution!
|
|
||||||
//!
|
|
||||||
//! An `i64` is _not_ a signed 64-bit integer! The [Rust documentation] calls `i64` the
|
|
||||||
//! 64-bit signed integer type, which is true in the sense that its encoding in memory
|
|
||||||
//! takes up 64 bits. Numerically, however, `i64` is a signed 63-bit integer.
|
|
||||||
//!
|
|
||||||
//! Fortunately, users of this crate should never need to construct [`ValueSum`] directly;
|
|
||||||
//! you should only need to interact with [`NoteValue`] (which can be safely constructed
|
|
||||||
//! from a `u64`) and `valueBalanceSapling` (which can be represented as an `i64`).
|
|
||||||
//!
|
|
||||||
//! [`Bundle`]: crate::sapling::Bundle
|
|
||||||
//! [`Bundle::value_balance`]: crate::sapling::Bundle::value_balance
|
|
||||||
//! [`SaplingBuilder::value_balance`]: crate::sapling::builder::SaplingBuilder::value_balance
|
|
||||||
//! [`SaplingBuilder::add_output`]: crate::sapling::builder::SaplingBuilder::add_output
|
|
||||||
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
|
|
||||||
|
|
||||||
use bitvec::{array::BitArray, order::Lsb0};
|
|
||||||
use ff::{Field, PrimeField};
|
|
||||||
use group::GroupEncoding;
|
|
||||||
use rand::RngCore;
|
|
||||||
use subtle::CtOption;
|
|
||||||
|
|
||||||
use super::constants::{VALUE_COMMITMENT_RANDOMNESS_GENERATOR, VALUE_COMMITMENT_VALUE_GENERATOR};
|
|
||||||
|
|
||||||
mod sums;
|
|
||||||
pub use sums::{CommitmentSum, OverflowError, TrapdoorSum, ValueSum};
|
|
||||||
|
|
||||||
/// Maximum note value.
|
|
||||||
pub const MAX_NOTE_VALUE: u64 = u64::MAX;
|
|
||||||
|
|
||||||
/// The non-negative value of an individual Sapling note.
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct NoteValue(u64);
|
|
||||||
|
|
||||||
impl NoteValue {
|
|
||||||
/// Returns the raw underlying value.
|
|
||||||
pub fn inner(&self) -> u64 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a note value from its raw numeric value.
|
|
||||||
///
|
|
||||||
/// This only enforces that the value is an unsigned 64-bit integer. Callers should
|
|
||||||
/// enforce any additional constraints on the value's valid range themselves.
|
|
||||||
pub fn from_raw(value: u64) -> Self {
|
|
||||||
NoteValue(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_bytes(bytes: [u8; 8]) -> Self {
|
|
||||||
NoteValue(u64::from_le_bytes(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_le_bits(self) -> BitArray<[u8; 8], Lsb0> {
|
|
||||||
BitArray::<_, Lsb0>::new(self.0.to_le_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The blinding factor for a [`ValueCommitment`].
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ValueCommitTrapdoor(jubjub::Scalar);
|
|
||||||
|
|
||||||
impl ValueCommitTrapdoor {
|
|
||||||
/// Generates a new value commitment trapdoor.
|
|
||||||
///
|
|
||||||
/// This is public for access by `zcash_proofs`.
|
|
||||||
pub fn random(rng: impl RngCore) -> Self {
|
|
||||||
ValueCommitTrapdoor(jubjub::Scalar::random(rng))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs `ValueCommitTrapdoor` from the byte representation of a scalar.
|
|
||||||
///
|
|
||||||
/// Returns a `None` [`CtOption`] if `bytes` is not a canonical representation of a
|
|
||||||
/// Jubjub scalar.
|
|
||||||
///
|
|
||||||
/// This is a low-level API, requiring a detailed understanding of the
|
|
||||||
/// [use of value commitment trapdoors][saplingbalance] in the Zcash protocol
|
|
||||||
/// to use correctly and securely. It is intended to be used in combination
|
|
||||||
/// with [`ValueCommitment::derive`].
|
|
||||||
///
|
|
||||||
/// [saplingbalance]: https://zips.z.cash/protocol/protocol.pdf#saplingbalance
|
|
||||||
pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Self> {
|
|
||||||
jubjub::Scalar::from_repr(bytes).map(ValueCommitTrapdoor)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inner Jubjub scalar representing this trapdoor.
|
|
||||||
///
|
|
||||||
/// This is public for access by `zcash_proofs`.
|
|
||||||
pub fn inner(&self) -> jubjub::Scalar {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A commitment to a [`ValueSum`].
|
|
||||||
///
|
|
||||||
/// # Consensus rules
|
|
||||||
///
|
|
||||||
/// The Zcash Protocol Spec requires Sapling Spend Descriptions and Output Descriptions to
|
|
||||||
/// not contain a small order `ValueCommitment`. However, the `ValueCommitment` type as
|
|
||||||
/// specified (and implemented here) may contain a small order point. In practice, it will
|
|
||||||
/// not occur:
|
|
||||||
/// - [`ValueCommitment::derive`] will only produce a small order point if both the given
|
|
||||||
/// [`NoteValue`] and [`ValueCommitTrapdoor`] are zero. However, the only constructor
|
|
||||||
/// available for `ValueCommitTrapdoor` is [`ValueCommitTrapdoor::random`], which will
|
|
||||||
/// produce zero with negligible probability (assuming a non-broken PRNG).
|
|
||||||
/// - [`ValueCommitment::from_bytes_not_small_order`] enforces this by definition, and is
|
|
||||||
/// the only constructor that can be used with data received over the network.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ValueCommitment(jubjub::ExtendedPoint);
|
|
||||||
|
|
||||||
impl ValueCommitment {
|
|
||||||
/// Derives a `ValueCommitment` by $\mathsf{ValueCommit^{Sapling}}$.
|
|
||||||
///
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.8.3: Homomorphic Pedersen commitments (Sapling and Orchard)][concretehomomorphiccommit].
|
|
||||||
///
|
|
||||||
/// [concretehomomorphiccommit]: https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
|
|
||||||
pub fn derive(value: NoteValue, rcv: ValueCommitTrapdoor) -> Self {
|
|
||||||
let cv = (VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Scalar::from(value.0))
|
|
||||||
+ (VALUE_COMMITMENT_RANDOMNESS_GENERATOR * rcv.0);
|
|
||||||
|
|
||||||
ValueCommitment(cv.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inner Jubjub point representing this value commitment.
|
|
||||||
///
|
|
||||||
/// This is public for access by `zcash_proofs`.
|
|
||||||
pub fn as_inner(&self) -> &jubjub::ExtendedPoint {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserializes a value commitment from its byte representation.
|
|
||||||
///
|
|
||||||
/// Returns `None` if `bytes` is an invalid representation of a Jubjub point, or the
|
|
||||||
/// resulting point is of small order.
|
|
||||||
///
|
|
||||||
/// This method can be used to enforce the "not small order" consensus rules defined
|
|
||||||
/// in [Zcash Protocol Spec § 4.4: Spend Descriptions][spenddesc] and
|
|
||||||
/// [§ 4.5: Output Descriptions][outputdesc].
|
|
||||||
///
|
|
||||||
/// [spenddesc]: https://zips.z.cash/protocol/protocol.pdf#spenddesc
|
|
||||||
/// [outputdesc]: https://zips.z.cash/protocol/protocol.pdf#outputdesc
|
|
||||||
pub fn from_bytes_not_small_order(bytes: &[u8; 32]) -> CtOption<ValueCommitment> {
|
|
||||||
jubjub::ExtendedPoint::from_bytes(bytes)
|
|
||||||
.and_then(|cv| CtOption::new(ValueCommitment(cv), !cv.is_small_order()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serializes this value commitment to its canonical byte representation.
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
|
||||||
self.0.to_bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generators for property testing.
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
|
|
||||||
pub mod testing {
|
|
||||||
use proptest::prelude::*;
|
|
||||||
|
|
||||||
use super::{NoteValue, ValueCommitTrapdoor, MAX_NOTE_VALUE};
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// Generate an arbitrary value in the range of valid nonnegative amounts.
|
|
||||||
pub fn arb_note_value()(value in 0u64..MAX_NOTE_VALUE) -> NoteValue {
|
|
||||||
NoteValue(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// Generate an arbitrary value in the range of valid positive amounts less than a
|
|
||||||
/// specified value.
|
|
||||||
pub fn arb_note_value_bounded(max: u64)(value in 0u64..max) -> NoteValue {
|
|
||||||
NoteValue(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// Generate an arbitrary value in the range of valid positive amounts less than a
|
|
||||||
/// specified value.
|
|
||||||
pub fn arb_positive_note_value(max: u64)(value in 1u64..max) -> NoteValue {
|
|
||||||
NoteValue(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// Generate an arbitrary Jubjub scalar.
|
|
||||||
fn arb_scalar()(bytes in prop::array::uniform32(0u8..)) -> jubjub::Scalar {
|
|
||||||
// Instead of rejecting out-of-range bytes, let's reduce them.
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
buf[..32].copy_from_slice(&bytes);
|
|
||||||
jubjub::Scalar::from_bytes_wide(&buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_compose! {
|
|
||||||
/// Generate an arbitrary ValueCommitTrapdoor
|
|
||||||
pub fn arb_trapdoor()(rcv in arb_scalar()) -> ValueCommitTrapdoor {
|
|
||||||
ValueCommitTrapdoor(rcv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use proptest::prelude::*;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
testing::{arb_note_value_bounded, arb_trapdoor},
|
|
||||||
CommitmentSum, OverflowError, TrapdoorSum, ValueCommitment, ValueSum,
|
|
||||||
};
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#[test]
|
|
||||||
fn bsk_consistent_with_bvk(
|
|
||||||
values in (1usize..10).prop_flat_map(|n_values| prop::collection::vec(
|
|
||||||
(arb_note_value_bounded((i64::MAX as u64) / (n_values as u64)), arb_trapdoor()),
|
|
||||||
n_values,
|
|
||||||
))
|
|
||||||
) {
|
|
||||||
let value_balance: i64 = values
|
|
||||||
.iter()
|
|
||||||
.map(|(value, _)| value)
|
|
||||||
.sum::<Result<ValueSum, OverflowError>>()
|
|
||||||
.expect("we generate values that won't overflow")
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let bsk = values
|
|
||||||
.iter()
|
|
||||||
.map(|(_, rcv)| rcv)
|
|
||||||
.sum::<TrapdoorSum>()
|
|
||||||
.into_bsk();
|
|
||||||
|
|
||||||
let bvk = values
|
|
||||||
.into_iter()
|
|
||||||
.map(|(value, rcv)| ValueCommitment::derive(value, rcv))
|
|
||||||
.sum::<CommitmentSum>()
|
|
||||||
.into_bvk(value_balance);
|
|
||||||
|
|
||||||
assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
use core::fmt::{self, Debug};
|
|
||||||
use core::iter::Sum;
|
|
||||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
|
||||||
|
|
||||||
use group::GroupEncoding;
|
|
||||||
use redjubjub::Binding;
|
|
||||||
|
|
||||||
use super::{NoteValue, ValueCommitTrapdoor, ValueCommitment};
|
|
||||||
use crate::sapling::constants::VALUE_COMMITMENT_VALUE_GENERATOR;
|
|
||||||
|
|
||||||
/// A value operation overflowed.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct OverflowError;
|
|
||||||
|
|
||||||
impl fmt::Display for OverflowError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Sapling value operation overflowed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for OverflowError {}
|
|
||||||
|
|
||||||
/// A sum of Sapling note values.
|
|
||||||
///
|
|
||||||
/// [Zcash Protocol Spec § 4.13: Balance and Binding Signature (Sapling)][saplingbalance]
|
|
||||||
/// constrains the range of this type to between `[-(r_J - 1)/2..(r_J - 1)/2]` in the
|
|
||||||
/// abstract protocol, and `[−38913406623490299131842..104805176454780817500623]` in the
|
|
||||||
/// concrete Zcash protocol. We represent it as an `i128`, which has a range large enough
|
|
||||||
/// to handle Zcash transactions while small enough to ensure the abstract protocol bounds
|
|
||||||
/// are not breached.
|
|
||||||
///
|
|
||||||
/// [saplingbalance]: https://zips.z.cash/protocol/protocol.pdf#saplingbalance
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct ValueSum(i128);
|
|
||||||
|
|
||||||
impl ValueSum {
|
|
||||||
/// Initializes a sum of `NoteValue`s to zero.
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
ValueSum(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<NoteValue> for ValueSum {
|
|
||||||
type Output = Option<ValueSum>;
|
|
||||||
|
|
||||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
|
||||||
fn add(self, rhs: NoteValue) -> Self::Output {
|
|
||||||
self.0.checked_add(rhs.0.into()).map(ValueSum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<NoteValue> for ValueSum {
|
|
||||||
type Output = Option<ValueSum>;
|
|
||||||
|
|
||||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
|
||||||
fn sub(self, rhs: NoteValue) -> Self::Output {
|
|
||||||
self.0.checked_sub(rhs.0.into()).map(ValueSum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Sum<&'a NoteValue> for Result<ValueSum, OverflowError> {
|
|
||||||
fn sum<I: Iterator<Item = &'a NoteValue>>(iter: I) -> Self {
|
|
||||||
iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + *v).ok_or(OverflowError))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sum<NoteValue> for Result<ValueSum, OverflowError> {
|
|
||||||
fn sum<I: Iterator<Item = NoteValue>>(iter: I) -> Self {
|
|
||||||
iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + v).ok_or(OverflowError))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ValueSum> for i64 {
|
|
||||||
type Error = OverflowError;
|
|
||||||
|
|
||||||
fn try_from(v: ValueSum) -> Result<i64, Self::Error> {
|
|
||||||
i64::try_from(v.0).map_err(|_| OverflowError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A sum of Sapling value commitment blinding factors.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct TrapdoorSum(jubjub::Scalar);
|
|
||||||
|
|
||||||
impl TrapdoorSum {
|
|
||||||
/// Initializes a sum of `ValueCommitTrapdoor`s to zero.
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
TrapdoorSum(jubjub::Scalar::zero())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transform this trapdoor sum into the corresponding RedJubjub private key.
|
|
||||||
///
|
|
||||||
/// This is public for access by `zcash_proofs`.
|
|
||||||
pub fn into_bsk(self) -> redjubjub::SigningKey<Binding> {
|
|
||||||
redjubjub::SigningKey::try_from(self.0.to_bytes())
|
|
||||||
.expect("valid scalars are valid signing keys")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
|
|
||||||
type Output = TrapdoorSum;
|
|
||||||
|
|
||||||
fn add(self, rhs: &Self) -> Self::Output {
|
|
||||||
TrapdoorSum(self.0 + rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<&ValueCommitTrapdoor> for TrapdoorSum {
|
|
||||||
type Output = TrapdoorSum;
|
|
||||||
|
|
||||||
fn add(self, rhs: &ValueCommitTrapdoor) -> Self::Output {
|
|
||||||
TrapdoorSum(self.0 + rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign<&ValueCommitTrapdoor> for TrapdoorSum {
|
|
||||||
fn add_assign(&mut self, rhs: &ValueCommitTrapdoor) {
|
|
||||||
self.0 += rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
|
|
||||||
type Output = TrapdoorSum;
|
|
||||||
|
|
||||||
fn sub(self, rhs: &Self) -> Self::Output {
|
|
||||||
TrapdoorSum(self.0 - rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<TrapdoorSum> for TrapdoorSum {
|
|
||||||
type Output = TrapdoorSum;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
TrapdoorSum(self.0 - rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubAssign<&ValueCommitTrapdoor> for TrapdoorSum {
|
|
||||||
fn sub_assign(&mut self, rhs: &ValueCommitTrapdoor) {
|
|
||||||
self.0 -= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Sum<&'a ValueCommitTrapdoor> for TrapdoorSum {
|
|
||||||
fn sum<I: Iterator<Item = &'a ValueCommitTrapdoor>>(iter: I) -> Self {
|
|
||||||
iter.fold(TrapdoorSum::zero(), |acc, cv| acc + cv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A sum of Sapling value commitments.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct CommitmentSum(jubjub::ExtendedPoint);
|
|
||||||
|
|
||||||
impl CommitmentSum {
|
|
||||||
/// Initializes a sum of `ValueCommitment`s to zero.
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
CommitmentSum(jubjub::ExtendedPoint::identity())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transform this value commitment sum into the corresponding RedJubjub public key.
|
|
||||||
///
|
|
||||||
/// This is public for access by `zcash_proofs`.
|
|
||||||
pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::VerificationKey<Binding> {
|
|
||||||
let value: i64 = value_balance.into();
|
|
||||||
|
|
||||||
// Compute the absolute value.
|
|
||||||
let abs_value = match value.checked_abs() {
|
|
||||||
Some(v) => u64::try_from(v).expect("v is non-negative"),
|
|
||||||
None => 1u64 << 63,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Construct the field representation of the signed value.
|
|
||||||
let value_balance = if value.is_negative() {
|
|
||||||
-jubjub::Scalar::from(abs_value)
|
|
||||||
} else {
|
|
||||||
jubjub::Scalar::from(abs_value)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Subtract `value_balance` from the sum to get the final bvk.
|
|
||||||
let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance;
|
|
||||||
|
|
||||||
redjubjub::VerificationKey::try_from(bvk.to_bytes())
|
|
||||||
.expect("valid points are valid verification keys")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<&ValueCommitment> for ValueCommitment {
|
|
||||||
type Output = CommitmentSum;
|
|
||||||
|
|
||||||
fn add(self, rhs: &Self) -> Self::Output {
|
|
||||||
CommitmentSum(self.0 + rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<&ValueCommitment> for CommitmentSum {
|
|
||||||
type Output = CommitmentSum;
|
|
||||||
|
|
||||||
fn add(self, rhs: &ValueCommitment) -> Self::Output {
|
|
||||||
CommitmentSum(self.0 + rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign<&ValueCommitment> for CommitmentSum {
|
|
||||||
fn add_assign(&mut self, rhs: &ValueCommitment) {
|
|
||||||
self.0 += rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<&ValueCommitment> for ValueCommitment {
|
|
||||||
type Output = CommitmentSum;
|
|
||||||
|
|
||||||
fn sub(self, rhs: &Self) -> Self::Output {
|
|
||||||
CommitmentSum(self.0 - rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubAssign<&ValueCommitment> for CommitmentSum {
|
|
||||||
fn sub_assign(&mut self, rhs: &ValueCommitment) {
|
|
||||||
self.0 -= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<CommitmentSum> for CommitmentSum {
|
|
||||||
type Output = CommitmentSum;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
CommitmentSum(self.0 - rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sum<ValueCommitment> for CommitmentSum {
|
|
||||||
fn sum<I: Iterator<Item = ValueCommitment>>(iter: I) -> Self {
|
|
||||||
iter.fold(CommitmentSum::zero(), |acc, cv| acc + &cv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Sum<&'a ValueCommitment> for CommitmentSum {
|
|
||||||
fn sum<I: Iterator<Item = &'a ValueCommitment>>(iter: I) -> Self {
|
|
||||||
iter.fold(CommitmentSum::zero(), |acc, cv| acc + cv)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
use bellman::{gadgets::multipack, groth16::Proof};
|
|
||||||
use bls12_381::Bls12;
|
|
||||||
use group::{ff::PrimeField, Curve};
|
|
||||||
use redjubjub::{Binding, SpendAuth};
|
|
||||||
|
|
||||||
use crate::sapling::{
|
|
||||||
note::ExtractedNoteCommitment,
|
|
||||||
value::{CommitmentSum, ValueCommitment},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod single;
|
|
||||||
pub use single::SaplingVerificationContext;
|
|
||||||
|
|
||||||
mod batch;
|
|
||||||
pub use batch::BatchValidator;
|
|
||||||
|
|
||||||
/// A context object for verifying the Sapling components of a Zcash transaction.
|
|
||||||
struct SaplingVerificationContextInner {
|
|
||||||
// (sum of the Spend value commitments) - (sum of the Output value commitments)
|
|
||||||
cv_sum: CommitmentSum,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SaplingVerificationContextInner {
|
|
||||||
/// Construct a new context to be used with a single transaction.
|
|
||||||
fn new() -> Self {
|
|
||||||
SaplingVerificationContextInner {
|
|
||||||
cv_sum: CommitmentSum::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform consensus checks on a Sapling SpendDescription, while
|
|
||||||
/// accumulating its value commitment inside the context for later use.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn check_spend<C>(
|
|
||||||
&mut self,
|
|
||||||
cv: &ValueCommitment,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
nullifier: &[u8; 32],
|
|
||||||
rk: &redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
zkproof: Proof<Bls12>,
|
|
||||||
verifier_ctx: &mut C,
|
|
||||||
spend_auth_sig_verifier: impl FnOnce(&mut C, &redjubjub::VerificationKey<SpendAuth>) -> bool,
|
|
||||||
proof_verifier: impl FnOnce(&mut C, Proof<Bls12>, [bls12_381::Scalar; 7]) -> bool,
|
|
||||||
) -> bool {
|
|
||||||
// The "cv is not small order" happens when a SpendDescription is deserialized.
|
|
||||||
// This happens when transactions or blocks are received over the network, or when
|
|
||||||
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
|
|
||||||
let rk_affine = jubjub::AffinePoint::from_bytes((*rk).into()).unwrap();
|
|
||||||
if rk_affine.is_small_order().into() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate the value commitment in the context
|
|
||||||
self.cv_sum += cv;
|
|
||||||
|
|
||||||
// Grab the nullifier as a sequence of bytes
|
|
||||||
let nullifier = &nullifier[..];
|
|
||||||
|
|
||||||
// Verify the spend_auth_sig
|
|
||||||
if !spend_auth_sig_verifier(verifier_ctx, rk) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct public input for circuit
|
|
||||||
let mut public_input = [bls12_381::Scalar::zero(); 7];
|
|
||||||
{
|
|
||||||
let affine = rk_affine;
|
|
||||||
let (u, v) = (affine.get_u(), affine.get_v());
|
|
||||||
public_input[0] = u;
|
|
||||||
public_input[1] = v;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let affine = cv.as_inner().to_affine();
|
|
||||||
let (u, v) = (affine.get_u(), affine.get_v());
|
|
||||||
public_input[2] = u;
|
|
||||||
public_input[3] = v;
|
|
||||||
}
|
|
||||||
public_input[4] = anchor;
|
|
||||||
|
|
||||||
// Add the nullifier through multiscalar packing
|
|
||||||
{
|
|
||||||
let nullifier = multipack::bytes_to_bits_le(nullifier);
|
|
||||||
let nullifier = multipack::compute_multipacking(&nullifier);
|
|
||||||
|
|
||||||
assert_eq!(nullifier.len(), 2);
|
|
||||||
|
|
||||||
public_input[5] = nullifier[0];
|
|
||||||
public_input[6] = nullifier[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the proof
|
|
||||||
proof_verifier(verifier_ctx, zkproof, public_input)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform consensus checks on a Sapling OutputDescription, while
|
|
||||||
/// accumulating its value commitment inside the context for later use.
|
|
||||||
fn check_output(
|
|
||||||
&mut self,
|
|
||||||
cv: &ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
epk: jubjub::ExtendedPoint,
|
|
||||||
zkproof: Proof<Bls12>,
|
|
||||||
proof_verifier: impl FnOnce(Proof<Bls12>, [bls12_381::Scalar; 5]) -> bool,
|
|
||||||
) -> bool {
|
|
||||||
// The "cv is not small order" happens when an OutputDescription is deserialized.
|
|
||||||
// This happens when transactions or blocks are received over the network, or when
|
|
||||||
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
|
|
||||||
if epk.is_small_order().into() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate the value commitment in the context
|
|
||||||
self.cv_sum -= cv;
|
|
||||||
|
|
||||||
// Construct public input for circuit
|
|
||||||
let mut public_input = [bls12_381::Scalar::zero(); 5];
|
|
||||||
{
|
|
||||||
let affine = cv.as_inner().to_affine();
|
|
||||||
let (u, v) = (affine.get_u(), affine.get_v());
|
|
||||||
public_input[0] = u;
|
|
||||||
public_input[1] = v;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let affine = epk.to_affine();
|
|
||||||
let (u, v) = (affine.get_u(), affine.get_v());
|
|
||||||
public_input[2] = u;
|
|
||||||
public_input[3] = v;
|
|
||||||
}
|
|
||||||
public_input[4] = bls12_381::Scalar::from_repr(cmu.to_bytes()).unwrap();
|
|
||||||
|
|
||||||
// Verify the proof
|
|
||||||
proof_verifier(zkproof, public_input)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform consensus checks on the valueBalance and bindingSig parts of a
|
|
||||||
/// Sapling transaction. All SpendDescriptions and OutputDescriptions must
|
|
||||||
/// have been checked before calling this function.
|
|
||||||
fn final_check<V: Into<i64>>(
|
|
||||||
&self,
|
|
||||||
value_balance: V,
|
|
||||||
binding_sig_verifier: impl FnOnce(redjubjub::VerificationKey<Binding>) -> bool,
|
|
||||||
) -> bool {
|
|
||||||
// Compute the final bvk.
|
|
||||||
let bvk = self.cv_sum.into_bvk(value_balance);
|
|
||||||
|
|
||||||
// Verify the binding_sig
|
|
||||||
binding_sig_verifier(bvk)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
use bellman::groth16;
|
|
||||||
use bls12_381::Bls12;
|
|
||||||
use group::GroupEncoding;
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use super::SaplingVerificationContextInner;
|
|
||||||
use crate::sapling::{
|
|
||||||
bundle::{Authorized, Bundle},
|
|
||||||
circuit::{OutputVerifyingKey, SpendVerifyingKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Batch validation context for Sapling.
|
|
||||||
///
|
|
||||||
/// This batch-validates Spend and Output proofs, and RedJubjub signatures.
|
|
||||||
///
|
|
||||||
/// Signatures are verified assuming ZIP 216 is active.
|
|
||||||
pub struct BatchValidator {
|
|
||||||
bundles_added: bool,
|
|
||||||
spend_proofs: groth16::batch::Verifier<Bls12>,
|
|
||||||
output_proofs: groth16::batch::Verifier<Bls12>,
|
|
||||||
signatures: redjubjub::batch::Verifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for BatchValidator {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BatchValidator {
|
|
||||||
/// Constructs a new batch validation context.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
BatchValidator {
|
|
||||||
bundles_added: false,
|
|
||||||
spend_proofs: groth16::batch::Verifier::new(),
|
|
||||||
output_proofs: groth16::batch::Verifier::new(),
|
|
||||||
signatures: redjubjub::batch::Verifier::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks the bundle against Sapling-specific consensus rules, and adds its proof and
|
|
||||||
/// signatures to the validator.
|
|
||||||
///
|
|
||||||
/// Returns `false` if the bundle doesn't satisfy all of the consensus rules. This
|
|
||||||
/// `BatchValidator` can continue to be used regardless, but some or all of the proofs
|
|
||||||
/// and signatures from this bundle may have already been added to the batch even if
|
|
||||||
/// it fails other consensus rules.
|
|
||||||
pub fn check_bundle<V: Copy + Into<i64>>(
|
|
||||||
&mut self,
|
|
||||||
bundle: Bundle<Authorized, V>,
|
|
||||||
sighash: [u8; 32],
|
|
||||||
) -> bool {
|
|
||||||
self.bundles_added = true;
|
|
||||||
|
|
||||||
let mut ctx = SaplingVerificationContextInner::new();
|
|
||||||
|
|
||||||
for spend in bundle.shielded_spends() {
|
|
||||||
// Deserialize the proof
|
|
||||||
let zkproof = match groth16::Proof::read(&spend.zkproof()[..]) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check the Spend consensus rules, and batch its proof and spend
|
|
||||||
// authorization signature.
|
|
||||||
let consensus_rules_passed = ctx.check_spend(
|
|
||||||
spend.cv(),
|
|
||||||
*spend.anchor(),
|
|
||||||
&spend.nullifier().0,
|
|
||||||
spend.rk(),
|
|
||||||
zkproof,
|
|
||||||
self,
|
|
||||||
|this, rk| {
|
|
||||||
this.signatures
|
|
||||||
.queue(((*rk).into(), *spend.spend_auth_sig(), &sighash));
|
|
||||||
true
|
|
||||||
},
|
|
||||||
|this, proof, public_inputs| {
|
|
||||||
this.spend_proofs.queue((proof, public_inputs.to_vec()));
|
|
||||||
true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if !consensus_rules_passed {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for output in bundle.shielded_outputs() {
|
|
||||||
// Deserialize the ephemeral key
|
|
||||||
let epk = match jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key().0).into() {
|
|
||||||
Some(p) => p,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Deserialize the proof
|
|
||||||
let zkproof = match groth16::Proof::read(&output.zkproof()[..]) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check the Output consensus rules, and batch its proof.
|
|
||||||
let consensus_rules_passed = ctx.check_output(
|
|
||||||
output.cv(),
|
|
||||||
*output.cmu(),
|
|
||||||
epk,
|
|
||||||
zkproof,
|
|
||||||
|proof, public_inputs| {
|
|
||||||
self.output_proofs.queue((proof, public_inputs.to_vec()));
|
|
||||||
true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if !consensus_rules_passed {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the whole-bundle consensus rules, and batch the binding signature.
|
|
||||||
ctx.final_check(*bundle.value_balance(), |bvk| {
|
|
||||||
self.signatures
|
|
||||||
.queue((bvk.into(), bundle.authorization().binding_sig, &sighash));
|
|
||||||
true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Batch-validates the accumulated bundles.
|
|
||||||
///
|
|
||||||
/// Returns `true` if every proof and signature in every bundle added to the batch
|
|
||||||
/// validator is valid, or `false` if one or more are invalid. No attempt is made to
|
|
||||||
/// figure out which of the accumulated bundles might be invalid; if that information
|
|
||||||
/// is desired, construct separate [`BatchValidator`]s for sub-batches of the bundles.
|
|
||||||
pub fn validate<R: RngCore + CryptoRng>(
|
|
||||||
self,
|
|
||||||
spend_vk: &SpendVerifyingKey,
|
|
||||||
output_vk: &OutputVerifyingKey,
|
|
||||||
mut rng: R,
|
|
||||||
) -> bool {
|
|
||||||
if !self.bundles_added {
|
|
||||||
// An empty batch is always valid, but is not free to run; skip it.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = self.signatures.verify(&mut rng) {
|
|
||||||
tracing::debug!("Signature batch validation failed: {}", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "multicore")]
|
|
||||||
let verify_proofs = |batch: groth16::batch::Verifier<Bls12>, vk| batch.verify_multicore(vk);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "multicore"))]
|
|
||||||
let mut verify_proofs =
|
|
||||||
|batch: groth16::batch::Verifier<Bls12>, vk| batch.verify(&mut rng, vk);
|
|
||||||
|
|
||||||
if verify_proofs(self.spend_proofs, &spend_vk.0).is_err() {
|
|
||||||
tracing::debug!("Spend proof batch validation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if verify_proofs(self.output_proofs, &output_vk.0).is_err() {
|
|
||||||
tracing::debug!("Output proof batch validation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
use bellman::groth16::{verify_proof, Proof};
|
|
||||||
use bls12_381::Bls12;
|
|
||||||
use redjubjub::{Binding, SpendAuth};
|
|
||||||
|
|
||||||
use super::SaplingVerificationContextInner;
|
|
||||||
use crate::sapling::{
|
|
||||||
circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey},
|
|
||||||
note::ExtractedNoteCommitment,
|
|
||||||
value::ValueCommitment,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A context object for verifying the Sapling components of a single Zcash transaction.
|
|
||||||
pub struct SaplingVerificationContext {
|
|
||||||
inner: SaplingVerificationContextInner,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SaplingVerificationContext {
|
|
||||||
/// Construct a new context to be used with a single transaction.
|
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
SaplingVerificationContext {
|
|
||||||
inner: SaplingVerificationContextInner::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform consensus checks on a Sapling SpendDescription, while
|
|
||||||
/// accumulating its value commitment inside the context for later use.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn check_spend(
|
|
||||||
&mut self,
|
|
||||||
cv: &ValueCommitment,
|
|
||||||
anchor: bls12_381::Scalar,
|
|
||||||
nullifier: &[u8; 32],
|
|
||||||
rk: redjubjub::VerificationKey<SpendAuth>,
|
|
||||||
sighash_value: &[u8; 32],
|
|
||||||
spend_auth_sig: redjubjub::Signature<SpendAuth>,
|
|
||||||
zkproof: Proof<Bls12>,
|
|
||||||
verifying_key: &PreparedSpendVerifyingKey,
|
|
||||||
) -> bool {
|
|
||||||
self.inner.check_spend(
|
|
||||||
cv,
|
|
||||||
anchor,
|
|
||||||
nullifier,
|
|
||||||
&rk,
|
|
||||||
zkproof,
|
|
||||||
&mut (),
|
|
||||||
|_, rk| rk.verify(sighash_value, &spend_auth_sig).is_ok(),
|
|
||||||
|_, proof, public_inputs| {
|
|
||||||
verify_proof(&verifying_key.0, &proof, &public_inputs[..]).is_ok()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform consensus checks on a Sapling OutputDescription, while
|
|
||||||
/// accumulating its value commitment inside the context for later use.
|
|
||||||
pub fn check_output(
|
|
||||||
&mut self,
|
|
||||||
cv: &ValueCommitment,
|
|
||||||
cmu: ExtractedNoteCommitment,
|
|
||||||
epk: jubjub::ExtendedPoint,
|
|
||||||
zkproof: Proof<Bls12>,
|
|
||||||
verifying_key: &PreparedOutputVerifyingKey,
|
|
||||||
) -> bool {
|
|
||||||
self.inner
|
|
||||||
.check_output(cv, cmu, epk, zkproof, |proof, public_inputs| {
|
|
||||||
verify_proof(&verifying_key.0, &proof, &public_inputs[..]).is_ok()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform consensus checks on the valueBalance and bindingSig parts of a
|
|
||||||
/// Sapling transaction. All SpendDescriptions and OutputDescriptions must
|
|
||||||
/// have been checked before calling this function.
|
|
||||||
pub fn final_check<V: Into<i64>>(
|
|
||||||
&self,
|
|
||||||
value_balance: V,
|
|
||||||
sighash_value: &[u8; 32],
|
|
||||||
binding_sig: redjubjub::Signature<Binding>,
|
|
||||||
) -> bool {
|
|
||||||
self.inner.final_check(value_balance, |bvk| {
|
|
||||||
bvk.verify(sighash_value, &binding_sig).is_ok()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
||||||
pub(crate) mod note_encryption;
|
|
File diff suppressed because it is too large
Load Diff
|
@ -47,10 +47,10 @@ fn read_cmu<R: Read>(mut reader: R) -> io::Result<ExtractedNoteCommitment> {
|
||||||
|
|
||||||
/// Consensus rules (§7.3) & (§7.4):
|
/// Consensus rules (§7.3) & (§7.4):
|
||||||
/// - Canonical encoding is enforced here
|
/// - Canonical encoding is enforced here
|
||||||
pub fn read_base<R: Read>(mut reader: R, field: &str) -> io::Result<bls12_381::Scalar> {
|
pub fn read_base<R: Read>(mut reader: R, field: &str) -> io::Result<jubjub::Base> {
|
||||||
let mut f = [0u8; 32];
|
let mut f = [0u8; 32];
|
||||||
reader.read_exact(&mut f)?;
|
reader.read_exact(&mut f)?;
|
||||||
Option::from(bls12_381::Scalar::from_repr(f)).ok_or_else(|| {
|
Option::from(jubjub::Base::from_repr(f)).ok_or_else(|| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
format!("{} not in field", field),
|
format!("{} not in field", field),
|
||||||
|
@ -379,7 +379,7 @@ pub(crate) fn read_v5_bundle<R: Read>(
|
||||||
.map(|(od_5, zkproof)| od_5.into_output_description(zkproof))
|
.map(|(od_5, zkproof)| od_5.into_output_description(zkproof))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(binding_sig.map(|binding_sig| {
|
Ok(binding_sig.and_then(|binding_sig| {
|
||||||
Bundle::from_parts(
|
Bundle::from_parts(
|
||||||
shielded_spends,
|
shielded_spends,
|
||||||
shielded_outputs,
|
shielded_outputs,
|
||||||
|
|
|
@ -646,7 +646,7 @@ impl Transaction {
|
||||||
expiry_height,
|
expiry_height,
|
||||||
transparent_bundle,
|
transparent_bundle,
|
||||||
sprout_bundle,
|
sprout_bundle,
|
||||||
sapling_bundle: binding_sig.map(|binding_sig| {
|
sapling_bundle: binding_sig.and_then(|binding_sig| {
|
||||||
sapling::Bundle::from_parts(
|
sapling::Bundle::from_parts(
|
||||||
shielded_spends,
|
shielded_spends,
|
||||||
shielded_outputs,
|
shielded_outputs,
|
||||||
|
|
Loading…
Reference in New Issue