Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract zip321 crate from zcash_client_backend #1143

Merged
merged 11 commits into from
Apr 22, 2024
Merged
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
nuttycom marked this conversation as resolved.
Show resolved Hide resolved
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
85 changes: 66 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,31 @@ 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(_) => pool_type == PoolType::Transparent,
Receiver::P2sh(_) => pool_type == PoolType::Transparent,
nuttycom marked this conversation as resolved.
Show resolved Hide resolved
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 +160,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 +183,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 +200,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 +210,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 ikely to not represent valid protocol
daira marked this conversation as resolved.
Show resolved Hide resolved
/// 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 +231,33 @@ mod tests {
.collect::<Vec<_>>()
}

fn arb_unified_address() -> impl Strategy<Value = Address> {
/// Generates an arbitrary Unified address. The receivers of this address are ikely to not
daira marked this conversation as resolved.
Show resolved Hide resolved
/// 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
112 changes: 112 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,114 @@ 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 {
match &self.kind {
AddressKind::Sprout(_) => false,
AddressKind::Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
AddressKind::Unified(addr) => addr.has_receiver_of_type(pool_type),
AddressKind::P2pkh(_) => pool_type == PoolType::Transparent,
AddressKind::P2sh(_) => pool_type == PoolType::Transparent,
AddressKind::Tex(_) => pool_type == PoolType::Transparent,
nuttycom marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Returns whether this address can receive a memo.
pub fn can_receive_memo(&self) -> bool {
match &self.kind {
AddressKind::Sprout(_) => true,
AddressKind::Sapling(_) => true,
AddressKind::Unified(addr) => addr.can_receive_memo(),
AddressKind::P2pkh(_) => false,
AddressKind::P2sh(_) => false,
AddressKind::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 (receiver, &self.kind) {
(r, AddressKind::Unified(ua)) => ua.contains_receiver(r),
(Receiver::Sapling(r), AddressKind::Sapling(d)) => r == d,
(Receiver::P2pkh(r), AddressKind::P2pkh(d)) => r == d,
(Receiver::P2pkh(r), AddressKind::Tex(d)) => r == d,
(Receiver::P2sh(r), AddressKind::P2sh(d)) => r == d,
_ => false,
}
nuttycom marked this conversation as resolved.
Show resolved Hide resolved
}
}

#[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! {
pub fn arb_address(net: NetworkType)(
nuttycom marked this conversation as resolved.
Show resolved Hide resolved
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
Loading