From 2cb61e4650f91f49fc18f226f410a96e6f86361b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 18 Dec 2024 15:04:55 -0700 Subject: [PATCH] Implement `no_std` support via a default-enabled `std` feature flag. Add thumbv7em-none-eabihf as a no-default-features build target. --- .github/workflows/ci.yml | 28 +++++++++++- Cargo.lock | 38 +++++++--------- Cargo.toml | 68 +++++++++++++++++++---------- rust-toolchain.toml | 2 +- src/builder.rs | 43 +++++++++++++----- src/bundle.rs | 15 ++++--- src/circuit.rs | 13 +++--- src/circuit/constants.rs | 1 + src/circuit/ecc.rs | 4 +- src/circuit/pedersen_hash.rs | 1 + src/constants.rs | 4 ++ src/keys.rs | 7 ++- src/lib.rs | 15 ++++++- src/note/nullifier.rs | 5 ++- src/note_encryption.rs | 9 ++-- src/pczt.rs | 8 +++- src/pczt/io_finalizer.rs | 1 + src/pczt/parse.rs | 4 +- src/pczt/updater.rs | 3 ++ src/pedersen_hash.rs | 17 +++++--- src/pedersen_hash/test_vectors.rs | 1 + src/prover.rs | 6 ++- src/test_vectors/note_encryption.rs | 2 + src/test_vectors/signatures.rs | 2 + src/tree.rs | 22 ++++++---- src/value/sums.rs | 1 + src/verifier/batch.rs | 3 ++ src/zip32.rs | 57 +++++++++++++----------- 28 files changed, 246 insertions(+), 134 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c57b34..74d7424 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,12 +41,36 @@ jobs: matrix: target: - wasm32-wasi + - thumbv7em-none-eabihf steps: - uses: actions/checkout@v4 + with: + path: crate_root + # We use a synthetic crate to ensure no dev-dependencies are enabled, which can + # be incompatible with some of these targets. + - name: Create synthetic crate for testing + run: cargo init --lib ci-build + - name: Copy Rust version into synthetic crate + run: cp crate_root/rust-toolchain.toml ci-build/ + - name: Copy patch directives into synthetic crate + run: | + echo "[patch.crates-io]" >> ./ci-build/Cargo.toml + cat ./crate_root/Cargo.toml | sed "0,/.\+\(patch.crates.\+\)/d" >> ./ci-build/Cargo.toml + - name: Add sapling-crypto as a dependency of the synthetic crate + working-directory: ./ci-build + run: cargo add --no-default-features --path ../crate_root + - name: Add lazy_static with the spin_no_std feature + working-directory: ./ci-build + run: cargo add lazy_static --features "spin_no_std" - name: Add target + working-directory: ./ci-build run: rustup target add ${{ matrix.target }} - - name: Build crate - run: cargo build --no-default-features --verbose --target ${{ matrix.target }} + - name: Render the `subtle` dependency tree + working-directory: ./ci-build + run: cargo tree -i subtle -e features + - name: Build for target + working-directory: ./ci-build + run: cargo build --verbose --target ${{ matrix.target }} bitrot: name: Bitrot check diff --git a/Cargo.lock b/Cargo.lock index 2225ab8..b85b647 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,6 +342,15 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" +[[package]] +name = "core2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" +dependencies = [ + "memchr", +] + [[package]] name = "cpp_demangle" version = "0.4.3" @@ -815,9 +824,9 @@ dependencies = [ [[package]] name = "memuse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" +checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" [[package]] name = "miniz_oxide" @@ -1202,12 +1211,10 @@ dependencies = [ [[package]] name = "redjubjub" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" +source = "git+https://github.com/nuttycom/redjubjub?rev=e413019904400f4caa3550df7c4040befadfbb14#e413019904400f4caa3550df7c4040befadfbb14" dependencies = [ "rand_core", "reddsa", - "serde", "thiserror", "zeroize", ] @@ -1315,8 +1322,8 @@ dependencies = [ "blake2b_simd", "blake2s_simd", "bls12_381", - "byteorder", "chacha20poly1305", + "core2", "criterion", "document-features", "ff", @@ -1504,29 +1511,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] [[package]] name = "typenum" @@ -1893,9 +1885,9 @@ dependencies = [ [[package]] name = "zip32" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22" +checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e" dependencies = [ "blake2b_simd", "memuse", diff --git a/Cargo.toml b/Cargo.toml index fa48cb3..f92ccd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ authors = [ "Kris Nuttycombe ", ] edition = "2021" -rust-version = "1.65" +rust-version = "1.66" description = "Cryptographic library for Zcash Sapling" homepage = "https://github.com/zcash/sapling-crypto" repository = "https://github.com/zcash/sapling-crypto" @@ -18,48 +18,50 @@ features = ["test-dependencies"] rustdoc-args = ["--cfg", "docsrs"] [dependencies] -ff = "0.13" -group = { version = "0.13", features = ["wnaf-memuse"] } +ff = { version = "0.13", default-features = false } +group = "0.13" -bls12_381 = "0.8" -jubjub = "0.10" -redjubjub = "0.7" +bls12_381 = { version = "0.8", default-features = false, features = ["alloc"] } +jubjub = { version = "0.10", default-features = false, features = ["alloc"] } +redjubjub = { version = "0.7", default-features = false } zcash_spec = "0.1" # Boilerplate getset = "0.1" +# No-std support +core2 = { version = "0.3", default-features = false, features = ["alloc"] } + # Circuits -bellman = { version = "0.14", default-features = false, features = ["groth16"] } +bellman = { version = "0.14", features = ["groth16"], optional = true } # CSPRNG -rand = "0.8" -rand_core = "0.6" +rand = { version = "0.8", default-features = false } +rand_core = { version = "0.6", default-features = false } # Digests -blake2b_simd = "1" -blake2s_simd = "1" +blake2b_simd = { version = "1", default-features = false } +blake2s_simd = { version = "1", default-features = false } # Documentation -document-features = "0.2" +document-features = { version = "0.2", optional = true } # Encodings -byteorder = "1" -hex = "0.4" +hex = { version = "0.4", default-features = false, features = ["alloc"] } # Logging and metrics -memuse = "0.2.1" -tracing = "0.1" +memuse = { version = "0.2.2", default-features = false } +tracing = { version = "0.1", default-features = false } # Note Commitment Trees -bitvec = "1" -incrementalmerkletree = { version = "0.7", features = ["legacy-api"] } +bitvec = { version = "1", default-features = false } +incrementalmerkletree = { version = "0.7", default-features = false, features = ["legacy-api"] } # Note encryption zcash_note_encryption = { version = "0.4", features = ["pre-zip-212"] } # Secret management -subtle = "2.2.3" +subtle = { version = "2.2.3", default-features = false } # Static constants lazy_static = "1" @@ -69,8 +71,9 @@ proptest = { version = "1", optional = true } # ZIP 32 aes = "0.8" -fpe = "0.6" -zip32 = "0.1" +fpe = { version = "0.6", default-features = false, features = ["alloc"] } +zip32 = { version = "0.1.1", default-features = false } + [dev-dependencies] chacha20poly1305 = "0.10" @@ -83,10 +86,26 @@ rand_xorshift = "0.3" pprof = { version = "0.11", features = ["criterion", "flamegraph"] } # MSRV 1.56 [features] -default = ["multicore"] +default = ["multicore", "std"] +std = [ + "core2/std", + "document-features", + "group/wnaf-memuse", + "redjubjub/std", + "circuit", +] + +## Enables creation of Sapling proofs +circuit = [ + "bellman", + "bls12_381/bits", + "bls12_381/groups", + "bls12_381/pairings", + "jubjub/bits", +] ## Enables multithreading support for creating proofs. -multicore = ["bellman/multicore"] +multicore = ["circuit", "bellman/multicore"] ### A temporary feature flag that exposes granular APIs needed by `zcashd`. These APIs ### should not be relied upon and will be removed in a future release. @@ -105,3 +124,6 @@ harness = false [[bench]] name = "pedersen_hash" harness = false + +[patch.crates-io] +redjubjub = { git = "https://github.com/nuttycom/redjubjub", rev = "e413019904400f4caa3550df7c4040befadfbb14" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ce69938..4a49675 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.65.0" +channel = "1.66.0" components = ["clippy", "rustfmt"] diff --git a/src/builder.rs b/src/builder.rs index 6665563..4b08d6e 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,7 +1,8 @@ //! Types and functions for building Sapling transaction components. -use core::fmt; -use std::{collections::BTreeMap, iter, marker::PhantomData}; +use alloc::collections::BTreeMap; +use alloc::vec::Vec; +use core::{fmt, iter, marker::PhantomData}; use group::ff::Field; use incrementalmerkletree::Position; @@ -11,24 +12,27 @@ use redjubjub::{Binding, SpendAuth}; use zcash_note_encryption::EphemeralKeyBytes; use crate::{ - bundle::{ - Authorization, Authorized, Bundle, GrothProofBytes, OutputDescription, SpendDescription, - }, - circuit, + bundle::{Authorization, Authorized, Bundle, GrothProofBytes}, keys::{ EphemeralSecretKey, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey, }, note::ExtractedNoteCommitment, note_encryption::{sapling_note_encryption, Zip212Enforcement}, - prover::{OutputProver, SpendProver}, util::generate_random_rseed_internal, - value::{ - CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum, - }, + value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum}, + Anchor, Diversifier, MerklePath, Node, Note, Nullifier, PaymentAddress, SaplingIvk, + NOTE_COMMITMENT_TREE_DEPTH, +}; + +#[cfg(feature = "circuit")] +use crate::{ + bundle::{OutputDescription, SpendDescription}, + circuit, + prover::{OutputProver, SpendProver}, + value::{CommitmentSum, TrapdoorSum}, zip32::ExtendedSpendingKey, - Anchor, Diversifier, MerklePath, Node, Note, Nullifier, PaymentAddress, ProofGenerationKey, - SaplingIvk, NOTE_COMMITMENT_TREE_DEPTH, + ProofGenerationKey, }; /// If there are any shielded inputs, always have at least two shielded outputs, padding @@ -268,6 +272,7 @@ impl PreparedSpendInfo { (cv, nullifier, rk, alpha) } + #[cfg(feature = "circuit")] fn build( self, proof_generation_key: Option, @@ -464,6 +469,7 @@ impl PreparedOutputInfo { ) } + #[cfg(feature = "circuit")] fn build( self, rng: &mut R, @@ -664,6 +670,7 @@ impl Builder { } /// Constructs the Sapling bundle from the builder's accumulated state. + #[cfg(feature = "circuit")] pub fn build>( self, extsks: &[ExtendedSpendingKey], @@ -726,6 +733,7 @@ impl Builder { /// Constructs a new Sapling transaction bundle of the given type from the specified set of spends /// and outputs. +#[cfg(feature = "circuit")] pub fn bundle>( rng: R, bundle_type: BundleType, @@ -917,6 +925,7 @@ fn build_bundle( /// Type alias for an in-progress bundle that has no proofs or signatures. /// /// This is returned by [`Builder::build`]. +#[cfg(feature = "circuit")] pub type UnauthorizedBundle = Bundle, V>; /// Marker trait representing bundle proofs in the process of being created. @@ -951,9 +960,11 @@ impl Authorization for InProgress< /// /// The [`SpendDescription`]s and [`OutputDescription`]s within the bundle contain the /// private data needed to create proofs. +#[cfg(feature = "circuit")] #[derive(Clone, Copy, Debug)] pub struct Unproven; +#[cfg(feature = "circuit")] impl InProgressProofs for Unproven { type SpendProof = circuit::Spend; type OutputProof = circuit::Output; @@ -969,16 +980,19 @@ impl InProgressProofs for Proven { } /// Reports on the progress made towards creating proofs for a bundle. +#[cfg(feature = "circuit")] 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); } +#[cfg(feature = "circuit")] impl ProverProgress for () { fn update(&mut self, _: u32, _: u32) {} } +#[cfg(feature = "circuit")] impl> ProverProgress for std::sync::mpsc::Sender { fn update(&mut self, cur: u32, end: u32) { // If the send fails, we should ignore the error, not crash. @@ -986,12 +1000,14 @@ impl> ProverProgress for std::sync::mpsc::Sender { } } +#[cfg(feature = "circuit")] impl ProverProgress for &mut U { fn update(&mut self, cur: u32, end: u32) { (*self).update(cur, end); } } +#[cfg(feature = "circuit")] struct CreateProofs<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress> { spend_prover: &'a SP, output_prover: &'a OP, @@ -1001,6 +1017,7 @@ struct CreateProofs<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: Prover progress: u32, } +#[cfg(feature = "circuit")] impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress> CreateProofs<'a, SP, OP, R, U> { @@ -1041,6 +1058,7 @@ impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress> OP::encode_proof(proof) } + #[cfg(feature = "circuit")] fn map_authorization( &mut self, a: InProgress, @@ -1052,6 +1070,7 @@ impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress> } } +#[cfg(feature = "circuit")] impl Bundle, V> { /// Creates the proofs for this bundle. pub fn create_proofs( diff --git a/src/bundle.rs b/src/bundle.rs index b015f13..f81353c 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use core::fmt::Debug; use memuse::DynamicUsage; @@ -9,7 +10,7 @@ use zcash_note_encryption::{ }; use crate::{ - circuit::GROTH_PROOF_SIZE, + constants::GROTH_PROOF_SIZE, note::ExtractedNoteCommitment, note_encryption::{CompactOutputDescription, SaplingDomain}, value::ValueCommitment, @@ -220,8 +221,8 @@ pub struct SpendDescription { spend_auth_sig: A::AuthSig, } -impl std::fmt::Debug for SpendDescription { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { +impl core::fmt::Debug for SpendDescription { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { write!( f, "SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})", @@ -431,8 +432,8 @@ impl ShieldedOutput for OutputDescription } } -impl std::fmt::Debug for OutputDescription { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { +impl core::fmt::Debug for OutputDescription { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { write!( f, "OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})", @@ -498,7 +499,7 @@ impl From> for CompactOutputDescription { #[cfg(any(test, feature = "test-dependencies"))] #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] pub mod testing { - use std::fmt; + use core::fmt; use ff::Field; use group::{Group, GroupEncoding}; @@ -507,7 +508,7 @@ pub mod testing { use rand::{rngs::StdRng, SeedableRng}; use crate::{ - circuit::GROTH_PROOF_SIZE, + constants::GROTH_PROOF_SIZE, note::testing::arb_cmu, value::{ testing::{arb_note_value_bounded, arb_trapdoor}, diff --git a/src/circuit.rs b/src/circuit.rs index 611dc88..2a67f31 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -1,15 +1,14 @@ //! The Sapling circuits. +use alloc::vec::Vec; use core::fmt; -use std::io; +use core2::io; use group::{ff::PrimeField, Curve}; use bellman::{groth16, Circuit, ConstraintSystem, SynthesisError}; use bls12_381::Bls12; -use super::{value::NoteValue, PaymentAddress, ProofGenerationKey}; - use bellman::gadgets::blake2s; use bellman::gadgets::boolean; use bellman::gadgets::multipack; @@ -21,6 +20,7 @@ use self::constants::{ PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR, VALUE_COMMITMENT_VALUE_GENERATOR, }; +use crate::{value::NoteValue, PaymentAddress, ProofGenerationKey}; #[cfg(test)] use group::ff::PrimeFieldBits; @@ -29,9 +29,6 @@ mod constants; mod ecc; mod pedersen_hash; -// π_A + π_B + π_C -pub(crate) const GROTH_PROOF_SIZE: usize = 48 + 96 + 48; - /// The opening (value and randomness) of a Sapling value commitment. #[derive(Clone)] pub struct ValueCommitmentOpening { @@ -703,7 +700,7 @@ fn test_input_circuit_with_bls12_381() { let mut rhs = uncle; if b { - ::std::mem::swap(&mut lhs, &mut rhs); + ::core::mem::swap(&mut lhs, &mut rhs); } let lhs = lhs.to_le_bits(); @@ -886,7 +883,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { let mut rhs = uncle; if b { - ::std::mem::swap(&mut lhs, &mut rhs); + ::core::mem::swap(&mut lhs, &mut rhs); } let lhs = lhs.to_le_bits(); diff --git a/src/circuit/constants.rs b/src/circuit/constants.rs index 692c688..17ae959 100644 --- a/src/circuit/constants.rs +++ b/src/circuit/constants.rs @@ -2,6 +2,7 @@ use crate::constants::{PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_GENERATORS}; +use alloc::vec::Vec; use bls12_381::Scalar; use group::{ff::Field, Curve, Group}; use jubjub::ExtendedPoint; diff --git a/src/circuit/ecc.rs b/src/circuit/ecc.rs index 6bffd87..ebe7331 100644 --- a/src/circuit/ecc.rs +++ b/src/circuit/ecc.rs @@ -1,6 +1,7 @@ //! Gadgets implementing Jubjub elliptic curve operations. -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use alloc::vec::Vec; +use core::ops::{AddAssign, MulAssign, Neg, SubAssign}; use bellman::{ConstraintSystem, SynthesisError}; @@ -620,6 +621,7 @@ impl MontgomeryPoint { #[cfg(test)] mod test { + use alloc::vec::Vec; use bellman::ConstraintSystem; use group::{ ff::{Field, PrimeField, PrimeFieldBits}, diff --git a/src/circuit/pedersen_hash.rs b/src/circuit/pedersen_hash.rs index 94b9014..29718a8 100644 --- a/src/circuit/pedersen_hash.rs +++ b/src/circuit/pedersen_hash.rs @@ -3,6 +3,7 @@ use super::ecc::{EdwardsPoint, MontgomeryPoint}; pub use crate::pedersen_hash::Personalization; +use alloc::vec::Vec; use bellman::gadgets::boolean::Boolean; use bellman::gadgets::lookup::*; use bellman::{ConstraintSystem, SynthesisError}; diff --git a/src/constants.rs b/src/constants.rs index bb730a9..0bfdf4d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,5 +1,6 @@ //! Various constants used by the Sapling protocol. +use alloc::vec::Vec; use ff::PrimeField; use group::Group; use jubjub::SubgroupPoint; @@ -38,6 +39,9 @@ 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_"; +// π_A + π_B + π_C +pub(crate) const GROTH_PROOF_SIZE: usize = 48 + 96 + 48; + /// 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( diff --git a/src/keys.rs b/src/keys.rs index b636785..9e583b0 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -4,8 +4,9 @@ //! //! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents -use std::fmt; -use std::io::{self, Read, Write}; +use alloc::vec::Vec; +use core::fmt; +use core2::io::{self, Read, Write}; use super::{ address::PaymentAddress, @@ -444,6 +445,7 @@ impl SaplingIvk { #[derive(Clone, Debug)] pub struct PreparedIncomingViewingKey(PreparedScalar); +#[cfg(feature = "std")] impl memuse::DynamicUsage for PreparedIncomingViewingKey { fn dynamic_usage(&self) -> usize { self.0.dynamic_usage() @@ -690,6 +692,7 @@ pub mod testing { #[cfg(test)] mod tests { + use alloc::string::ToString; use group::{Group, GroupEncoding}; use super::{FullViewingKey, SpendAuthorizingKey, SpendValidatingKey}; diff --git a/src/lib.rs b/src/lib.rs index 654b767..8cc5720 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,17 +8,26 @@ //! opposed to e.g. an Orchard payment address, which is also shielded). //! //! ## Feature flags -#![doc = document_features::document_features!()] +#![cfg_attr(feature = "std", doc = document_features::document_features!())] //! +#![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] // Catch documentation errors caused by code changes. #![deny(rustdoc::broken_intra_doc_links)] #![deny(unsafe_code)] +#[macro_use] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + mod address; pub mod builder; pub mod bundle; + +#[cfg(feature = "circuit")] pub mod circuit; pub mod constants; pub mod group_hash; @@ -27,11 +36,13 @@ pub mod note; pub mod note_encryption; pub mod pczt; pub mod pedersen_hash; +#[cfg(feature = "circuit")] pub mod prover; mod spec; mod tree; pub mod util; pub mod value; +#[cfg(feature = "circuit")] mod verifier; pub mod zip32; @@ -43,6 +54,8 @@ pub use tree::{ merkle_hash, Anchor, CommitmentTree, IncrementalWitness, MerklePath, Node, NOTE_COMMITMENT_TREE_DEPTH, }; + +#[cfg(feature = "circuit")] pub use verifier::{BatchValidator, SaplingVerificationContext}; #[cfg(any(test, feature = "test-dependencies"))] diff --git a/src/note/nullifier.rs b/src/note/nullifier.rs index 8176f40..58fd01b 100644 --- a/src/note/nullifier.rs +++ b/src/note/nullifier.rs @@ -1,5 +1,6 @@ -use std::array::TryFromSliceError; -use std::fmt; +use alloc::fmt; +use alloc::vec::Vec; +use core::array::TryFromSliceError; use subtle::{Choice, ConstantTimeEq}; diff --git a/src/note_encryption.rs b/src/note_encryption.rs index 48f03af..756e04e 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -2,8 +2,8 @@ //! //! NB: the example code is only covering the post-Canopy case. +use alloc::vec::Vec; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; -use byteorder::{LittleEndian, WriteBytesExt}; use ff::PrimeField; use memuse::DynamicUsage; use rand_core::RngCore; @@ -193,9 +193,7 @@ impl Domain for SaplingDomain { Rseed::AfterZip212(_) => 2, }; input[1..12].copy_from_slice(¬e.recipient().diversifier().0); - (&mut input[12..20]) - .write_u64::(note.value().inner()) - .unwrap(); + input[12..20].copy_from_slice(¬e.value().inner().to_le_bytes()); match note.rseed() { Rseed::BeforeZip212(rcm) => { @@ -462,6 +460,7 @@ pub fn try_sapling_output_recovery( #[cfg(test)] mod tests { + use alloc::vec::Vec; use chacha20poly1305::{ aead::{AeadInPlace, KeyInit}, ChaCha20Poly1305, @@ -486,7 +485,7 @@ mod tests { use crate::{ bundle::{GrothProofBytes, OutputDescription}, - circuit::GROTH_PROOF_SIZE, + constants::GROTH_PROOF_SIZE, keys::{DiversifiedTransmissionKey, EphemeralSecretKey, OutgoingViewingKey}, note::ExtractedNoteCommitment, note_encryption::PreparedIncomingViewingKey, diff --git a/src/pczt.rs b/src/pczt.rs index d478b44..4494df7 100644 --- a/src/pczt.rs +++ b/src/pczt.rs @@ -1,7 +1,9 @@ //! PCZT support for Sapling. +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; use core::fmt; -use std::collections::BTreeMap; use getset::Getters; use redjubjub::{Binding, SpendAuth}; @@ -30,7 +32,9 @@ pub use io_finalizer::IoFinalizerError; mod updater; pub use updater::{OutputUpdater, SpendUpdater, Updater, UpdaterError}; +#[cfg(feature = "circuit")] mod prover; +#[cfg(feature = "circuit")] pub use prover::ProverError; mod signer; @@ -277,7 +281,7 @@ pub struct Output { } impl fmt::Debug for Output { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Output") .field("cv", &self.cv) .field("cmu", &self.cmu) diff --git a/src/pczt/io_finalizer.rs b/src/pczt/io_finalizer.rs index 723a4aa..8164229 100644 --- a/src/pczt/io_finalizer.rs +++ b/src/pczt/io_finalizer.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use rand::{CryptoRng, RngCore}; use crate::value::{CommitmentSum, TrapdoorSum}; diff --git a/src/pczt/parse.rs b/src/pczt/parse.rs index 4ac77db..dc11af4 100644 --- a/src/pczt/parse.rs +++ b/src/pczt/parse.rs @@ -1,4 +1,6 @@ -use std::collections::BTreeMap; +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; use ff::PrimeField; use zcash_note_encryption::{EphemeralKeyBytes, OutgoingCipherKey}; diff --git a/src/pczt/updater.rs b/src/pczt/updater.rs index 71c7d7a..357c3ec 100644 --- a/src/pczt/updater.rs +++ b/src/pczt/updater.rs @@ -1,3 +1,6 @@ +use alloc::string::String; +use alloc::vec::Vec; + use crate::ProofGenerationKey; use super::{Bundle, Output, Spend, Zip32Derivation}; diff --git a/src/pedersen_hash.rs b/src/pedersen_hash.rs index 20cc40a..43d4a10 100644 --- a/src/pedersen_hash.rs +++ b/src/pedersen_hash.rs @@ -3,14 +3,12 @@ #[cfg(test)] pub(crate) mod test_vectors; -use byteorder::{ByteOrder, LittleEndian}; +use alloc::vec::Vec; +use core::ops::{AddAssign, Neg}; 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, -}; +use super::constants::{PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_EXP_WINDOW_SIZE}; #[derive(Copy, Clone)] pub enum Personalization { @@ -41,7 +39,7 @@ where .chain(bits.into_iter()); let mut result = jubjub::SubgroupPoint::identity(); - let mut generators = PEDERSEN_HASH_EXP_TABLE.iter(); + let mut generators = crate::constants::PEDERSEN_HASH_EXP_TABLE.iter(); loop { let mut acc = jubjub::Fr::zero(); @@ -94,7 +92,11 @@ where 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]); + for (src, dst) in acc.chunks_exact(8).zip(limbs[..num_limbs].iter_mut()) { + let mut limb_bytes = [0u8; 8]; + limb_bytes.copy_from_slice(src); + *dst = u64::from_le_bytes(limb_bytes); + } let mut tmp = jubjub::SubgroupPoint::identity(); @@ -124,6 +126,7 @@ where #[cfg(test)] pub mod test { + use alloc::string::ToString; use group::Curve; use super::*; diff --git a/src/pedersen_hash/test_vectors.rs b/src/pedersen_hash/test_vectors.rs index 4d051af..84cd8d8 100644 --- a/src/pedersen_hash/test_vectors.rs +++ b/src/pedersen_hash/test_vectors.rs @@ -1,6 +1,7 @@ //! Test vectors from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_pedersen.py use super::{test::TestVector, Personalization}; +use alloc::vec::Vec; pub fn get_vectors<'a>() -> Vec> { vec![ diff --git a/src/prover.rs b/src/prover.rs index f5e2ad7..cfcbcb2 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -6,7 +6,8 @@ use rand_core::RngCore; use crate::{ bundle::GrothProofBytes, - circuit::{self, GROTH_PROOF_SIZE}, + circuit, + constants::GROTH_PROOF_SIZE, keys::EphemeralSecretKey, value::{NoteValue, ValueCommitTrapdoor}, MerklePath, @@ -179,7 +180,8 @@ pub mod mock { use super::{OutputProver, SpendProver}; use crate::{ bundle::GrothProofBytes, - circuit::{self, ValueCommitmentOpening, GROTH_PROOF_SIZE}, + circuit::{self, ValueCommitmentOpening}, + constants::GROTH_PROOF_SIZE, keys::EphemeralSecretKey, value::{NoteValue, ValueCommitTrapdoor}, Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed, diff --git a/src/test_vectors/note_encryption.rs b/src/test_vectors/note_encryption.rs index 09209f2..9f7073c 100644 --- a/src/test_vectors/note_encryption.rs +++ b/src/test_vectors/note_encryption.rs @@ -1,3 +1,5 @@ +use alloc::vec::Vec; + pub(crate) struct TestVector { pub ovk: [u8; 32], pub ivk: [u8; 32], diff --git a/src/test_vectors/signatures.rs b/src/test_vectors/signatures.rs index 7ce341c..bec89fb 100644 --- a/src/test_vectors/signatures.rs +++ b/src/test_vectors/signatures.rs @@ -1,3 +1,5 @@ +use alloc::vec::Vec; + pub(crate) struct TestVector { pub(crate) sk: [u8; 32], pub(crate) vk: [u8; 32], diff --git a/src/tree.rs b/src/tree.rs index ea339b4..11c3906 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -4,7 +4,8 @@ use incrementalmerkletree::{Hashable, Level}; use lazy_static::lazy_static; use subtle::CtOption; -use std::fmt; +use alloc::vec::Vec; +use core::fmt; use super::{ note::ExtractedNoteCommitment, @@ -20,14 +21,16 @@ pub type MerklePath = incrementalmerkletree::MerklePath = { - 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 - }; + static ref EMPTY_ROOTS: Vec = empty_roots(); +} + +fn empty_roots() -> Vec { + 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. @@ -144,6 +147,7 @@ impl Node { } /// Returns the wrapped value + #[cfg(feature = "circuit")] pub(crate) fn inner(&self) -> &jubjub::Base { &self.0 } diff --git a/src/value/sums.rs b/src/value/sums.rs index 0543606..1223544 100644 --- a/src/value/sums.rs +++ b/src/value/sums.rs @@ -18,6 +18,7 @@ impl fmt::Display for OverflowError { } } +#[cfg(feature = "std")] impl std::error::Error for OverflowError {} /// A sum of Sapling note values. diff --git a/src/verifier/batch.rs b/src/verifier/batch.rs index 0eb196e..d630620 100644 --- a/src/verifier/batch.rs +++ b/src/verifier/batch.rs @@ -140,7 +140,10 @@ impl BatchValidator { } if let Err(e) = self.signatures.verify(&mut rng) { + #[cfg(feature = "std")] tracing::debug!("Signature batch validation failed: {}", e); + #[cfg(not(feature = "std"))] + tracing::debug!("Signature batch validation failed: {:?}", e); return false; } diff --git a/src/zip32.rs b/src/zip32.rs index ea744f4..bf04deb 100644 --- a/src/zip32.rs +++ b/src/zip32.rs @@ -6,14 +6,13 @@ use aes::Aes256; use blake2b_simd::Params as Blake2bParams; -use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; use subtle::CtOption; use zcash_spec::PrfExpand; use zip32::{ChainCode, ChildIndex, DiversifierIndex, Scope}; -use std::io::{self, Read, Write}; -use std::ops::AddAssign; +use core::ops::AddAssign; +use core2::io::{self, Read, Write}; use super::{Diversifier, NullifierDerivingKey, PaymentAddress, ViewingKey}; use crate::note_encryption::PreparedIncomingViewingKey; @@ -269,7 +268,7 @@ pub struct ExtendedSpendingKey { dk: DiversifierKey, } -impl std::cmp::PartialEq for ExtendedSpendingKey { +impl core::cmp::PartialEq for ExtendedSpendingKey { fn eq(&self, rhs: &ExtendedSpendingKey) -> bool { self.depth == rhs.depth && self.parent_fvk_tag == rhs.parent_fvk_tag @@ -282,8 +281,8 @@ impl std::cmp::PartialEq for ExtendedSpendingKey { } } -impl std::fmt::Debug for ExtendedSpendingKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { +impl core::fmt::Debug for ExtendedSpendingKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { write!( f, "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})", @@ -354,17 +353,20 @@ impl ExtendedSpendingKey { /// Reads and decodes the encoded form of the extended spending key as defined in /// [ZIP 32](https://zips.z.cash/zip-0032) from the provided reader. pub fn read(mut reader: R) -> io::Result { - let depth = reader.read_u8()?; + let mut depth = [0; 1]; + reader.read_exact(&mut depth)?; + let depth = depth[0]; let mut tag = [0; 4]; reader.read_exact(&mut tag)?; - let child_index = reader.read_u32::().and_then(|i| { - KeyIndex::new(depth, i).ok_or_else(|| { - io::Error::new( - io::ErrorKind::Unsupported, + let mut child_index_bytes = [0; 4]; + reader.read_exact(&mut child_index_bytes)?; + let child_index = + KeyIndex::new(depth, u32::from_le_bytes(child_index_bytes)).ok_or_else(|| { + core2::io::Error::new( + core2::io::ErrorKind::InvalidData, "Unsupported child index in encoding", ) - }) - })?; + })?; let mut c = [0; 32]; reader.read_exact(&mut c)?; let expsk = ExpandedSpendingKey::read(&mut reader)?; @@ -420,7 +422,7 @@ impl ExtendedSpendingKey { let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); let tmp = { let mut le_i = [0; 4]; - LittleEndian::write_u32(&mut le_i, i.index()); + le_i.copy_from_slice(&i.index().to_le_bytes()); PrfExpand::SAPLING_ZIP32_CHILD_HARDENED.with( self.chain_code.as_bytes(), &self.expsk.to_bytes(), @@ -529,7 +531,7 @@ pub struct ExtendedFullViewingKey { pub(crate) dk: DiversifierKey, } -impl std::cmp::PartialEq for ExtendedFullViewingKey { +impl core::cmp::PartialEq for ExtendedFullViewingKey { fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool { self.depth == rhs.depth && self.parent_fvk_tag == rhs.parent_fvk_tag @@ -542,8 +544,8 @@ impl std::cmp::PartialEq for ExtendedFullViewingKey { } } -impl std::fmt::Debug for ExtendedFullViewingKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { +impl core::fmt::Debug for ExtendedFullViewingKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { write!( f, "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})", @@ -554,17 +556,20 @@ impl std::fmt::Debug for ExtendedFullViewingKey { impl ExtendedFullViewingKey { pub fn read(mut reader: R) -> io::Result { - let depth = reader.read_u8()?; + let mut depth = [0; 1]; + reader.read_exact(&mut depth)?; + let depth = depth[0]; let mut tag = [0; 4]; reader.read_exact(&mut tag)?; - let child_index = reader.read_u32::().and_then(|i| { - KeyIndex::new(depth, i).ok_or_else(|| { - io::Error::new( - io::ErrorKind::Unsupported, + let mut child_index_bytes = [0; 4]; + reader.read_exact(&mut child_index_bytes)?; + let child_index = + KeyIndex::new(depth, u32::from_le_bytes(child_index_bytes)).ok_or_else(|| { + core2::io::Error::new( + core2::io::ErrorKind::InvalidData, "Unsupported child index in encoding", ) - }) - })?; + })?; let mut c = [0; 32]; reader.read_exact(&mut c)?; let fvk = FullViewingKey::read(&mut reader)?; @@ -582,9 +587,9 @@ impl ExtendedFullViewingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_u8(self.depth)?; + writer.write_all(&[self.depth])?; writer.write_all(&self.parent_fvk_tag.0)?; - writer.write_u32::(self.child_index.index())?; + writer.write_all(&self.child_index.index().to_le_bytes())?; writer.write_all(self.chain_code.as_bytes())?; writer.write_all(&self.fvk.to_bytes())?; writer.write_all(&self.dk.0)?;