diff --git a/Cargo.lock b/Cargo.lock index 6dd02cb20..a8e38ed16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1073,7 +1073,7 @@ dependencies = [ "pasta_curves", "proptest", "rand", - "sinsemilla 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sinsemilla", "subtle", "uint", ] @@ -1576,7 +1576,7 @@ dependencies = [ "reddsa", "serde", "shardtree", - "sinsemilla 0.1.0 (git+https://github.com/zcash/sinsemilla?rev=aabb707e862bc3d7b803c77d14e5a771bcee3e8c)", + "sinsemilla", "subtle", "tracing", "visibility", @@ -2190,17 +2190,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "sinsemilla" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d268ae0ea06faafe1662e9967cd4f9022014f5eeb798e0c302c876df8b7af9c" -dependencies = [ - "group", - "pasta_curves", - "subtle", -] - [[package]] name = "sinsemilla" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 46b491488..fba5a68b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,9 +41,9 @@ reddsa = { version = "0.5", default-features = false } nonempty = { version = "0.11", default-features = false } poseidon = { package = "halo2_poseidon", version = "0.1" } serde = { version = "1.0", default-features = false, features = ["derive"] } -sinsemilla = { git = "https://github.com/zcash/sinsemilla", rev = "aabb707e862bc3d7b803c77d14e5a771bcee3e8c" } +sinsemilla = "0.1" subtle = { version = "2.3", default-features = false } -zcash_note_encryption = { git = "https://github.com/zcash/zcash_note_encryption", rev = "668ea44cf59a226715a5f3cb1bf88710a8c188a3" } +zcash_note_encryption = "0.4" incrementalmerkletree = "0.8.1" zcash_spec = "0.2.1" zip32 = { version = "0.2.0", default-features = false } @@ -71,7 +71,7 @@ criterion = "0.4" # 0.5 depends on clap 4 which has MSRV 1.70 halo2_gadgets = { version = "0.3", features = ["test-dependencies"] } hex = "0.4" proptest = ">=1.0.0, <1.7.0" -zcash_note_encryption = { git = "https://github.com/zcash/zcash_note_encryption", rev = "668ea44cf59a226715a5f3cb1bf88710a8c188a3", features = ["pre-zip-212"] } +zcash_note_encryption = { version = "0.4", features = ["pre-zip-212"] } incrementalmerkletree = { version = "0.8.1", features = ["test-dependencies"] } shardtree = "0.6" @@ -110,6 +110,8 @@ debug = true debug = true [patch.crates-io] +sinsemilla = { git = "https://github.com/zcash/sinsemilla", rev = "aabb707e862bc3d7b803c77d14e5a771bcee3e8c" } +zcash_note_encryption = { git = "https://github.com/zcash/zcash_note_encryption", rev = "668ea44cf59a226715a5f3cb1bf88710a8c188a3" } zcash_spec = { git = "https://github.com/QED-it/zcash_spec", rev = "842d697048a8960348adcde704c24438fc5b4544" } halo2_gadgets = { git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } halo2_proofs = { git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } diff --git a/src/asset_record.rs b/src/asset_record.rs deleted file mode 100644 index b3af557ca..000000000 --- a/src/asset_record.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Structs and logic related to aggregated information about an asset. - -use crate::{value::NoteValue, Note}; - -/// Represents aggregated information about an asset, including its supply, finalization status, -/// and reference note. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct AssetRecord { - /// The amount of the asset. - pub amount: NoteValue, - - /// Whether or not the asset is finalized. - pub is_finalized: bool, - - /// A reference note - pub reference_note: Note, -} - -impl AssetRecord { - /// Creates a new [`AssetRecord`] instance. - pub fn new(amount: NoteValue, is_finalized: bool, reference_note: Note) -> Self { - Self { - amount, - is_finalized, - reference_note, - } - } -} diff --git a/src/builder.rs b/src/builder.rs index d602f4e9b..eff75ebf8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -560,7 +560,7 @@ impl ActionInfo { /// /// This is returned by [`Builder::build`]. #[cfg(feature = "circuit")] -pub type UnauthorizedBundle = Bundle, V, D>; +pub type UnauthorizedBundle = Bundle, V, P>; /// Metadata about a bundle created by [`bundle`] or [`Builder::build`] that is not /// necessarily recoverable from the bundle itself. @@ -1126,19 +1126,6 @@ pub struct InProgress { sigs: S, } -impl InProgress { - /// Mutate the proof using the provided function. - pub fn map_proof(self, f: F) -> InProgress - where - F: FnOnce(P) -> P2, - { - InProgress { - proof: f(self.proof), - sigs: self.sigs, - } - } -} - impl Authorization for InProgress { type SpendAuth = S::SpendAuth; } diff --git a/src/bundle.rs b/src/bundle.rs index 8aef3739c..75b5d5c22 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -260,6 +260,7 @@ impl Bundle { value_balance, burn, anchor, + // For the OrchardZSA protocol, `expiry_height` is set to 0, indicating no expiry. expiry_height: 0, authorization, } @@ -625,19 +626,18 @@ pub mod testing { use crate::{ builder::{VerBindingSig, VerSpendAuthSig}, - primitives::redpallas::testing::arb_binding_signing_key, - value::{testing::arb_note_value_bounded, NoteValue, ValueSum, MAX_NOTE_VALUE}, + note::{asset_base::testing::arb_zsa_asset_base, AssetBase}, + primitives::{redpallas::testing::arb_binding_signing_key, OrchardPrimitives}, + value::{ + testing::{arb_note_value, arb_note_value_bounded}, + NoteValue, ValueSum, MAX_NOTE_VALUE, + }, Anchor, Proof, }; use super::{Action, Authorized, Bundle, Flags}; pub use crate::action::testing::ActionArb; - use crate::{ - note::{asset_base::testing::arb_zsa_asset_base, AssetBase}, - primitives::OrchardPrimitives, - value::testing::arb_note_value, - }; /// Marker type for a bundle that contains no authorizing data. pub type Unauthorized = super::EffectsOnly; diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 41d349389..5d2e094b3 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -44,10 +44,10 @@ pub(crate) fn hash_bundle_txid_data, P: Or P::hash_bundle_txid_data(bundle) } -/// Construct the `orchard_digest` commitment for the absent bundle as defined in -/// [ZIP-246: Digests for the Version 6 Transaction Format][zip246] +/// Construct the commitment for the absent bundle as defined in +/// [ZIP-244: Transaction Identifier Non-Malleability][zip244] /// -/// [zip246]: https://zips.z.cash/zip-0246 +/// [zip244]: https://zips.z.cash/zip-0244 pub fn hash_bundle_txid_empty() -> Blake2bHash { hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION).finalize() } diff --git a/src/circuit/circuit_vanilla.rs b/src/circuit/circuit_vanilla.rs index 4a1d26d15..767e6d6a2 100644 --- a/src/circuit/circuit_vanilla.rs +++ b/src/circuit/circuit_vanilla.rs @@ -2,6 +2,8 @@ //! //! Includes the configuration, synthesis, and proof verification logic. +// Review hint: this file is largely derived from src/circuit.rs + use group::Curve; use pasta_curves::pallas; diff --git a/src/issuance.rs b/src/issuance.rs index de81602f3..fe21ee347 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -22,16 +22,14 @@ use rand::RngCore; use zcash_spec::sighash_versioning::{VersionedSig, SIGHASH_V0}; use crate::{ - asset_record::AssetRecord, bundle::commitments::{hash_issue_bundle_auth_data, hash_issue_bundle_txid_data}, constants::reference_keys::ReferenceKeys, - issuance_auth::{IssueAuthKey, IssueAuthSig, IssueValidatingKey}, + issuance_auth::{IssueAuthKey, IssueAuthSig, IssueValidatingKey, ZSASchnorr}, note::{rho_for_issuance_note, AssetBase, Nullifier, Rho}, value::NoteValue, Address, Note, }; -use crate::issuance_auth::ZSASchnorr; use Error::{ AssetBaseCannotBeIdentityPoint, CannotBeFirstIssuance, IncorrectRhoDerivation, InvalidIssueAuthKey, InvalidIssueBundleSig, InvalidIssueValidatingKey, InvalidSighashVersion, @@ -701,6 +699,31 @@ pub fn verify_issue_bundle( ) } +/// Represents aggregated information about an asset, including its supply, finalization status, +/// and reference note. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AssetRecord { + /// The amount of the asset. + pub amount: NoteValue, + + /// Whether or not the asset is finalized. + pub is_finalized: bool, + + /// A reference note + pub reference_note: Note, +} + +impl AssetRecord { + /// Creates a new [`AssetRecord`] instance. + pub fn new(amount: NoteValue, is_finalized: bool, reference_note: Note) -> Self { + Self { + amount, + is_finalized, + reference_note, + } + } +} + /// Errors produced during the issuance process #[derive(Debug, PartialEq, Eq)] pub enum Error { @@ -808,7 +831,6 @@ impl fmt::Display for Error { #[cfg(test)] mod tests { use crate::{ - asset_record::AssetRecord, builder::{Builder, BundleType}, circuit::ProvingKey, issuance::Error::{ @@ -816,8 +838,8 @@ mod tests { IssueActionPreviouslyFinalizedAssetBase, IssueBundleIkMismatchAssetBase, }, issuance::{ - compute_asset_desc_hash, is_reference_note, verify_issue_bundle, IssueAction, - IssueBundle, IssueInfo, Signed, VerBIP340IssueAuthSig, + compute_asset_desc_hash, is_reference_note, verify_issue_bundle, AssetRecord, + IssueAction, IssueBundle, IssueInfo, Signed, VerBIP340IssueAuthSig, }, issuance_auth::{IssueAuthKey, IssueValidatingKey, ZSASchnorr}, keys::{FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, diff --git a/src/keys.rs b/src/keys.rs index 01b2d1303..0513e66e1 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -12,14 +12,14 @@ use group::{ prime::PrimeCurveAffine, Curve, GroupEncoding, }; -use pasta_curves::{pallas, pallas::Scalar}; +use pasta_curves::pallas; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_note_encryption::EphemeralKeyBytes; use crate::{ address::Address, - primitives::redpallas::{self, SpendAuth, VerificationKey}, + primitives::redpallas::{self, SpendAuth}, spec::{ commit_ivk, diversify_hash, extract_p, ka_orchard, ka_orchard_prepared, prf_nf, to_base, to_scalar, NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar, @@ -141,17 +141,13 @@ impl From<&SpendingKey> for SpendAuthorizingKey { // SpendingKey cannot be constructed such that this assertion would fail. assert!(!bool::from(ask.is_zero())); // TODO: Add TryFrom for SpendAuthorizingKey. - SpendAuthorizingKey(conditionally_negate(ask)) - } -} - -// If the last bit of repr_P(ak) is 1, negate ask. -fn conditionally_negate(scalar: Scalar) -> redpallas::SigningKey { - let ret = redpallas::SigningKey::(scalar.to_repr().try_into().unwrap()); - if (<[u8; 32]>::from(redpallas::VerificationKey::::from(&ret).0)[31] >> 7) == 1 { - redpallas::SigningKey::((-scalar).to_repr().try_into().unwrap()) - } else { - ret + let ret = SpendAuthorizingKey(ask.to_repr().try_into().unwrap()); + // If the last bit of repr_P(ak) is 1, negate ask. + if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 { + SpendAuthorizingKey((-ask).to_repr().try_into().unwrap()) + } else { + ret + } } } @@ -209,26 +205,21 @@ impl SpendValidatingKey { pub(crate) fn from_bytes(bytes: &[u8]) -> Option { <[u8; 32]>::try_from(bytes) .ok() - .and_then(check_structural_validity) + .and_then(|b| { + // Structural validity checks for ak_P: + // - The point must not be the identity + // (which for Pallas is canonically encoded as all-zeroes). + // - The sign of the y-coordinate must be positive. + if b != [0; 32] && b[31] & 0x80 == 0 { + >::try_from(b).ok() + } else { + None + } + }) .map(SpendValidatingKey) } } -/// A function to check structural validity of the validating keys for authorizing transfers and -/// issuing assets -/// Structural validity checks for ak_P or ik_P: -/// - The point must not be the identity (which for Pallas is canonically encoded as all-zeroes). -/// - The compressed y-coordinate bit must be 0. -fn check_structural_validity( - verification_key_bytes: [u8; 32], -) -> Option> { - if verification_key_bytes != [0; 32] && verification_key_bytes[31] & 0x80 == 0 { - >::try_from(verification_key_bytes).ok() - } else { - None - } -} - /// A key used to derive [`Nullifier`]s from [`Note`]s. /// /// $\mathsf{nk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents]. @@ -925,9 +916,10 @@ impl SharedSecret { #[cfg(any(test, feature = "test-dependencies"))] #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] pub mod testing { - use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey}; use proptest::prelude::*; + use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey}; + prop_compose! { /// Generate a uniformly distributed Orchard spending key. pub fn arb_spending_key()( diff --git a/src/lib.rs b/src/lib.rs index 70fb8d111..1b931ce43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,6 @@ use alloc::vec::Vec; mod action; mod address; -pub mod asset_record; pub mod builder; pub mod bundle; #[cfg(feature = "circuit")] diff --git a/src/note/asset_base.rs b/src/note/asset_base.rs index df7293b18..aa8435ff7 100644 --- a/src/note/asset_base.rs +++ b/src/note/asset_base.rs @@ -9,13 +9,12 @@ use pasta_curves::{arithmetic::CurveExt, pallas}; use rand_core::CryptoRngCore; use subtle::{Choice, ConstantTimeEq, CtOption}; -use crate::issuance_auth::ZSASchnorr; use crate::{ constants::fixed_bases::{ NATIVE_ASSET_BASE_V_BYTES, VALUE_COMMITMENT_PERSONALIZATION, ZSA_ASSET_BASE_PERSONALIZATION, }, issuance::compute_asset_desc_hash, - issuance_auth::{IssueAuthKey, IssueValidatingKey}, + issuance_auth::{IssueAuthKey, IssueValidatingKey, ZSASchnorr}, }; /// Note type identifier. diff --git a/src/primitives/compact_action.rs b/src/primitives/compact_action.rs index 12ed9d2ae..de0f5b8c0 100644 --- a/src/primitives/compact_action.rs +++ b/src/primitives/compact_action.rs @@ -1,5 +1,7 @@ //! Defines actions for Orchard shielded outputs and compact action for light clients. +// Review hint: this file is largely derived from src/note_encryption.rs + use core::fmt; use zcash_note_encryption::{note_bytes::NoteBytes, EphemeralKeyBytes, ShieldedOutput}; diff --git a/src/primitives/orchard_domain.rs b/src/primitives/orchard_domain.rs index c1d668127..da49de040 100644 --- a/src/primitives/orchard_domain.rs +++ b/src/primitives/orchard_domain.rs @@ -1,5 +1,7 @@ //! Orchard-specific note encryption domain. +// Review hint: this file is largely derived from src/note_encryption.rs + use crate::{ action::Action, note::Rho, primitives::compact_action::CompactAction, primitives::orchard_primitives::OrchardPrimitives, diff --git a/src/primitives/redpallas.rs b/src/primitives/redpallas.rs index 2f4ed45e3..70155cfbf 100644 --- a/src/primitives/redpallas.rs +++ b/src/primitives/redpallas.rs @@ -24,7 +24,7 @@ impl SigType for Binding {} /// A RedPallas signing key. #[derive(Clone, Copy, Debug)] -pub struct SigningKey(pub(crate) reddsa::SigningKey); +pub struct SigningKey(reddsa::SigningKey); impl From> for [u8; 32] { fn from(sk: SigningKey) -> [u8; 32] { @@ -64,7 +64,7 @@ impl SigningKey { /// A RedPallas verification key. #[derive(Clone, Debug)] -pub struct VerificationKey(pub(crate) reddsa::VerificationKey); +pub struct VerificationKey(reddsa::VerificationKey); impl From> for [u8; 32] { fn from(vk: VerificationKey) -> [u8; 32] { diff --git a/src/primitives/zcash_note_encryption_domain.rs b/src/primitives/zcash_note_encryption_domain.rs index 3a48f0062..aecd43a31 100644 --- a/src/primitives/zcash_note_encryption_domain.rs +++ b/src/primitives/zcash_note_encryption_domain.rs @@ -1,6 +1,8 @@ //! This module implements `Domain` and `BatchDomain` traits from the `zcash_note_encryption` //! crate and contains the common logic for `OrchardVanilla` and `OrchardZSA` flavors. +// Review hint: this file is largely derived from src/note_encryption.rs + use alloc::vec::Vec; use blake2b_simd::{Hash, Params}; use group::ff::PrimeField; diff --git a/src/spec/prf_expand.rs b/src/spec/prf_expand.rs deleted file mode 100644 index fa7881889..000000000 --- a/src/spec/prf_expand.rs +++ /dev/null @@ -1,77 +0,0 @@ -use blake2b_simd::Params; - -const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; - -/// The set of domains in which $PRF^\mathsf{expand}$ is defined. -pub(crate) enum PrfExpand { - Esk, - Rcm, - OrchardAsk, - OrchardNk, - OrchardRivk, - Psi, - ZsaIsk, - OrchardZip32Child, - OrchardDkOvk, - OrchardRivkInternal, -} - -impl PrfExpand { - fn domain_separator(&self) -> u8 { - match self { - Self::Esk => 0x04, - Self::Rcm => 0x05, - Self::OrchardAsk => 0x06, - Self::OrchardNk => 0x07, - Self::OrchardRivk => 0x08, - Self::Psi => 0x09, - Self::ZsaIsk => 0x0a, - Self::OrchardZip32Child => 0x81, - Self::OrchardDkOvk => 0x82, - Self::OrchardRivkInternal => 0x83, - } - } - - /// Expands the given secret key in this domain, with no additional data. - /// - /// $PRF^\mathsf{expand}(sk, dst) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst)$ - /// - /// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. - /// - /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs - pub(crate) fn expand(self, sk: &[u8]) -> [u8; 64] { - self.with_ad_slices(sk, &[]) - } - - /// Expands the given secret key in this domain, with the given additional data. - /// - /// $PRF^\mathsf{expand}(sk, dst, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst || t)$ - /// - /// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. - /// - /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs - pub(crate) fn with_ad(self, sk: &[u8], t: &[u8]) -> [u8; 64] { - self.with_ad_slices(sk, &[t]) - } - - /// Expands the given secret key in this domain, with additional data concatenated - /// from the given slices. - /// - /// $PRF^\mathsf{expand}(sk, dst, a, b, ...) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst || a || b || ...)$ - /// - /// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. - /// - /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs - pub(crate) fn with_ad_slices(self, sk: &[u8], ts: &[&[u8]]) -> [u8; 64] { - let mut h = Params::new() - .hash_length(64) - .personal(PRF_EXPAND_PERSONALIZATION) - .to_state(); - h.update(sk); - h.update(&[self.domain_separator()]); - for t in ts { - h.update(t); - } - *h.finalize().as_array() - } -} diff --git a/src/zip32.rs b/src/zip32.rs index 00eab0573..224284743 100644 --- a/src/zip32.rs +++ b/src/zip32.rs @@ -17,11 +17,11 @@ use crate::{ pub use zip32::ChildIndex; -const ZIP32_ORCHARD_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashOrchardFVFP"; /// Personalization for the master extended spending key -pub const ZIP32_ORCHARD_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Orchard"; +const ZIP32_ORCHARD_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Orchard"; /// Personalization for the master extended issuance key -pub const ZIP32_ORCHARD_ISSUANCE_PERSONALIZATION: &[u8; 16] = b"ZcashSA_Issue_V1"; +const ZIP32_ORCHARD_ISSUANCE_PERSONALIZATION: &[u8; 16] = b"ZcashSA_Issue_V1"; +const ZIP32_ORCHARD_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashOrchardFVFP"; /// Errors produced in derivation of extended spending keys #[derive(Debug, PartialEq, Eq)] diff --git a/tests/issuance_global_state.rs b/tests/issuance_global_state.rs index 5f58e02f9..6d7d688c7 100644 --- a/tests/issuance_global_state.rs +++ b/tests/issuance_global_state.rs @@ -5,9 +5,8 @@ use nonempty::NonEmpty; use rand::{rngs::OsRng, RngCore}; use orchard::{ - asset_record::AssetRecord, issuance::{ - compute_asset_desc_hash, verify_issue_bundle, + compute_asset_desc_hash, verify_issue_bundle, AssetRecord, Error::{ IssueActionPreviouslyFinalizedAssetBase, MissingReferenceNoteOnFirstIssuance, ValueOverflow,