use std::str::FromStr;
use bitcoin::hashes::sha256::Hash as Sha256Hash;
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::schnorr::Signature;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::nut00::Witness;
use super::nut10::Secret;
use super::nut11::valid_signatures;
use super::{Conditions, Proof};
use crate::util::unix_time;
pub mod serde_htlc_witness;
#[derive(Debug, Error)]
pub enum Error {
#[error("Secret is not a HTLC secret")]
IncorrectSecretKind,
#[error("Locktime in past")]
LocktimeInPast,
#[error("Hash required")]
HashRequired,
#[error("Hash is not valid")]
InvalidHash,
#[error("Preimage does not match")]
Preimage,
#[error("Witness did not provide signatures")]
SignaturesNotProvided,
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
#[error(transparent)]
NUT11(#[from] super::nut11::Error),
#[error(transparent)]
Serde(#[from] serde_json::Error),
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
pub struct HTLCWitness {
pub preimage: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub signatures: Option<Vec<String>>,
}
impl Proof {
pub fn verify_htlc(&self) -> Result<(), Error> {
let secret: Secret = self.secret.clone().try_into()?;
let conditions: Option<Conditions> =
secret.secret_data.tags.and_then(|c| c.try_into().ok());
let htlc_witness = match &self.witness {
Some(Witness::HTLCWitness(witness)) => witness,
_ => return Err(Error::IncorrectSecretKind),
};
if let Some(conditions) = conditions {
if let Some(locktime) = conditions.locktime {
if locktime.lt(&unix_time()) && conditions.refund_keys.is_none() {
return Ok(());
}
if let (Some(refund_key), Some(signatures)) =
(conditions.refund_keys, &self.witness)
{
let signatures = signatures
.signatures()
.ok_or(Error::SignaturesNotProvided)?
.iter()
.map(|s| Signature::from_str(s))
.collect::<Result<Vec<Signature>, _>>()?;
if valid_signatures(self.secret.as_bytes(), &refund_key, &signatures).ge(&1) {
return Ok(());
}
}
}
if let Some(pubkey) = conditions.pubkeys {
let req_sigs = conditions.num_sigs.unwrap_or(1);
let signatures = htlc_witness
.signatures
.as_ref()
.ok_or(Error::SignaturesNotProvided)?;
let signatures = signatures
.iter()
.map(|s| Signature::from_str(s))
.collect::<Result<Vec<Signature>, _>>()?;
if valid_signatures(self.secret.as_bytes(), &pubkey, &signatures).lt(&req_sigs) {
return Err(Error::IncorrectSecretKind);
}
}
}
if secret.kind.ne(&super::Kind::HTLC) {
return Err(Error::IncorrectSecretKind);
}
let hash_lock =
Sha256Hash::from_str(&secret.secret_data.data).map_err(|_| Error::InvalidHash)?;
let preimage_hash = Sha256Hash::hash(htlc_witness.preimage.as_bytes());
if hash_lock.ne(&preimage_hash) {
return Err(Error::Preimage);
}
Ok(())
}
#[inline]
pub fn add_preimage(&mut self, preimage: String) {
self.witness = Some(Witness::HTLCWitness(HTLCWitness {
preimage,
signatures: None,
}))
}
}