use async_trait::async_trait;
use bsv::primitives::public_key::PublicKey;
use bsv::wallet::cached_key_deriver::CachedKeyDeriver;
use crate::error::{WalletError, WalletResult};
use crate::signer::signing_provider::SigningProvider;
use crate::utility::script_template_brc29::ScriptTemplateBRC29;
pub struct StandardSigningProvider {
key_deriver: CachedKeyDeriver,
identity_pub_key: PublicKey,
}
impl StandardSigningProvider {
pub fn new(key_deriver: CachedKeyDeriver, identity_pub_key: PublicKey) -> Self {
Self {
key_deriver,
identity_pub_key,
}
}
pub fn key_deriver(&self) -> &CachedKeyDeriver {
&self.key_deriver
}
}
#[async_trait]
impl SigningProvider for StandardSigningProvider {
async fn derive_change_locking_script(
&self,
derivation_prefix: &str,
derivation_suffix: &str,
) -> WalletResult<Vec<u8>> {
let template =
ScriptTemplateBRC29::new(derivation_prefix.to_string(), derivation_suffix.to_string());
template.lock(self.key_deriver.root_key(), &self.identity_pub_key)
}
async fn sign_input(
&self,
sighash: &[u8; 32],
sighash_type: u32,
derivation_prefix: &str,
derivation_suffix: &str,
unlocker_pub_key: &PublicKey,
) -> WalletResult<Vec<u8>> {
let template =
ScriptTemplateBRC29::new(derivation_prefix.to_string(), derivation_suffix.to_string());
let p2pkh = template.unlock(self.key_deriver.root_key(), unlocker_pub_key)?;
let priv_key = p2pkh.private_key.as_ref().ok_or_else(|| {
WalletError::Internal("P2PKH template has no private key".to_string())
})?;
let sig_obj = priv_key
.sign(sighash, true)
.map_err(|e| WalletError::Internal(format!("ECDSA sign failed: {e}")))?;
let der = sig_obj.to_der();
let pub_key = priv_key.to_public_key();
let pubkey_bytes = pub_key.to_der();
let hashtype_byte = (sighash_type & 0xFF) as u8;
let sig_with_ht_len = der.len() + 1;
let mut script = Vec::with_capacity(1 + sig_with_ht_len + 1 + 33);
script.push(sig_with_ht_len as u8);
script.extend_from_slice(&der);
script.push(hashtype_byte);
script.push(33);
script.extend_from_slice(&pubkey_bytes);
Ok(script)
}
fn identity_public_key(&self) -> &PublicKey {
&self.identity_pub_key
}
}