unified::ivk: Parse unified incoming viewing keys.
This commit is contained in:
parent
5bb77e9149
commit
7cd12f4ee2
|
@ -6,6 +6,7 @@ use std::fmt;
|
||||||
use zcash_encoding::CompactSize;
|
use zcash_encoding::CompactSize;
|
||||||
|
|
||||||
pub(crate) mod address;
|
pub(crate) mod address;
|
||||||
|
pub(crate) mod ivk;
|
||||||
|
|
||||||
pub use address::Address;
|
pub use address::Address;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
use std::cmp;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
|
use crate::kind;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
private::{SealedContainer, SealedItem},
|
||||||
|
Encoding, ParseError, Container, Typecode,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The set of known IVKs for Unified IVKs.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Ivk {
|
||||||
|
/// The raw encoding of an Orchard Incoming Viewing Key.
|
||||||
|
///
|
||||||
|
/// `(dk, ivk)` each 32 bytes.
|
||||||
|
Orchard([u8; 64]),
|
||||||
|
|
||||||
|
/// Data contained within the Sapling component of a Unified Incoming Viewing Key.
|
||||||
|
///
|
||||||
|
/// In order to ensure that Unified Addresses can always be derived from UIVKs, we
|
||||||
|
/// store more data here than was specified to be part of a Sapling IVK. Specifically,
|
||||||
|
/// we store the same data here as we do for Orchard.
|
||||||
|
///
|
||||||
|
/// `(dk, ivk)` each 32 bytes.
|
||||||
|
Sapling([u8; 64]),
|
||||||
|
|
||||||
|
/// The extended public key for the BIP 44 account corresponding to the transparent
|
||||||
|
/// address subtree from which transparent addresses are derived.
|
||||||
|
///
|
||||||
|
/// Transparent addresses don't have "viewing keys" - the addresses themselves serve
|
||||||
|
/// that purpose. However, we want the ability to derive diversified Unified Addresses
|
||||||
|
/// from Unified Viewing Keys, and to not break the unlinkability property when they
|
||||||
|
/// include transparent receivers. To achieve this, we treat the last hardened node in
|
||||||
|
/// the BIP 44 derivation path as the "transparent viewing key"; all addresses derived
|
||||||
|
/// from this node use non-hardened derivation, and can thus be derived just from this
|
||||||
|
/// extended public key.
|
||||||
|
P2pkh([u8; 78]),
|
||||||
|
|
||||||
|
/// The raw data of a P2SH address.
|
||||||
|
///
|
||||||
|
/// # Security
|
||||||
|
///
|
||||||
|
/// P2SH addresses are hashes of scripts, and as such have no generic HD mechanism for
|
||||||
|
/// us to derive independent-but-linked P2SH addresses. As such, if someone constructs
|
||||||
|
/// a UIVK containing a P2SH address, and then derives diversified UAs from it, those
|
||||||
|
/// UAs will be trivially linkable as they will share the same P2SH address.
|
||||||
|
P2sh(kind::p2sh::Data),
|
||||||
|
|
||||||
|
Unknown {
|
||||||
|
typecode: u32,
|
||||||
|
data: Vec<u8>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl cmp::Ord for Ivk {
|
||||||
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
|
match self.typecode().cmp(&other.typecode()) {
|
||||||
|
cmp::Ordering::Equal => self.data().cmp(other.data()),
|
||||||
|
res => res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl cmp::PartialOrd for Ivk {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(u32, &[u8])> for Ivk {
|
||||||
|
type Error = ParseError;
|
||||||
|
|
||||||
|
fn try_from((typecode, data): (u32, &[u8])) -> Result<Self, Self::Error> {
|
||||||
|
match typecode.try_into()? {
|
||||||
|
Typecode::P2pkh => data.try_into().map(Ivk::P2pkh),
|
||||||
|
Typecode::P2sh => data.try_into().map(Ivk::P2sh),
|
||||||
|
Typecode::Sapling => data.try_into().map(Ivk::Sapling),
|
||||||
|
Typecode::Orchard => data.try_into().map(Ivk::Orchard),
|
||||||
|
Typecode::Unknown(_) => Ok(Ivk::Unknown {
|
||||||
|
typecode,
|
||||||
|
data: data.to_vec(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
.map_err(|e| {
|
||||||
|
ParseError::InvalidEncoding(format!("Invalid ivk for typecode {}: {:?}", typecode, e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SealedItem for Ivk {
|
||||||
|
fn typecode(&self) -> Typecode {
|
||||||
|
match self {
|
||||||
|
Ivk::P2pkh(_) => Typecode::P2pkh,
|
||||||
|
Ivk::P2sh(_) => Typecode::P2sh,
|
||||||
|
Ivk::Sapling(_) => Typecode::Sapling,
|
||||||
|
Ivk::Orchard(_) => Typecode::Orchard,
|
||||||
|
Ivk::Unknown { typecode, .. } => Typecode::Unknown(*typecode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data(&self) -> &[u8] {
|
||||||
|
match self {
|
||||||
|
Ivk::P2pkh(data) => data,
|
||||||
|
Ivk::P2sh(data) => data,
|
||||||
|
Ivk::Sapling(data) => data,
|
||||||
|
Ivk::Orchard(data) => data,
|
||||||
|
Ivk::Unknown { data, .. } => data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Unified Incoming Viewing Key.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Uivk(pub(crate) Vec<Ivk>);
|
||||||
|
|
||||||
|
impl Container for Uivk {
|
||||||
|
type Item = Ivk;
|
||||||
|
|
||||||
|
/// Returns the IVKs contained within this UIVK, in the order they were
|
||||||
|
/// parsed from the string encoding.
|
||||||
|
///
|
||||||
|
/// This API is for advanced usage; in most cases you should use `Uivk::items`.
|
||||||
|
fn items_as_parsed(&self) -> &[Ivk] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encoding for Uivk {}
|
||||||
|
|
||||||
|
impl SealedContainer for Uivk {
|
||||||
|
/// The HRP for a Bech32m-encoded mainnet Unified IVK.
|
||||||
|
///
|
||||||
|
/// Defined in [ZIP 316][zip-0316].
|
||||||
|
///
|
||||||
|
/// [zip-0316]: https://zips.z.cash/zip-0316
|
||||||
|
const MAINNET: &'static str = "uivk";
|
||||||
|
|
||||||
|
/// The HRP for a Bech32m-encoded testnet Unified IVK.
|
||||||
|
///
|
||||||
|
/// Defined in [ZIP 316][zip-0316].
|
||||||
|
///
|
||||||
|
/// [zip-0316]: https://zips.z.cash/zip-0316
|
||||||
|
const TESTNET: &'static str = "uivktest";
|
||||||
|
|
||||||
|
/// The HRP for a Bech32m-encoded regtest Unified IVK.
|
||||||
|
const REGTEST: &'static str = "uivkregtest";
|
||||||
|
|
||||||
|
fn from_inner(ivks: Vec<Self::Item>) -> Self {
|
||||||
|
Self(ivks)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue