#![recursion_limit = "256"]
#![deny(dead_code, /* missing_docs, */ warnings)]
#[macro_use]
extern crate amplify;
#[cfg(feature = "strict_encoding")]
#[macro_use]
extern crate strict_encoding;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde_with;
#[cfg(feature = "serde")]
extern crate serde_crate as serde;
use std::fmt::Debug;
use std::str::FromStr;
use bitcoin::util::base58;
use bitcoin::util::bip32::{self, ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey};
use bitcoin::Network;
pub const VERSION_MAGIC_XPUB: [u8; 4] = [0x04, 0x88, 0xB2, 0x1E];
pub const VERSION_MAGIC_XPRV: [u8; 4] = [0x04, 0x88, 0xAD, 0xE4];
pub const VERSION_MAGIC_YPUB: [u8; 4] = [0x04, 0x9D, 0x7C, 0xB2];
pub const VERSION_MAGIC_YPRV: [u8; 4] = [0x04, 0x9D, 0x78, 0x78];
pub const VERSION_MAGIC_ZPUB: [u8; 4] = [0x04, 0xB2, 0x47, 0x46];
pub const VERSION_MAGIC_ZPRV: [u8; 4] = [0x04, 0xB2, 0x43, 0x0C];
pub const VERSION_MAGIC_YPUB_MULTISIG: [u8; 4] = [0x02, 0x95, 0xb4, 0x3f];
pub const VERSION_MAGIC_YPRV_MULTISIG: [u8; 4] = [0x02, 0x95, 0xb0, 0x05];
pub const VERSION_MAGIC_ZPUB_MULTISIG: [u8; 4] = [0x02, 0xaa, 0x7e, 0xd3];
pub const VERSION_MAGIC_ZPRV_MULTISIG: [u8; 4] = [0x02, 0xaa, 0x7a, 0x99];
pub const VERSION_MAGIC_TPUB: [u8; 4] = [0x04, 0x35, 0x87, 0xCF];
pub const VERSION_MAGIC_TPRV: [u8; 4] = [0x04, 0x35, 0x83, 0x94];
pub const VERSION_MAGIC_UPUB: [u8; 4] = [0x04, 0x4A, 0x52, 0x62];
pub const VERSION_MAGIC_UPRV: [u8; 4] = [0x04, 0x4A, 0x4E, 0x28];
pub const VERSION_MAGIC_VPUB: [u8; 4] = [0x04, 0x5F, 0x1C, 0xF6];
pub const VERSION_MAGIC_VPRV: [u8; 4] = [0x04, 0x5F, 0x18, 0xBC];
pub const VERSION_MAGIC_UPUB_MULTISIG: [u8; 4] = [0x02, 0x42, 0x89, 0xef];
pub const VERSION_MAGIC_UPRV_MULTISIG: [u8; 4] = [0x02, 0x42, 0x85, 0xb5];
pub const VERSION_MAGIC_VPUB_MULTISIG: [u8; 4] = [0x02, 0x57, 0x54, 0x83];
pub const VERSION_MAGIC_VPRV_MULTISIG: [u8; 4] = [0x02, 0x57, 0x50, 0x48];
#[derive(
Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From, Error
)]
#[display(doc_comments)]
pub enum Error {
#[from]
Base58(base58::Error),
CannotDeriveFromHardenedKey,
InvalidChildNumber(u32),
InvalidChildNumberFormat,
InvalidDerivationPathFormat,
UnknownVersion([u8; 4]),
WrongExtendedKeyLength(usize),
UnknownSlip32Prefix,
InternalFailure,
}
#[cfg(feature = "strict_encoding")]
impl strict_encoding::StrictEncode for Error {
fn strict_encode<E: std::io::Write>(&self, _: E) -> Result<usize, strict_encoding::Error> {
unreachable!("StrictEncode for slip132::Error is a dummy required by miniscript")
}
}
#[cfg(feature = "strict_encoding")]
impl strict_encoding::StrictDecode for Error {
fn strict_decode<D: std::io::Read>(_: D) -> Result<Self, strict_encoding::Error> {
unreachable!("StrictDecode for slip132::Error is a dummy required by miniscript")
}
}
impl From<bip32::Error> for Error {
fn from(err: bip32::Error) -> Self {
match err {
bip32::Error::CannotDeriveFromHardenedKey => Error::CannotDeriveFromHardenedKey,
bip32::Error::InvalidChildNumber(no) => Error::InvalidChildNumber(no),
bip32::Error::InvalidChildNumberFormat => Error::InvalidChildNumberFormat,
bip32::Error::InvalidDerivationPathFormat => Error::InvalidDerivationPathFormat,
bip32::Error::Ecdsa(_) => Error::InternalFailure,
bip32::Error::UnknownVersion(ver) => Error::UnknownVersion(ver),
bip32::Error::WrongExtendedKeyLength(len) => Error::WrongExtendedKeyLength(len),
bip32::Error::Base58(err) => Error::Base58(err),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct KeyVersion([u8; 4]);
#[cfg(feature = "strict_encoding")]
impl strict_encoding::StrictEncode for KeyVersion {
fn strict_encode<E: std::io::Write>(&self, mut e: E) -> Result<usize, strict_encoding::Error> {
e.write_all(&self.0)?;
Ok(4)
}
}
#[cfg(feature = "strict_encoding")]
impl strict_encoding::StrictDecode for KeyVersion {
fn strict_decode<D: std::io::Read>(mut d: D) -> Result<Self, strict_encoding::Error> {
let mut bytes = [0u8; 4];
d.read_exact(&mut bytes)?;
Ok(Self(bytes))
}
}
pub trait VersionResolver:
Copy + Clone + PartialEq + Eq + PartialOrd + Ord + std::hash::Hash + Debug
{
type Network;
type Application;
fn resolve(
network: Self::Network,
applicable_for: Self::Application,
is_priv: bool,
) -> KeyVersion;
fn is_pub(_: &KeyVersion) -> Option<bool> { None }
fn is_prv(_: &KeyVersion) -> Option<bool> { None }
fn network(_: &KeyVersion) -> Option<Self::Network> { None }
fn application(_: &KeyVersion) -> Option<Self::Application> { None }
fn derivation_path(_: &KeyVersion) -> Option<DerivationPath> { None }
fn make_pub(_: &KeyVersion) -> Option<KeyVersion> { None }
fn make_prv(_: &KeyVersion) -> Option<KeyVersion> { None }
}
impl KeyVersion {
pub fn is_pub<R: VersionResolver>(&self) -> Option<bool> { R::is_pub(self) }
pub fn is_prv<R: VersionResolver>(&self) -> Option<bool> { R::is_prv(self) }
pub fn network<R: VersionResolver>(&self) -> Option<R::Network> { R::network(self) }
pub fn application<R: VersionResolver>(&self) -> Option<R::Application> { R::application(self) }
pub fn derivation_path<R: VersionResolver>(&self) -> Option<DerivationPath> {
R::derivation_path(self)
}
pub fn try_to_pub<R: VersionResolver>(&self) -> Option<KeyVersion> { R::make_pub(self) }
pub fn try_to_prv<R: VersionResolver>(&self) -> Option<KeyVersion> { R::make_prv(self) }
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct DefaultResolver;
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)]
#[non_exhaustive]
pub enum KeyApplication {
#[display("hashed")]
#[cfg_attr(feature = "serde", serde(rename = "hashed"))]
Hashed,
#[display("segwit")]
#[cfg_attr(feature = "serde", serde(rename = "segwit"))]
SegWit,
#[display("segwitmultisig")]
#[cfg_attr(feature = "serde", serde(rename = "segwit-multisig"))]
SegWitMiltisig,
#[display("nested")]
#[cfg_attr(feature = "serde", serde(rename = "nested"))]
Nested,
#[display("nestedmultisig")]
#[cfg_attr(feature = "serde", serde(rename = "nested-multisig"))]
NestedMultisig,
}
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error
)]
#[display(doc_comments)]
pub struct UnknownKeyApplicationError;
impl FromStr for KeyApplication {
type Err = UnknownKeyApplicationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"hashed" => KeyApplication::Hashed,
"segwit" => KeyApplication::SegWit,
"segwitmultisig" => KeyApplication::SegWitMiltisig,
"nested" => KeyApplication::Nested,
"nestedmultisig" => KeyApplication::NestedMultisig,
_ => return Err(UnknownKeyApplicationError),
})
}
}
impl KeyVersion {
pub fn from_slice(version_slice: &[u8]) -> Option<KeyVersion> {
if version_slice.len() != 4 {
return None;
}
let mut bytes = [0u8; 4];
bytes.copy_from_slice(version_slice);
Some(KeyVersion::from_bytes(bytes))
}
pub fn from_bytes(version_bytes: [u8; 4]) -> KeyVersion { KeyVersion(version_bytes) }
pub fn from_u32(version: u32) -> KeyVersion { KeyVersion(version.to_be_bytes()) }
pub fn to_u32(&self) -> u32 { u32::from_be_bytes(self.0) }
pub fn as_slice(&self) -> &[u8] { &self.0 }
pub fn as_bytes(&self) -> &[u8; 4] { &self.0 }
pub fn to_bytes(&self) -> [u8; 4] { self.0 }
pub fn into_bytes(self) -> [u8; 4] { self.0 }
}
impl VersionResolver for DefaultResolver {
type Network = Network;
type Application = KeyApplication;
fn resolve(
network: Self::Network,
applicable_for: Self::Application,
is_priv: bool,
) -> KeyVersion {
match (network, applicable_for, is_priv) {
(Network::Bitcoin, KeyApplication::Hashed, false) => KeyVersion(VERSION_MAGIC_XPUB),
(Network::Bitcoin, KeyApplication::Hashed, true) => KeyVersion(VERSION_MAGIC_XPRV),
(Network::Bitcoin, KeyApplication::Nested, false) => KeyVersion(VERSION_MAGIC_YPUB),
(Network::Bitcoin, KeyApplication::Nested, true) => KeyVersion(VERSION_MAGIC_YPRV),
(Network::Bitcoin, KeyApplication::SegWit, false) => KeyVersion(VERSION_MAGIC_ZPUB),
(Network::Bitcoin, KeyApplication::SegWit, true) => KeyVersion(VERSION_MAGIC_ZPRV),
(Network::Bitcoin, KeyApplication::NestedMultisig, false) => {
KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)
}
(Network::Bitcoin, KeyApplication::NestedMultisig, true) => {
KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)
}
(Network::Bitcoin, KeyApplication::SegWitMiltisig, false) => {
KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)
}
(Network::Bitcoin, KeyApplication::SegWitMiltisig, true) => {
KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)
}
(_, KeyApplication::Hashed, false) => KeyVersion(VERSION_MAGIC_TPUB),
(_, KeyApplication::Hashed, true) => KeyVersion(VERSION_MAGIC_TPRV),
(_, KeyApplication::Nested, false) => KeyVersion(VERSION_MAGIC_UPUB),
(_, KeyApplication::Nested, true) => KeyVersion(VERSION_MAGIC_UPRV),
(_, KeyApplication::SegWit, false) => KeyVersion(VERSION_MAGIC_VPUB),
(_, KeyApplication::SegWit, true) => KeyVersion(VERSION_MAGIC_VPRV),
(_, KeyApplication::NestedMultisig, false) => KeyVersion(VERSION_MAGIC_UPUB_MULTISIG),
(_, KeyApplication::NestedMultisig, true) => KeyVersion(VERSION_MAGIC_UPRV_MULTISIG),
(_, KeyApplication::SegWitMiltisig, false) => KeyVersion(VERSION_MAGIC_VPUB_MULTISIG),
(_, KeyApplication::SegWitMiltisig, true) => KeyVersion(VERSION_MAGIC_VPRV_MULTISIG),
}
}
fn is_pub(kv: &KeyVersion) -> Option<bool> {
match kv.as_bytes() {
&VERSION_MAGIC_XPUB
| &VERSION_MAGIC_YPUB
| &VERSION_MAGIC_ZPUB
| &VERSION_MAGIC_TPUB
| &VERSION_MAGIC_UPUB
| &VERSION_MAGIC_VPUB
| &VERSION_MAGIC_YPUB_MULTISIG
| &VERSION_MAGIC_ZPUB_MULTISIG
| &VERSION_MAGIC_UPUB_MULTISIG
| &VERSION_MAGIC_VPUB_MULTISIG => Some(true),
&VERSION_MAGIC_XPRV
| &VERSION_MAGIC_YPRV
| &VERSION_MAGIC_ZPRV
| &VERSION_MAGIC_TPRV
| &VERSION_MAGIC_UPRV
| &VERSION_MAGIC_VPRV
| &VERSION_MAGIC_YPRV_MULTISIG
| &VERSION_MAGIC_ZPRV_MULTISIG
| &VERSION_MAGIC_UPRV_MULTISIG
| &VERSION_MAGIC_VPRV_MULTISIG => Some(false),
_ => None,
}
}
fn is_prv(kv: &KeyVersion) -> Option<bool> { DefaultResolver::is_pub(kv).map(|v| !v) }
fn network(kv: &KeyVersion) -> Option<Self::Network> {
match kv.as_bytes() {
&VERSION_MAGIC_XPRV
| &VERSION_MAGIC_XPUB
| &VERSION_MAGIC_YPRV
| &VERSION_MAGIC_YPUB
| &VERSION_MAGIC_ZPRV
| &VERSION_MAGIC_ZPUB
| &VERSION_MAGIC_YPRV_MULTISIG
| &VERSION_MAGIC_YPUB_MULTISIG
| &VERSION_MAGIC_ZPRV_MULTISIG
| &VERSION_MAGIC_ZPUB_MULTISIG => Some(Network::Bitcoin),
&VERSION_MAGIC_TPRV
| &VERSION_MAGIC_TPUB
| &VERSION_MAGIC_UPRV
| &VERSION_MAGIC_UPUB
| &VERSION_MAGIC_VPRV
| &VERSION_MAGIC_VPUB
| &VERSION_MAGIC_UPRV_MULTISIG
| &VERSION_MAGIC_UPUB_MULTISIG
| &VERSION_MAGIC_VPRV_MULTISIG
| &VERSION_MAGIC_VPUB_MULTISIG => Some(Network::Testnet),
_ => None,
}
}
fn application(kv: &KeyVersion) -> Option<Self::Application> {
match kv.as_bytes() {
&VERSION_MAGIC_XPUB | &VERSION_MAGIC_XPRV | &VERSION_MAGIC_TPUB
| &VERSION_MAGIC_TPRV => Some(KeyApplication::Hashed),
&VERSION_MAGIC_YPUB | &VERSION_MAGIC_YPRV | &VERSION_MAGIC_UPUB
| &VERSION_MAGIC_UPRV => Some(KeyApplication::Nested),
&VERSION_MAGIC_YPUB_MULTISIG
| &VERSION_MAGIC_YPRV_MULTISIG
| &VERSION_MAGIC_UPUB_MULTISIG
| &VERSION_MAGIC_UPRV_MULTISIG => Some(KeyApplication::NestedMultisig),
&VERSION_MAGIC_ZPUB | &VERSION_MAGIC_ZPRV | &VERSION_MAGIC_VPUB
| &VERSION_MAGIC_VPRV => Some(KeyApplication::SegWit),
&VERSION_MAGIC_ZPUB_MULTISIG
| &VERSION_MAGIC_ZPRV_MULTISIG
| &VERSION_MAGIC_VPUB_MULTISIG
| &VERSION_MAGIC_VPRV_MULTISIG => Some(KeyApplication::SegWitMiltisig),
_ => None,
}
}
fn derivation_path(kv: &KeyVersion) -> Option<DerivationPath> {
match kv.as_bytes() {
&VERSION_MAGIC_XPUB | &VERSION_MAGIC_XPRV => Some(DerivationPath::from(vec![
ChildNumber::Hardened { index: 44 },
ChildNumber::Hardened { index: 0 },
])),
&VERSION_MAGIC_TPUB | &VERSION_MAGIC_TPRV => Some(DerivationPath::from(vec![
ChildNumber::Hardened { index: 44 },
ChildNumber::Hardened { index: 1 },
])),
&VERSION_MAGIC_YPUB | &VERSION_MAGIC_YPRV => Some(DerivationPath::from(vec![
ChildNumber::Hardened { index: 49 },
ChildNumber::Hardened { index: 0 },
])),
&VERSION_MAGIC_UPUB | &VERSION_MAGIC_UPRV => Some(DerivationPath::from(vec![
ChildNumber::Hardened { index: 49 },
ChildNumber::Hardened { index: 1 },
])),
&VERSION_MAGIC_ZPUB | &VERSION_MAGIC_ZPRV => Some(DerivationPath::from(vec![
ChildNumber::Hardened { index: 84 },
ChildNumber::Hardened { index: 0 },
])),
&VERSION_MAGIC_VPUB | &VERSION_MAGIC_VPRV => Some(DerivationPath::from(vec![
ChildNumber::Hardened { index: 84 },
ChildNumber::Hardened { index: 1 },
])),
_ => None,
}
}
fn make_pub(kv: &KeyVersion) -> Option<KeyVersion> {
match kv.as_bytes() {
&VERSION_MAGIC_XPRV => Some(KeyVersion::from_bytes(VERSION_MAGIC_XPUB)),
&VERSION_MAGIC_YPRV => Some(KeyVersion::from_bytes(VERSION_MAGIC_YPUB)),
&VERSION_MAGIC_ZPRV => Some(KeyVersion::from_bytes(VERSION_MAGIC_ZPUB)),
&VERSION_MAGIC_TPRV => Some(KeyVersion::from_bytes(VERSION_MAGIC_TPUB)),
&VERSION_MAGIC_UPRV => Some(KeyVersion::from_bytes(VERSION_MAGIC_UPUB)),
&VERSION_MAGIC_VPRV => Some(KeyVersion::from_bytes(VERSION_MAGIC_VPUB)),
&VERSION_MAGIC_YPRV_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_YPUB_MULTISIG))
}
&VERSION_MAGIC_ZPRV_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_ZPUB_MULTISIG))
}
&VERSION_MAGIC_UPRV_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_UPUB_MULTISIG))
}
&VERSION_MAGIC_VPRV_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_VPUB_MULTISIG))
}
&VERSION_MAGIC_XPUB
| &VERSION_MAGIC_YPUB
| &VERSION_MAGIC_ZPUB
| &VERSION_MAGIC_TPUB
| &VERSION_MAGIC_UPUB
| &VERSION_MAGIC_VPUB
| &VERSION_MAGIC_YPUB_MULTISIG
| &VERSION_MAGIC_ZPUB_MULTISIG
| &VERSION_MAGIC_UPUB_MULTISIG
| &VERSION_MAGIC_VPUB_MULTISIG => Some(*kv),
_ => None,
}
}
fn make_prv(kv: &KeyVersion) -> Option<KeyVersion> {
match kv.as_bytes() {
&VERSION_MAGIC_XPUB => Some(KeyVersion::from_bytes(VERSION_MAGIC_XPRV)),
&VERSION_MAGIC_YPUB => Some(KeyVersion::from_bytes(VERSION_MAGIC_YPRV)),
&VERSION_MAGIC_ZPUB => Some(KeyVersion::from_bytes(VERSION_MAGIC_ZPRV)),
&VERSION_MAGIC_TPUB => Some(KeyVersion::from_bytes(VERSION_MAGIC_TPRV)),
&VERSION_MAGIC_UPUB => Some(KeyVersion::from_bytes(VERSION_MAGIC_UPRV)),
&VERSION_MAGIC_VPUB => Some(KeyVersion::from_bytes(VERSION_MAGIC_VPRV)),
&VERSION_MAGIC_YPUB_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_YPRV_MULTISIG))
}
&VERSION_MAGIC_ZPUB_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_ZPRV_MULTISIG))
}
&VERSION_MAGIC_UPUB_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_UPRV_MULTISIG))
}
&VERSION_MAGIC_VPUB_MULTISIG => {
Some(KeyVersion::from_bytes(VERSION_MAGIC_VPRV_MULTISIG))
}
&VERSION_MAGIC_XPRV
| &VERSION_MAGIC_YPRV
| &VERSION_MAGIC_ZPRV
| &VERSION_MAGIC_TPRV
| &VERSION_MAGIC_UPRV
| &VERSION_MAGIC_VPRV
| &VERSION_MAGIC_YPRV_MULTISIG
| &VERSION_MAGIC_ZPRV_MULTISIG
| &VERSION_MAGIC_UPRV_MULTISIG
| &VERSION_MAGIC_VPRV_MULTISIG => Some(*kv),
_ => None,
}
}
}
pub trait FromSlip132 {
fn from_slip132_str(s: &str) -> Result<Self, Error>
where
Self: Sized;
}
impl FromSlip132 for ExtendedPubKey {
fn from_slip132_str(s: &str) -> Result<Self, Error> {
let mut data = base58::from_check(s)?;
let mut prefix = [0u8; 4];
prefix.copy_from_slice(&data[0..4]);
let slice = match prefix {
VERSION_MAGIC_XPUB
| VERSION_MAGIC_YPUB
| VERSION_MAGIC_ZPUB
| VERSION_MAGIC_YPUB_MULTISIG
| VERSION_MAGIC_ZPUB_MULTISIG => VERSION_MAGIC_XPUB,
VERSION_MAGIC_TPUB
| VERSION_MAGIC_UPUB
| VERSION_MAGIC_VPUB
| VERSION_MAGIC_UPUB_MULTISIG
| VERSION_MAGIC_VPUB_MULTISIG => VERSION_MAGIC_TPUB,
_ => return Err(Error::UnknownSlip32Prefix),
};
data[0..4].copy_from_slice(&slice);
let xpub = ExtendedPubKey::decode(&data)?;
Ok(xpub)
}
}
impl FromSlip132 for ExtendedPrivKey {
fn from_slip132_str(s: &str) -> Result<Self, Error> {
let mut data = base58::from_check(s)?;
let mut prefix = [0u8; 4];
prefix.copy_from_slice(&data[0..4]);
let slice = match prefix {
VERSION_MAGIC_XPRV
| VERSION_MAGIC_YPRV
| VERSION_MAGIC_ZPRV
| VERSION_MAGIC_YPRV_MULTISIG
| VERSION_MAGIC_ZPRV_MULTISIG => VERSION_MAGIC_XPRV,
VERSION_MAGIC_TPRV
| VERSION_MAGIC_UPRV
| VERSION_MAGIC_VPRV
| VERSION_MAGIC_UPRV_MULTISIG
| VERSION_MAGIC_VPRV_MULTISIG => VERSION_MAGIC_TPRV,
_ => return Err(Error::UnknownSlip32Prefix),
};
data[0..4].copy_from_slice(&slice);
let xprv = ExtendedPrivKey::decode(&data)?;
Ok(xprv)
}
}