use std::collections::HashSet;
use amplify::Wrapper;
use bpstd::secp256k1::{ecdsa, schnorr as bip340};
use bpstd::{
Address, InternalKeypair, InternalPk, KeyOrigin, LegacyPk, Sats, Sighash, Sign, TapLeafHash,
TapMerklePath, TapNodeHash, TapSighash, XOnlyPk, Xpriv, XprivAccount,
};
use descriptors::Descriptor;
use psbt::{Psbt, Rejected, Signer};
pub struct SignTxInfo {
pub fee: Sats,
pub inputs: Sats,
pub beneficiaries: HashSet<Address, Sats>,
}
pub struct ConsoleSigner<'descr, 'me, D: Descriptor>
where Self: 'me
{
pub descriptor: &'descr D,
pub account: XprivAccount,
pub signer: XprivSigner<'me>,
}
pub struct XprivSigner<'xpriv> {
account: &'xpriv XprivAccount,
}
impl<'me, D: Descriptor> Signer for ConsoleSigner<'_, 'me, D>
where Self: 'me
{
type Sign<'s>
= &'s XprivSigner<'s>
where Self: 's + 'me;
fn approve(&self, _psbt: &Psbt) -> Result<Self::Sign<'_>, Rejected> { Ok(&self.signer) }
}
impl XprivSigner<'_> {
fn derive_subkey(&self, origin: Option<&KeyOrigin>) -> Option<Xpriv> {
let origin = origin?;
if !self.account.origin().is_subset_of(origin) {
return None;
}
Some(
self.account
.xpriv()
.derive_priv(&origin.as_derivation()[self.account.origin().derivation().len()..]),
)
}
}
impl Sign for &'_ XprivSigner<'_> {
fn sign_ecdsa(
&self,
message: Sighash,
pk: LegacyPk,
origin: Option<&KeyOrigin>,
) -> Option<ecdsa::Signature> {
let sk = self.derive_subkey(origin)?;
if sk.to_compr_pk().to_inner() != pk.pubkey {
return None;
}
Some(sk.to_private_ecdsa().sign_ecdsa(message.into()))
}
fn sign_bip340_key_only(
&self,
message: TapSighash,
pk: InternalPk,
origin: Option<&KeyOrigin>,
merkle_root: Option<TapNodeHash>,
) -> Option<bip340::Signature> {
let xpriv = self.derive_subkey(origin)?;
if xpriv.to_xonly_pk() != pk.to_xonly_pk() {
return None;
}
let output_pair =
InternalKeypair::from(xpriv.to_keypair_bip340()).to_output_keypair(merkle_root).0;
if output_pair.x_only_public_key().0.serialize()
!= pk.to_output_pk(merkle_root).0.to_byte_array()
{
return None;
}
Some(output_pair.sign_schnorr(message.as_ref()))
}
fn sign_bip340_script_path(
&self,
message: TapSighash,
pk: XOnlyPk,
origin: Option<&KeyOrigin>,
) -> Option<bip340::Signature> {
let sk = self.derive_subkey(origin)?;
if sk.to_xonly_pk() != pk {
return None;
}
Some(sk.to_keypair_bip340().sign_schnorr(message.as_ref()))
}
fn should_sign_script_path(
&self,
_index: usize,
_merkle_path: &TapMerklePath,
_leaf: TapLeafHash,
) -> bool {
true
}
fn should_sign_key_path(&self, _index: usize) -> bool { true }
}