Generalize the naming of unified containers and items.

This commit is contained in:
Kris Nuttycombe 2021-12-02 09:05:24 -07:00
parent 75591047f7
commit 82be04dfaf
3 changed files with 47 additions and 45 deletions

View File

@ -2,7 +2,7 @@ use std::{convert::TryInto, error::Error, fmt, str::FromStr};
use bech32::{self, FromBase32, ToBase32, Variant};
use crate::kind::unified::{private::SealedContainer, FromReceivers};
use crate::kind::unified::{private::SealedContainer, Encoding};
use crate::{kind::*, AddressKind, Network, ZcashAddress};
/// An error while attempting to parse a string as a Zcash address.

View File

@ -29,10 +29,10 @@ impl Ord for Typecode {
| (Self::P2sh, Self::P2sh)
| (Self::P2pkh, Self::P2pkh) => cmp::Ordering::Equal,
// We don't know for certain the preference order of unknown receivers, but it
// We don't know for certain the preference order of unknown items, but it
// is likely that the higher typecode has higher preference. The exact order
// doesn't really matter, as unknown receivers have lower preference than
// known receivers.
// doesn't really matter, as unknown items have lower preference than
// known items.
(Self::Unknown(a), Self::Unknown(b)) => b.cmp(a),
// For the remaining cases, we rely on `match` always choosing the first arm
@ -96,7 +96,7 @@ impl Typecode {
/// An error while attempting to parse a string as a Zcash address.
#[derive(Debug, PartialEq)]
pub enum ParseError {
/// The unified address contains both P2PKH and P2SH receivers.
/// The unified address contains both P2PKH and P2SH items.
BothP2phkAndP2sh,
/// The unified address contains a duplicated typecode.
DuplicateTypecode(Typecode),
@ -104,18 +104,18 @@ pub enum ParseError {
InvalidTypecodeValue(u64),
/// The string is an invalid encoding.
InvalidEncoding(String),
/// The unified address only contains transparent receivers.
/// The unified address only contains transparent items.
OnlyTransparent,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::BothP2phkAndP2sh => write!(f, "UA contains both P2PKH and P2SH receivers"),
ParseError::BothP2phkAndP2sh => write!(f, "UA contains both P2PKH and P2SH items"),
ParseError::DuplicateTypecode(c) => write!(f, "Duplicate typecode {}", u32::from(*c)),
ParseError::InvalidTypecodeValue(v) => write!(f, "Typecode value out of range {}", v),
ParseError::InvalidEncoding(msg) => write!(f, "Invalid encoding: {}", msg),
ParseError::OnlyTransparent => write!(f, "UA only contains transparent receivers"),
ParseError::OnlyTransparent => write!(f, "UA only contains transparent items"),
}
}
}
@ -132,7 +132,7 @@ pub(crate) mod private {
use zcash_encoding::CompactSize;
/// A raw address or viewing key.
pub trait SealedReceiver:
pub trait SealedItem:
for<'a> TryFrom<(u32, &'a [u8]), Error = ParseError> + cmp::Ord + cmp::PartialOrd + Clone
{
fn typecode(&self) -> Typecode;
@ -140,20 +140,22 @@ pub(crate) mod private {
}
/// A Unified Container containing addresses or viewing keys.
pub trait SealedContainer: super::ToReceivers {
pub trait SealedContainer: super::Container {
const MAINNET: &'static str;
const TESTNET: &'static str;
const REGTEST: &'static str;
fn from_inner(receivers: Vec<Self::Receiver>) -> Self;
/// Implementations of this method should act as unchecked constructors
/// of the container type; the caller is guaranteed to check the
/// general invariants that apply to all unified containers.
fn from_inner(items: Vec<Self::Item>) -> Self;
/// Write the raw encoding of this container's receivers to a stream
fn write_raw_encoding<W: Write>(&self, mut writer: W) {
for receiver in &self.receivers() {
let addr = receiver.data();
for item in &self.items() {
let addr = item.data();
CompactSize::write(
&mut writer,
<u32>::from(receiver.typecode()).try_into().unwrap(),
<u32>::from(item.typecode()).try_into().unwrap(),
)
.unwrap();
CompactSize::write(&mut writer, addr.len()).unwrap();
@ -177,12 +179,12 @@ pub(crate) mod private {
}
}
use private::SealedReceiver;
use private::SealedItem;
/// Trait providing common encoding logic for Unified containers.
pub trait FromReceivers: private::SealedContainer + std::marker::Sized {
pub trait Encoding: private::SealedContainer + std::marker::Sized {
fn try_from_bytes(hrp: &str, buf: &[u8]) -> Result<Self, ParseError> {
fn read_receiver<R: SealedReceiver>(
fn read_receiver<R: SealedItem>(
mut cursor: &mut std::io::Cursor<&[u8]>,
) -> Result<R, ParseError> {
let typecode = CompactSize::read(&mut cursor)
@ -244,13 +246,13 @@ pub trait FromReceivers: private::SealedContainer + std::marker::Sized {
result.push(read_receiver(&mut cursor)?);
}
assert_eq!(cursor.position(), encoded.len().try_into().unwrap());
Self::try_from_receivers(result)
Self::try_from_items(result)
}
fn try_from_receivers(receivers: Vec<Self::Receiver>) -> Result<Self, ParseError> {
let mut typecodes = HashSet::with_capacity(receivers.len());
for receiver in &receivers {
let t = receiver.typecode();
fn try_from_items(items: Vec<Self::Item>) -> Result<Self, ParseError> {
let mut typecodes = HashSet::with_capacity(items.len());
for item in &items {
let t = item.typecode();
if typecodes.contains(&t) {
return Err(ParseError::DuplicateTypecode(t));
} else if (t == Typecode::P2pkh && typecodes.contains(&Typecode::P2sh))
@ -266,27 +268,27 @@ pub trait FromReceivers: private::SealedContainer + std::marker::Sized {
Err(ParseError::OnlyTransparent)
} else {
// All checks pass!
Ok(Self::from_inner(receivers))
Ok(Self::from_inner(items))
}
}
}
/// Trait providing common decoding logic for Unified containers.
pub trait ToReceivers {
/// The type of receiver in this unified container.
type Receiver: SealedReceiver;
pub trait Container {
/// The type of item in this unified container.
type Item: SealedItem;
/// Returns the receivers contained within this container, sorted in preference order.
fn receivers(&self) -> Vec<Self::Receiver> {
let mut receivers = self.receivers_as_parsed().to_vec();
// Unstable sorting is fine, because all receivers are guaranteed by construction
/// Returns the items contained within this container, sorted in preference order.
fn items(&self) -> Vec<Self::Item> {
let mut items = self.items_as_parsed().to_vec();
// Unstable sorting is fine, because all items are guaranteed by construction
// to have distinct typecodes.
receivers.sort_unstable_by_key(|r| r.typecode());
receivers
items.sort_unstable_by_key(|r| r.typecode());
items
}
/// Returns the receivers in the order they were parsed from the string encoding.
/// Returns the items in the order they were parsed from the string encoding.
///
/// This API is for advanced usage; in most cases you should use `Self::receivers`.
fn receivers_as_parsed(&self) -> &[Self::Receiver];
/// This API is for advanced usage; in most cases you should use `Self::items`.
fn items_as_parsed(&self) -> &[Self::Item];
}

View File

@ -1,4 +1,4 @@
use super::{private::SealedReceiver, ParseError, Typecode};
use super::{private::SealedItem, ParseError, Typecode};
use crate::kind;
use std::cmp;
@ -49,7 +49,7 @@ impl TryFrom<(u32, &[u8])> for Receiver {
}
}
impl SealedReceiver for Receiver {
impl SealedItem for Receiver {
fn typecode(&self) -> Typecode {
match self {
Receiver::P2pkh(_) => Typecode::P2pkh,
@ -93,16 +93,16 @@ impl super::private::SealedContainer for Address {
/// The HRP for a Bech32m-encoded regtest Unified Address.
const REGTEST: &'static str = "uregtest";
fn from_inner(receivers: Vec<Self::Receiver>) -> Self {
fn from_inner(receivers: Vec<Self::Item>) -> Self {
Self(receivers)
}
}
impl super::FromReceivers for Address {}
impl super::ToReceivers for Address {
type Receiver = Receiver;
impl super::Encoding for Address {}
impl super::Container for Address {
type Item = Receiver;
fn receivers_as_parsed(&self) -> &[Receiver] {
fn items_as_parsed(&self) -> &[Receiver] {
&self.0
}
}
@ -114,7 +114,7 @@ pub(crate) mod test_vectors;
mod tests {
use assert_matches::assert_matches;
use crate::kind::unified::{private::SealedContainer, FromReceivers, ToReceivers};
use crate::kind::unified::{private::SealedContainer, Container, Encoding};
use proptest::{
array::{uniform11, uniform20, uniform32},
prelude::*,
@ -295,7 +295,7 @@ mod tests {
// `Address::receivers` sorts the receivers in priority order.
assert_eq!(
ua.receivers(),
ua.items(),
vec![
Receiver::Orchard([0; 43]),
Receiver::Sapling([0; 43]),