firma 0.20.0

Firma is a library to create bitcoin multisig wallets with private keys stored on offline devices.
Documentation
// https://dreampuf.github.io/GraphvizOnline/#digraph%20G%20%7B%0A%20%20%22.firma%22%20-%3E%20%22%5Bnetwork%5D%22%0A%20%20%0A%20%20%22%5Bnetwork%5D%22%20-%3E%20wallets%0A%20%20%22%5Bnetwork%5D%22%20-%3E%20keys%0A%20%20%22%5Bnetwork%5D%22%20-%3E%20psbts%0A%20%20%22%5Bnetwork%5D%22%20-%3E%20%22daemon_opts%22%20%0A%20%20%0A%20%20keys%20-%3E%20%22%5Bkey%20name%5D%22%0A%20%20%22master_secret%22%20%5Bshape%3DSquare%5D%0A%20%20%22descriptor_public_key%22%20%5Bshape%3DSquare%5D%0A%20%20%22%5Bkey%20name%5D%22%20-%3E%20%22master_secret%22%20%0A%20%20%22%5Bkey%20name%5D%22%20-%3E%20%22descriptor_public_key%22%20%0A%20%20%0A%20%20wallets%20-%3E%20%22%5Bwallet%20name%5D%22%0A%20%20%22wallet%22%20%5Bshape%3DSquare%5D%0A%20%20%22wallet_indexes%22%20%5Bshape%3DSquare%5D%0A%20%20%22daemon_opts%22%20%5Bshape%3DSquare%5D%0A%20%20%22wallet_signature%22%20%5Bshape%3DSquare%5D%0A%20%20%22%5Bwallet%20name%5D%22%20-%3E%20%22wallet%22%20%0A%20%20%22%5Bwallet%20name%5D%22%20-%3E%20%22wallet_indexes%22%20%0A%20%20%22%5Bwallet%20name%5D%22%20-%3E%20%22wallet_signature%22%20%0A%20%20%0A%20%20psbts%20-%3E%20%22%5Bpsbt%20name%5D%22%0A%20%20%22psbt%22%20%5Bshape%3DSquare%5D%0A%20%20%22%5Bpsbt%20name%5D%22%20-%3E%20%22psbt%22%20%0A%7D

