#![recursion_limit = "256"]
#![deny(
non_upper_case_globals,
non_camel_case_types,
non_snake_case,
unused_mut,
unused_imports,
dead_code,
)]
#[macro_use]
extern crate amplify_derive;
#[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, 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,
}
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(_) | bip32::Error::RngError(_) => {
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]);
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> {
return None;
}
fn is_prv(_: &KeyVersion) -> Option<bool> {
return None;
}
fn network(_: &KeyVersion) -> Option<Self::Network> {
return None;
}
fn application(_: &KeyVersion) -> Option<Self::Application> {
return None;
}
fn derivation_path(_: &KeyVersion) -> Option<DerivationPath> {
return None;
}
fn make_pub(_: &KeyVersion) -> Option<KeyVersion> {
return None;
}
fn make_prv(_: &KeyVersion) -> Option<KeyVersion> {
return 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")
)]
#[derive(
Copy, Clone, PartialEq, Eq, Debug, Display, StrictEncode, StrictDecode,
)]
#[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("segwit-multisig")]
#[cfg_attr(feature = "serde", serde(rename = "segwit-multisig"))]
SegWitMiltisig,
#[display("nested")]
#[cfg_attr(feature = "serde", serde(rename = "nested"))]
Nested,
#[display("nested-multisig")]
#[cfg_attr(feature = "serde", serde(rename = "nested-multisig"))]
NestedMultisig,
}
#[derive(Clone, Copy, PartialEq, Eq, 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() {
"pkh" => KeyApplication::Hashed,
"sh" => KeyApplication::Hashed,
"wpkh" => KeyApplication::SegWit,
"wsh" => KeyApplication::SegWitMiltisig,
"wpkh-sh" => KeyApplication::Nested,
"wsh-sh" => KeyApplication::NestedMultisig,
_ => 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.clone()),
_ => 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.clone()),
_ => 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,
_ => 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,
_ => Err(Error::UnknownSlip32Prefix)?,
};
data[0..4].copy_from_slice(&slice);
let xprv = ExtendedPrivKey::decode(&data)?;
Ok(xprv)
}
}