Skip to content

Commit

Permalink
Merge pull request #1143 from nuttycom/crate_zip321
Browse files Browse the repository at this point in the history
Extract `zip321` crate from `zcash_client_backend`
  • Loading branch information
str4d authored Apr 22, 2024
2 parents 5df164b + d2aa6cf commit 5c6a6a4
Show file tree
Hide file tree
Showing 33 changed files with 1,177 additions and 481 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"components/zcash_address",
"components/zcash_encoding",
"components/zcash_protocol",
"components/zip321",
"zcash_client_backend",
"zcash_client_sqlite",
"zcash_extensions",
Expand Down Expand Up @@ -34,6 +35,7 @@ zcash_client_backend = { version = "0.12", path = "zcash_client_backend" }
zcash_encoding = { version = "0.2", path = "components/zcash_encoding" }
zcash_keys = { version = "0.2", path = "zcash_keys" }
zcash_protocol = { version = "0.1", path = "components/zcash_protocol" }
zip321 = { version = "0.0", path = "components/zip321" }

zcash_note_encryption = "0.4"
zcash_primitives = { version = "0.15", path = "zcash_primitives", default-features = false }
Expand Down
7 changes: 7 additions & 0 deletions components/zcash_address/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this library adheres to Rust's notion of

## [Unreleased]

### Added
- `zcash_address::ZcashAddress::{can_receive_memo, can_receive_as, matches_receiver}`
- `zcash_address::unified::Address::{can_receive_memo, has_receiver_of_type, contains_receiver}`
- Module `zcash_address::testing` under the `test-dependencies` feature.
- Module `zcash_address::unified::address::testing` under the
`test-dependencies` feature.

## [0.3.2] - 2024-03-06
### Added
- `zcash_address::convert`:
Expand Down
10 changes: 5 additions & 5 deletions components/zcash_address/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
bech32 = "0.9"
bs58 = { version = "0.5", features = ["check"] }
bech32.workspace = true
bs58.workspace = true
f4jumble = { version = "0.1", path = "../f4jumble" }
zcash_protocol.workspace = true
zcash_encoding.workspace = true
proptest = { workspace = true, optional = true }

[dev-dependencies]
assert_matches = "1.3.0"
proptest = "1"
assert_matches.workspace = true

[features]
test-dependencies = []
test-dependencies = ["dep:proptest"]

[lib]
bench = false
84 changes: 65 additions & 19 deletions components/zcash_address/src/kind/unified/address.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use zcash_protocol::{PoolType, ShieldedProtocol};

use super::{private::SealedItem, ParseError, Typecode};

