Skip to content

Commit

Permalink
Merge pull request #1653 from zcash/zcash_address-no-std
Browse files Browse the repository at this point in the history
zcash_address: Only require `alloc` instead of `std`
  • Loading branch information
nuttycom authored Dec 13, 2024
2 parents 554e4e5 + fb163a3 commit 82093f8
Show file tree
Hide file tree
Showing 24 changed files with 286 additions and 270 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ categories = ["cryptography::cryptocurrencies"]
equihash = { version = "0.2", path = "components/equihash" }
zcash_address = { version = "0.6", path = "components/zcash_address" }
zcash_client_backend = { version = "0.15", path = "zcash_client_backend" }
zcash_encoding = { version = "0.2.1", path = "components/zcash_encoding" }
zcash_encoding = { version = "0.2.1", path = "components/zcash_encoding", default-features = false }
zcash_keys = { version = "0.5", path = "zcash_keys" }
zcash_protocol = { version = "0.4.1", path = "components/zcash_protocol" }
zcash_protocol = { version = "0.4.1", path = "components/zcash_protocol", default-features = false }
zip321 = { version = "0.2", path = "components/zip321" }

zcash_note_encryption = "0.4.1"
Expand Down Expand Up @@ -92,8 +92,8 @@ document-features = "0.2"

# Encodings
base64 = "0.22"
bech32 = "0.9"
bs58 = { version = "0.5", features = ["check"] }
bech32 = { version = "0.11", default-features = false, features = ["alloc"] }
bs58 = { version = "0.5", default-features = false, features = ["alloc", "check"] }
byteorder = "1"
hex = "0.4"
percent-encoding = "2.1.0"
Expand All @@ -112,6 +112,9 @@ webpki-roots = "0.25"
memuse = "0.2.1"
tracing = "0.1"

# No-std support
core2 = { version = "0.3", default-features = false, features = ["alloc"] }

# Parallel processing
crossbeam-channel = "0.5"
maybe-rayon = { version = "0.1.0", default-features = false }
Expand Down
5 changes: 5 additions & 0 deletions components/f4jumble/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this library adheres to Rust's notion of

## [Unreleased]

## [0.1.1] - 2024-12-13
### Added
- `alloc` feature flag as a mid-point between full `no-std` support and the
`std` feature flag.

## [0.1.0] - 2022-05-11
Initial release.
MSRV is 1.51
8 changes: 6 additions & 2 deletions components/f4jumble/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "f4jumble"
description = "Implementation of Zcash's F4Jumble algorithm"
version = "0.1.0"
version = "0.1.1"
authors = [
"Jack Grigg <[email protected]>",
"Kris Nuttycombe <[email protected]>",
Expand Down Expand Up @@ -32,4 +32,8 @@ proptest = "1"

[features]
default = ["std"]
std = ["blake2b_simd/std"]
alloc = []
std = [
"alloc",
"blake2b_simd/std",
]
16 changes: 10 additions & 6 deletions components/f4jumble/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ use core::fmt;
use core::ops::RangeInclusive;
use core::result::Result;

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(feature = "std")]
use std::vec::Vec;

#[cfg(feature = "alloc")]
use alloc::vec::Vec;

