use core::str::FromStr;
use alloc::{string::String, vec::Vec};
use bitcoin::bip32;
use bitcoin::hashes::Hash;
use bitcoin::{PubkeyHash, script::Builder, secp256k1};
use crate::descriptor::Descriptor;
use crate::parser::{ParseError, Position};
use alloc::string::ToString;
#[derive(Clone)]
pub struct KeyToken {
inner: KeyTokenInner,
}
#[derive(Clone)]
pub(crate) enum KeyTokenInner {
PublicKey(bitcoin::PublicKey),
XOnlyPublicKey(bitcoin::XOnlyPublicKey),
ExtendedKey(ExtendedKey),
}
impl KeyToken {
#[inline]
pub(crate) fn new(inner: KeyTokenInner) -> Self {
Self { inner }
}
pub fn is_compressed(&self) -> bool {
match &self.inner {
KeyTokenInner::PublicKey(pk) => pk.compressed,
KeyTokenInner::XOnlyPublicKey(_) => true,
KeyTokenInner::ExtendedKey(_) => true,
}
}
pub fn identifier(&self) -> String {
match &self.inner {
KeyTokenInner::PublicKey(pk) => pk.to_string(),
KeyTokenInner::XOnlyPublicKey(pk) => pk.to_string(),
KeyTokenInner::ExtendedKey(ext) => ext.identifier(),
}
}
pub fn as_definite_key(&self) -> Option<DefiniteKeyToken> {
match &self.inner {
KeyTokenInner::PublicKey(pk) => Some(DefiniteKeyToken::PublicKey(*pk)),
KeyTokenInner::XOnlyPublicKey(pk) => Some(DefiniteKeyToken::XOnlyPublicKey(*pk)),
KeyTokenInner::ExtendedKey(_) => None,
}
}
pub fn derive(&self, index: u32) -> Result<Self, String> {
match &self.inner {
KeyTokenInner::ExtendedKey(ext) => {
let derived = ext.derive(index)?;
Ok(KeyToken {
inner: KeyTokenInner::from_definite_key(derived),
})
}
_ => Ok(self.clone()), }
}
pub fn from_definite_key(key: DefiniteKeyToken) -> Self {
Self {
inner: match key {
DefiniteKeyToken::PublicKey(pk) => KeyTokenInner::PublicKey(pk),
DefiniteKeyToken::XOnlyPublicKey(pk) => KeyTokenInner::XOnlyPublicKey(pk),
},
}
}
}
impl KeyTokenInner {
fn from_definite_key(key: DefiniteKeyToken) -> Self {
match key {
DefiniteKeyToken::PublicKey(pk) => Self::PublicKey(pk),
DefiniteKeyToken::XOnlyPublicKey(pk) => Self::XOnlyPublicKey(pk),
}
}
}
#[derive(Clone, Copy)]
pub enum DefiniteKeyToken {
PublicKey(bitcoin::PublicKey),
XOnlyPublicKey(bitcoin::XOnlyPublicKey),
}
impl DefiniteKeyToken {
pub fn to_bytes(&self) -> Vec<u8> {
match self {
DefiniteKeyToken::PublicKey(pk) => pk.to_bytes().to_vec(),
DefiniteKeyToken::XOnlyPublicKey(pk) => pk.serialize().to_vec(),
}
}
pub fn push_to_script(&self, builder: Builder) -> Builder {
match self {
DefiniteKeyToken::PublicKey(pk) => builder.push_key(pk),
DefiniteKeyToken::XOnlyPublicKey(pk) => builder.push_x_only_key(pk),
}
}
pub fn pubkey_hash(&self) -> PubkeyHash {
match self {
DefiniteKeyToken::PublicKey(pk) => pk.pubkey_hash(),
DefiniteKeyToken::XOnlyPublicKey(pk) => PubkeyHash::hash(&pk.serialize()),
}
}
}
#[cfg(feature = "debug")]
impl core::fmt::Debug for KeyToken {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.identifier())
}
}
#[cfg(feature = "debug")]
impl core::fmt::Debug for DefiniteKeyToken {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DefiniteKeyToken::PublicKey(pk) => write!(f, "PublicKey({})", pk),
DefiniteKeyToken::XOnlyPublicKey(pk) => write!(f, "XOnlyPublicKey({})", pk),
}
}
}
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Wildcard {
None,
Normal,
}
impl core::fmt::Display for Wildcard {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Wildcard::None => write!(f, ""),
Wildcard::Normal => write!(f, "/*"),
}
}
}
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
struct ExtendedKey {
pub raw: String,
pub origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
pub key: bip32::Xpub,
pub path: bip32::DerivationPath,
pub wildcard: Wildcard,
pub x_only: bool,
}
impl ExtendedKey {
#[inline]
pub fn identifier(&self) -> String {
self.raw.clone()
}
pub fn derive(&self, index: u32) -> Result<DefiniteKeyToken, String> {
let secp = secp256k1::Secp256k1::new();
let mut path = self.path.clone();
if let Wildcard::Normal = self.wildcard {
path = path.child(
bip32::ChildNumber::from_normal_idx(index)
.map_err(|e| alloc::format!("{:?}", e))?,
);
}
let pubkey = self
.key
.derive_pub(&secp, &path)
.map_err(|e| alloc::format!("{:?}", e))?;
if self.x_only {
Ok(DefiniteKeyToken::XOnlyPublicKey(
bitcoin::XOnlyPublicKey::from(pubkey.public_key),
))
} else {
Ok(DefiniteKeyToken::PublicKey(bitcoin::PublicKey::from(
pubkey.public_key,
)))
}
}
}
impl core::fmt::Display for ExtendedKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use alloc::string::ToString;
if let Some((fingerprint, path)) = &self.origin {
write!(
f,
"[{fingerprint}{}{}]",
if path.is_empty() { "" } else { "/" },
&path.to_string()
)?;
}
write!(f, "{}", self.key)?;
write!(
f,
"{}{}",
if self.path.is_empty() { "" } else { "/" },
&self.path.to_string()
)?;
write!(f, "{}", self.wildcard)?;
Ok(())
}
}
pub fn parse_key<'a>(
token: (&'a str, Position),
descriptor: &Descriptor,
) -> Result<KeyToken, ParseError<'a>> {
if token.0.contains("pub") {
let mut origin_fingerprint = None;
let mut origin_path = None;
let mut remaining = token.0;
remaining = if token.0.starts_with('[') {
let parts: Vec<&str> = token.0.splitn(2, ']').collect();
if parts.len() != 2 {
return Err(ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid format: missing closing square bracket",
});
}
let origin_part = &parts[0][1..]; if origin_part.len() < 9 {
return Err(ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid origin format",
});
}
let fingerprint_part = &origin_part[..8];
origin_fingerprint =
Some(bip32::Fingerprint::from_str(fingerprint_part).map_err(|_| {
ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid origin fingerprint",
}
})?);
let remaining = &origin_part[8..];
if !remaining.is_empty() {
let origin_path_str = alloc::format!("m{}", &remaining);
origin_path = Some(bip32::DerivationPath::from_str(&origin_path_str).map_err(
|_| ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid origin path",
},
)?);
}
parts[1]
} else {
token.0
};
let mut wildcard = Wildcard::None;
let x_only = *descriptor == Descriptor::Tr;
let parts = remaining.splitn(2, '/').collect::<Vec<&str>>();
let key_part = parts[0];
let suffix = parts.get(1);
let path_str = suffix
.map(|suffix| {
let mut path_str = alloc::format!("m/{}", suffix);
if path_str.ends_with("/*") {
wildcard = Wildcard::Normal;
path_str = path_str[..path_str.len() - 2].into();
} else if path_str.ends_with("/*'") {
return Err(ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid format: hardened wildcard not allowed",
});
}
Ok(path_str)
})
.transpose()?;
let key = bip32::Xpub::from_str(key_part).map_err(|_| ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid xpub",
})?;
let path = match path_str {
Some(path_str) => {
bip32::DerivationPath::from_str(&path_str).map_err(|_| ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid path",
})?
}
None => Default::default(),
};
let key = ExtendedKey {
raw: token.0.into(),
origin: match (origin_fingerprint, origin_path) {
(Some(fingerprint), Some(path)) => Some((fingerprint, path)),
(Some(fingerprint), None) => Some((fingerprint, Default::default())),
_ => None,
},
key,
path,
wildcard,
x_only,
};
return Ok(KeyToken {
inner: KeyTokenInner::ExtendedKey(key),
});
}
let key = match descriptor {
Descriptor::Tr => {
if token.0.len() != 66 && token.0.len() != 130 {
return Err(ParseError::InvalidXOnlyKeyLength {
key: token.0,
position: token.1,
found: token.0.len(),
});
}
let pub_key =
bitcoin::PublicKey::from_str(token.0).map_err(|_| ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid bitcoin::PublicKey key",
})?;
KeyTokenInner::XOnlyPublicKey(pub_key.into())
}
_ => {
let pub_key =
bitcoin::PublicKey::from_str(token.0).map_err(|_| ParseError::InvalidKey {
key: token.0,
position: token.1,
inner: "Invalid bitcoin::PublicKey key",
})?;
KeyTokenInner::PublicKey(pub_key)
}
};
Ok(KeyToken { inner: key })
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_key() {
let key = "[aabbccdd/10'/123]tpubDAenfwNu5GyCJWv8oqRAckdKMSUoZjgVF5p8WvQwHQeXjDhAHmGrPa4a4y2Fn7HF2nfCLefJanHV3ny1UY25MRVogizB2zRUdAo7Tr9XAjm/10/*";
let key = parse_key((key, 0), &Descriptor::Wpkh).unwrap();
dbg!(&key);
let derived = key.derive(22).unwrap();
dbg!(&derived);
}
#[test]
fn test_parse_xonly_key() {
let key = "020202020202020212131610202020202121316121618171818121715181919190";
let key = parse_key((key, 0), &Descriptor::Tr).unwrap();
dbg!(&key);
}
}