use std::convert::{TryFrom, TryInto};
Expand Down Expand Up @@ -101,6 +103,30 @@ impl SealedItem for Receiver {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Address(pub(crate) Vec<Receiver>);

impl Address {
/// Returns whether this address has the ability to receive transfers of the given pool type.
pub fn has_receiver_of_type(&self, pool_type: PoolType) -> bool {
self.0.iter().any(|r| match r {
Receiver::Orchard(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Orchard),
Receiver::Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
Receiver::P2pkh(_) | Receiver::P2sh(_) => pool_type == PoolType::Transparent,
Receiver::Unknown { .. } => false,
})
}

/// Returns whether this address contains the given receiver.
pub fn contains_receiver(&self, receiver: &Receiver) -> bool {
self.0.contains(receiver)
}

/// Returns whether this address can receive a memo.
pub fn can_receive_memo(&self) -> bool {
self.0
.iter()
.any(|r| matches!(r, Receiver::Sapling(_) | Receiver::Orchard(_)))
}
}

impl super::private::SealedContainer for Address {
/// The HRP for a Bech32m-encoded mainnet Unified Address.
///
Expand Down Expand Up @@ -133,27 +159,19 @@ impl super::Container for Address {
}
}

#[cfg(any(test, feature = "test-dependencies"))]
pub mod test_vectors;

#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use zcash_encoding::MAX_COMPACT_SIZE;

use crate::{
kind::unified::{private::SealedContainer, Container, Encoding},
Network,
};

#[cfg(feature = "test-dependencies")]
pub mod testing {
use proptest::{
array::{uniform11, uniform20, uniform32},
collection::vec,
prelude::*,
sample::select,
strategy::Strategy,
};
use zcash_encoding::MAX_COMPACT_SIZE;

use super::{Address, ParseError, Receiver, Typecode};
use super::{Address, Receiver};
use crate::unified::Typecode;

prop_compose! {
fn uniform43()(a in uniform11(0u8..), b in uniform32(0u8..)) -> [u8; 43] {
Expand All @@ -164,11 +182,13 @@ mod tests {
}
}

fn arb_transparent_typecode() -> impl Strategy<Value = Typecode> {
/// A strategy to generate an arbitrary transparent typecode.
pub fn arb_transparent_typecode() -> impl Strategy<Value = Typecode> {
select(vec![Typecode::P2pkh, Typecode::P2sh])
}

fn arb_shielded_typecode() -> impl Strategy<Value = Typecode> {
/// A strategy to generate an arbitrary shielded (Sapling, Orchard, or unknown) typecode.
pub fn arb_shielded_typecode() -> impl Strategy<Value = Typecode> {
prop_oneof![
Just(Typecode::Sapling),
Just(Typecode::Orchard),
Expand All @@ -179,7 +199,7 @@ mod tests {
/// A strategy to generate an arbitrary valid set of typecodes without
/// duplication and containing only one of P2sh and P2pkh transparent
/// typecodes. The resulting vector will be sorted in encoding order.
fn arb_typecodes() -> impl Strategy<Value = Vec<Typecode>> {
pub fn arb_typecodes() -> impl Strategy<Value = Vec<Typecode>> {
prop::option::of(arb_transparent_typecode()).prop_flat_map(|transparent| {
prop::collection::hash_set(arb_shielded_typecode(), 1..4).prop_map(move |xs| {
let mut typecodes: Vec<_> = xs.into_iter().chain(transparent).collect();
Expand All @@ -189,7 +209,11 @@ mod tests {
})
}

fn arb_unified_address_for_typecodes(
/// Generates an arbitrary Unified address containing receivers corresponding to the provided
/// set of typecodes. The receivers of this address are likely to not represent valid protocol
/// receivers, and should only be used for testing parsing and/or encoding functions that do
/// not concern themselves with the validity of the underlying receivers.
pub fn arb_unified_address_for_typecodes(
typecodes: Vec<Typecode>,
) -> impl Strategy<Value = Vec<Receiver>> {
typecodes
Expand All @@ -206,11 +230,33 @@ mod tests {
.collect::<Vec<_>>()
}

fn arb_unified_address() -> impl Strategy<Value = Address> {
/// Generates an arbitrary Unified address. The receivers of this address are likely to not
/// represent valid protocol receivers, and should only be used for testing parsing and/or
/// encoding functions that do not concern themselves with the validity of the underlying
/// receivers.
pub fn arb_unified_address() -> impl Strategy<Value = Address> {
arb_typecodes()
.prop_flat_map(arb_unified_address_for_typecodes)
.prop_map(Address)
}
}

#[cfg(any(test, feature = "test-dependencies"))]
pub mod test_vectors;

#[cfg(test)]
mod tests {
use assert_matches::assert_matches;

use crate::{
kind::unified::{private::SealedContainer, Container, Encoding},
unified::address::testing::arb_unified_address,
Network,
};

use proptest::{prelude::*, sample::select};

use super::{Address, ParseError, Receiver, Typecode};

proptest! {
#[test]
Expand Down
114 changes: 114 additions & 0 deletions components/zcash_address/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ pub use convert::{
};
pub use encoding::ParseError;
pub use kind::unified;
use kind::unified::Receiver;
pub use zcash_protocol::consensus::NetworkType as Network;
use zcash_protocol::{PoolType, ShieldedProtocol};

/// A Zcash address.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -266,4 +268,116 @@ impl ZcashAddress {
}),
}
}

/// Returns whether this address has the ability to receive transfers of the given pool type.
pub fn can_receive_as(&self, pool_type: PoolType) -> bool {
use AddressKind::*;
match &self.kind {
Sprout(_) => false,
Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
Unified(addr) => addr.has_receiver_of_type(pool_type),
P2pkh(_) | P2sh(_) | Tex(_) => pool_type == PoolType::Transparent,
}
}

/// Returns whether this address can receive a memo.
pub fn can_receive_memo(&self) -> bool {
use AddressKind::*;
match &self.kind {
Sprout(_) | Sapling(_) => true,
Unified(addr) => addr.can_receive_memo(),
P2pkh(_) | P2sh(_) | Tex(_) => false,
}
}

/// Returns whether or not this address contains or corresponds to the given unified address
/// receiver.
pub fn matches_receiver(&self, receiver: &Receiver) -> bool {
match (&self.kind, receiver) {
(AddressKind::Unified(ua), r) => ua.contains_receiver(r),
(AddressKind::Sapling(d), Receiver::Sapling(r)) => r == d,
(AddressKind::P2pkh(d), Receiver::P2pkh(r)) => r == d,
(AddressKind::Tex(d), Receiver::P2pkh(r)) => r == d,
(AddressKind::P2sh(d), Receiver::P2sh(r)) => r == d,
_ => false,
}
}
}

#[cfg(feature = "test-dependencies")]
pub mod testing {
use std::convert::TryInto;

use proptest::{array::uniform20, collection::vec, prelude::any, prop_compose, prop_oneof};

use crate::{unified::address::testing::arb_unified_address, AddressKind, ZcashAddress};
use zcash_protocol::consensus::NetworkType;

prop_compose! {
fn arb_sprout_addr_kind()(
r_bytes in vec(any::<u8>(), 64)
) -> AddressKind {
AddressKind::Sprout(r_bytes.try_into().unwrap())
}
}

prop_compose! {
fn arb_sapling_addr_kind()(
r_bytes in vec(any::<u8>(), 43)
) -> AddressKind {
AddressKind::Sapling(r_bytes.try_into().unwrap())
}
}

prop_compose! {
fn arb_p2pkh_addr_kind()(
r_bytes in uniform20(any::<u8>())
) -> AddressKind {
AddressKind::P2pkh(r_bytes)
}
}

prop_compose! {
fn arb_p2sh_addr_kind()(
r_bytes in uniform20(any::<u8>())
) -> AddressKind {
AddressKind::P2sh(r_bytes)
}
}

prop_compose! {
fn arb_unified_addr_kind()(
uaddr in arb_unified_address()
) -> AddressKind {
AddressKind::Unified(uaddr)
}
}

prop_compose! {
fn arb_tex_addr_kind()(
r_bytes in uniform20(any::<u8>())
) -> AddressKind {
AddressKind::Tex(r_bytes)
}
}

prop_compose! {
/// Create an arbitrary, structurally-valid `ZcashAddress` value.
///
/// Note that the data contained in the generated address does _not_ necessarily correspond
/// to a valid address according to the Zcash protocol; binary data in the resulting value
/// is entirely random.
pub fn arb_address(net: NetworkType)(
kind in prop_oneof!(
arb_sprout_addr_kind(),
arb_sapling_addr_kind(),
arb_p2pkh_addr_kind(),
arb_p2sh_addr_kind(),
arb_unified_addr_kind(),
arb_tex_addr_kind()
)
) -> ZcashAddress {
ZcashAddress { net, kind }
}
}
}
2 changes: 2 additions & 0 deletions components/zcash_protocol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `zcash_protocol::PoolType::{TRANSPARENT, SAPLING, ORCHARD}`

## [0.1.1] - 2024-03-25
### Added
Expand Down
6 changes: 6 additions & 0 deletions components/zcash_protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub enum PoolType {
Shielded(ShieldedProtocol),
}

impl PoolType {
pub const TRANSPARENT: PoolType = PoolType::Transparent;
pub const SAPLING: PoolType = PoolType::Shielded(ShieldedProtocol::Sapling);
pub const ORCHARD: PoolType = PoolType::Shielded(ShieldedProtocol::Orchard);
}

impl fmt::Display for PoolType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down
Loading

0 comments on commit 5c6a6a4

Please sign in to comment.