#[cfg(test)]
mod test_vectors;
Expand Down Expand Up @@ -264,8 +268,8 @@ pub fn f4jumble_inv_mut(message: &mut [u8]) -> Result<(), Error> {
/// "af1d55f2695aea02440867bbbfae3b08e8da55b625de3fa91432ab7b2c0a7dff9033ee666db1513ba5761ef482919fb8",
/// );
/// ```
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn f4jumble(message: &[u8]) -> Result<Vec<u8>, Error> {
let mut result = message.to_vec();
f4jumble_mut(&mut result).map(|()| result)
Expand All @@ -291,8 +295,8 @@ pub fn f4jumble(message: &[u8]) -> Result<Vec<u8>, Error> {
/// let message_b = f4jumble::f4jumble_inv(&encoded_b).unwrap();
/// assert_eq!(message_b, b"The package from Sarah arrives tomorrow morning.");
/// ```
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn f4jumble_inv(message: &[u8]) -> Result<Vec<u8>, Error> {
let mut result = message.to_vec();
f4jumble_inv_mut(&mut result).map(|()| result)
Expand Down
9 changes: 7 additions & 2 deletions components/zcash_address/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.6.2] - 2024-12-13
### Fixed
- Migrated to `f4jumble 0.1.1` to fix `no-std` support.

## [0.6.1] - 2024-12-13
### Added
- `impl serde::{Serialize, Deserialize} for zcash_address::ZcashAddress` behind
the `serde` feature flag.
- `no-std` support, via a default-enabled `std` feature flag.

## [0.6.0] - 2024-10-02
### Changed
Expand Down
14 changes: 10 additions & 4 deletions components/zcash_address/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "zcash_address"
description = "Zcash address parsing and serialization"
version = "0.6.0"
version = "0.6.2"
authors = [
"Jack Grigg <[email protected]>",
]
Expand All @@ -21,19 +21,25 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
bech32.workspace = true
bs58.workspace = true
f4jumble = { version = "0.1", path = "../f4jumble" }
core2.workspace = true
f4jumble = { version = "0.1.1", path = "../f4jumble", default-features = false, features = ["alloc"] }
zcash_protocol.workspace = true
zcash_encoding.workspace = true
proptest = { workspace = true, optional = true }
serde = { workspace = true, optional = true }

[dev-dependencies]
assert_matches.workspace = true
proptest.workspace = true

[features]
default = ["std"]
std = [
"core2/std",
"f4jumble/std",
"zcash_encoding/std",
"zcash_protocol/std",
]
test-dependencies = ["dep:proptest"]
serde = ["dep:serde"]

[lib]
bench = false
7 changes: 6 additions & 1 deletion components/zcash_address/src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{error::Error, fmt};
use core::fmt;

#[cfg(feature = "std")]
use std::error::Error;

use crate::{kind::*, AddressKind, Network, ZcashAddress};

Expand Down Expand Up @@ -43,7 +46,9 @@ impl<E: fmt::Display> fmt::Display for ConversionError<E> {
}
}

#[cfg(feature = "std")]
impl Error for UnsupportedAddress {}
#[cfg(feature = "std")]
impl<E: Error + 'static> Error for ConversionError<E> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Expand Down
110 changes: 58 additions & 52 deletions components/zcash_address/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use std::{convert::TryInto, error::Error, fmt, str::FromStr};
use alloc::string::String;
use alloc::vec::Vec;
use core::convert::TryInto;
use core::fmt;
use core::str::FromStr;

use bech32::{self, FromBase32, ToBase32, Variant};
#[cfg(feature = "std")]
use std::error::Error;

use bech32::{primitives::decode::CheckedHrpstring, Bech32, Bech32m, Checksum, Hrp};
use zcash_protocol::consensus::{NetworkConstants, NetworkType};
use zcash_protocol::constants::{mainnet, regtest, testnet};

Expand Down Expand Up @@ -38,6 +45,7 @@ impl fmt::Display for ParseError {
}
}

#[cfg(feature = "std")]
impl Error for ParseError {}

impl FromStr for ZcashAddress {
Expand All @@ -64,48 +72,48 @@ impl FromStr for ZcashAddress {
}
}

// Try decoding as a Sapling or TEX address (Bech32/Bech32m)
if let Ok((hrp, data, variant)) = bech32::decode(s) {
// If we reached this point, the encoding is found to be valid Bech32 or Bech32m.
let data = Vec::<u8>::from_base32(&data).map_err(|_| ParseError::InvalidEncoding)?;

match variant {
Variant::Bech32 => {
let net = match hrp.as_str() {
mainnet::HRP_SAPLING_PAYMENT_ADDRESS => NetworkType::Main,
testnet::HRP_SAPLING_PAYMENT_ADDRESS => NetworkType::Test,
regtest::HRP_SAPLING_PAYMENT_ADDRESS => NetworkType::Regtest,
// We will not define new Bech32 address encodings.
_ => {
return Err(ParseError::NotZcash);
}
};

return data[..]
.try_into()
.map(AddressKind::Sapling)
.map_err(|_| ParseError::InvalidEncoding)
.map(|kind| ZcashAddress { net, kind });
// Try decoding as a Sapling address (Bech32)
if let Ok(parsed) = CheckedHrpstring::new::<Bech32>(s) {
// If we reached this point, the encoding is found to be valid Bech32.
let net = match parsed.hrp().as_str() {
mainnet::HRP_SAPLING_PAYMENT_ADDRESS => NetworkType::Main,
testnet::HRP_SAPLING_PAYMENT_ADDRESS => NetworkType::Test,
regtest::HRP_SAPLING_PAYMENT_ADDRESS => NetworkType::Regtest,
// We will not define new Bech32 address encodings.
_ => {
return Err(ParseError::NotZcash);
}
Variant::Bech32m => {
// Try decoding as a TEX address (Bech32m)
let net = match hrp.as_str() {
mainnet::HRP_TEX_ADDRESS => NetworkType::Main,
testnet::HRP_TEX_ADDRESS => NetworkType::Test,
regtest::HRP_TEX_ADDRESS => NetworkType::Regtest,
// Not recognized as a Zcash address type
_ => {
return Err(ParseError::NotZcash);
}
};

return data[..]
.try_into()
.map(AddressKind::Tex)
.map_err(|_| ParseError::InvalidEncoding)
.map(|kind| ZcashAddress { net, kind });
};

let data = parsed.byte_iter().collect::<Vec<_>>();

return data
.try_into()
.map(AddressKind::Sapling)
.map_err(|_| ParseError::InvalidEncoding)
.map(|kind| ZcashAddress { net, kind });
}

// Try decoding as a TEX address (Bech32m)
if let Ok(parsed) = CheckedHrpstring::new::<Bech32m>(s) {
// If we reached this point, the encoding is found to be valid Bech32m.
let net = match parsed.hrp().as_str() {
mainnet::HRP_TEX_ADDRESS => NetworkType::Main,
testnet::HRP_TEX_ADDRESS => NetworkType::Test,
regtest::HRP_TEX_ADDRESS => NetworkType::Regtest,
// Not recognized as a Zcash address type
_ => {
return Err(ParseError::NotZcash);
}
}
};

let data = parsed.byte_iter().collect::<Vec<_>>();

return data
.try_into()
.map(AddressKind::Tex)
.map_err(|_| ParseError::InvalidEncoding)
.map(|kind| ZcashAddress { net, kind });
}

// The rest use Base58Check.
Expand Down Expand Up @@ -144,8 +152,8 @@ impl FromStr for ZcashAddress {
}
}

fn encode_bech32(hrp: &str, data: &[u8], variant: Variant) -> String {
bech32::encode(hrp, data.to_base32(), variant).expect("hrp is invalid")
fn encode_bech32<Ck: Checksum>(hrp: &str, data: &[u8]) -> String {
bech32::encode::<Ck>(Hrp::parse_unchecked(hrp), data).expect("encoding is short enough")
}

fn encode_b58(prefix: [u8; 2], data: &[u8]) -> String {
Expand All @@ -159,24 +167,22 @@ impl fmt::Display for ZcashAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let encoded = match &self.kind {
AddressKind::Sprout(data) => encode_b58(self.net.b58_sprout_address_prefix(), data),
AddressKind::Sapling(data) => encode_bech32(
self.net.hrp_sapling_payment_address(),
data,
Variant::Bech32,
),
AddressKind::Sapling(data) => {
encode_bech32::<Bech32>(self.net.hrp_sapling_payment_address(), data)
}
AddressKind::Unified(addr) => addr.encode(&self.net),
AddressKind::P2pkh(data) => encode_b58(self.net.b58_pubkey_address_prefix(), data),
AddressKind::P2sh(data) => encode_b58(self.net.b58_script_address_prefix(), data),
AddressKind::Tex(data) => {
encode_bech32(self.net.hrp_tex_address(), data, Variant::Bech32m)
}
AddressKind::Tex(data) => encode_bech32::<Bech32m>(self.net.hrp_tex_address(), data),
};
write!(f, "{}", encoded)
}
}

#[cfg(test)]
mod tests {
use alloc::string::ToString;

use assert_matches::assert_matches;

use super::*;
Expand Down
Loading

0 comments on commit 82093f8

Please sign in to comment.