use crate::mnemonic::Mnemonic;
use crate::offline::descriptor::{parse_descriptor_with_checksum, ExtendedDescriptor};
use crate::offline::sign::get_psbt_name;
use crate::offline::sign_wallet::WALLET_SIGN_DERIVATION;
use crate::{
    check_compatibility, psbt_from_base64, psbt_to_base64, BitcoinPsbt, Error, Identifier, Kind,
    Result,
};
use bitcoin::secp256k1::{Secp256k1, Signing};
use bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
use bitcoin::{secp256k1, Network};
use log::debug;
use miniscript::descriptor::{DescriptorXKey, Wildcard};
use miniscript::{Descriptor, ForEachKey};
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Wallet {
    pub id: Identifier,
    pub descriptor: String,
    pub created_at_height: u64,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct WalletIndexes {
    pub id: Identifier,
    pub main: u32,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct WalletSignature {
    pub id: Identifier,
    pub signature: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct MasterSecret {
    pub id: Identifier,
    pub key: ExtendedPrivKey,
    pub network: Network, // ExtendedPrivKey is not enough since regtest is the same as testnet
    pub fingerprint: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mnemonic: Option<Mnemonic>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dice: Option<Dice>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct DescriptorPublicKey {
    //TODO make it DescriptorPublicKeyJson
    pub id: Identifier,
    /// ToString of [miniscript::descriptor::DescriptorPublicKey]
    /// Example: `[28645006/48'/1'/0'/2']tpubDEwqCvJxKwKWX9xvRe48uofWJn1Y89Jn8UeH1Efrjb1UEVjUDy3URYTiqWaVCW7WdvHrL8XrSihHEhTwv5H3VDJoakjuCHiAnr6xcF2Xm4s/0/*`
    /// TODO use DescriptorPublicKey when implement Serialize
    pub desc_pub_key: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Psbt {
    pub id: Identifier,
    /// PSBT serialized with base64
    pub psbt: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Dice {
    pub launches: String,
    pub faces: u32,
    pub value: String,
}

impl Wallet {
    pub fn extract_desc_pub_keys(&self) -> Result<Vec<miniscript::DescriptorPublicKey>> {
        let mut desc_pub_keys = vec![];
        let end = self
            .descriptor
            .find('#')
            .unwrap_or_else(|| self.descriptor.len());
        let descriptor: miniscript::Descriptor<miniscript::DescriptorPublicKey> =
            self.descriptor[..end].parse().unwrap();
        if let Descriptor::Wsh(miniscript) = descriptor {
            miniscript.for_each_key(|k| {
                desc_pub_keys.push(k.as_key().clone());
                true
            });
        }
        Ok(desc_pub_keys)
    }
    pub fn extract_wallet_sign_keys(&self) -> Result<Vec<bitcoin::PublicKey>> {
        let secp = Secp256k1::verification_only();
        let mut keys = vec![];
        for k in self.extract_desc_pub_keys()? {
            debug!("extract_wallet_sign_keys descriptor_pub_key:{} ", k);
            let key = k.derive(WALLET_SIGN_DERIVATION).derive_public_key(&secp)?;
            debug!(
                "extract_wallet_sign_keys public_key:{} WALLET_SIGN_DERIVATION:{}",
                key, WALLET_SIGN_DERIVATION
            );
            keys.push(key);
        }
        Ok(keys)
    }
    pub fn fingerprints(&self) -> Vec<Fingerprint> {
        let mut result = vec![];
        if let Ok(v) = self.extract_desc_pub_keys() {
            for k in v {
                if let miniscript::DescriptorPublicKey::XPub(x) = k {
                    if let Some(f) = x.origin {
                        result.push(f.0);
                    }
                }
            }
        }
        result
    }
    pub fn descriptor(&self) -> Result<ExtendedDescriptor> {
        parse_descriptor_with_checksum(&self.descriptor)
    }
}

impl DescriptorPublicKey {
    pub fn key(&self) -> Result<miniscript::DescriptorPublicKey> {
        Ok(self.desc_pub_key.parse()?)
    }
    fn xkey(&self) -> Result<DescriptorXKey<ExtendedPubKey>> {
        if let Ok(miniscript::DescriptorPublicKey::XPub(x)) = self.key() {
            return Ok(x);
        }
        Err(Error::WrongKeyType)
    }
    pub fn origin_path(&self) -> Result<DerivationPath> {
        Ok(self.xkey()?.origin.ok_or(Error::WrongKeyType)?.1)
    }
    pub fn xpub(&self) -> Result<ExtendedPubKey> {
        Ok(self.xkey()?.xkey)
    }
}

impl MasterSecret {
    pub fn from_xprv(network: Network, key: ExtendedPrivKey, name: &str) -> Result<Self> {
        check_compatibility(network, key.network)?;
        let fingerprint = key.fingerprint(&Secp256k1::signing_only()).to_string();

        Ok(MasterSecret {
            key,
            network,
            fingerprint,
            mnemonic: None,
            dice: None,
            id: Identifier::new(network, Kind::MasterSecret, name),
        })
    }

    pub fn new(network: Network, mnemonic: Mnemonic, name: &str) -> Result<Self> {
        let seed = mnemonic.to_seed(None);
        let key = ExtendedPrivKey::new_master(network, &seed.0)?;
        let fingerprint = key.fingerprint(&Secp256k1::signing_only()).to_string();

        Ok(MasterSecret {
            key,
            network,
            fingerprint,
            mnemonic: Some(mnemonic),
            dice: None,
            id: Identifier::new(network, Kind::MasterSecret, name),
        })
    }

    fn path(&self) -> DerivationPath {
        //TODO copay multisig derivation, not sure
        let n = match self.network {
            Network::Bitcoin => "0",
            Network::Testnet => "1",
            Network::Regtest => "2",
            Network::Signet => "3",
        };
        // m / 48' / coin_type' / account' / script_type' / change / address_index
        DerivationPath::from_str(&format!("m/48'/{}'/0'/2'", n)).unwrap()
    }

    pub fn as_desc_prv_key<T: Signing>(&self, secp: &Secp256k1<T>) -> Result<ExtendedPrivKey> {
        debug!("Using path: {}", self.path());
        Ok(self.key.derive_priv(secp, &self.path())?)
    }

    pub fn as_wallet_sign_prv_key<T: Signing>(
        &self,
        secp: &Secp256k1<T>,
    ) -> Result<ExtendedPrivKey> {
        let k = self.as_desc_prv_key(secp)?;
        Ok(k.derive_priv(
            secp,
            &DerivationPath::from_str(&format!("m/0/{}", WALLET_SIGN_DERIVATION))?,
        )?)
    }

    pub fn as_wallet_sign_pub_key<T: Signing>(
        &self,
        secp: &Secp256k1<T>,
    ) -> Result<bitcoin::PublicKey> {
        let k = self.as_wallet_sign_prv_key(secp)?;
        let xpub = ExtendedPubKey::from_private(secp, &k);
        Ok(xpub.public_key)
    }

    /// returns the public part of the key, it is an expensive method cause it's initializing a
    /// secp context
    pub fn as_desc_pub_key(&self) -> Result<DescriptorPublicKey> {
        let secp = Secp256k1::signing_only();
        let xprv_derived = self.as_desc_prv_key(&secp)?;
        let xpub = ExtendedPubKey::from_private(&secp, &xprv_derived);
        let desc_pub_key = miniscript::DescriptorPublicKey::XPub(DescriptorXKey {
            origin: Some((self.key.fingerprint(&secp), self.path())),
            xkey: xpub,
            derivation_path: DerivationPath::from_str("m/0")?,
            wildcard: Wildcard::Unhardened,
        });
        let id = self.id.with_kind(Kind::DescriptorPublicKey);
        Ok(DescriptorPublicKey {
            id,
            desc_pub_key: desc_pub_key.to_string(),
        })
    }

    pub fn fingerprint<S: secp256k1::Signing>(&self, secp: &Secp256k1<S>) -> Fingerprint {
        self.key.fingerprint(secp)
    }
}

impl Psbt {
    pub fn psbt(&self) -> Result<BitcoinPsbt> {
        Ok(psbt_from_base64(&self.psbt)?.1)
    }

    pub fn set_psbt(&mut self, psbt: &BitcoinPsbt) {
        self.psbt = psbt_to_base64(psbt).1;
    }
}

impl From<(&BitcoinPsbt, Network)> for Psbt {
    fn from(psbt_and_network: (&BitcoinPsbt, Network)) -> Self {
        let (psbt, network) = psbt_and_network;
        let (_, base64) = psbt_to_base64(psbt);
        let name = get_psbt_name(psbt).expect("PSBT without name"); //TODO
        Psbt {
            psbt: base64,
            id: Identifier::new(network, Kind::Psbt, &name),
        }
    }
}