use crate::{ConclaveError, ConclaveResult};
use bitcoin::XOnlyPublicKey;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
pub struct CovenantManager;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CovenantScript {
pub script_hex: String,
pub internal_key: String,
}
impl CovenantManager {
pub fn generate_cat_vault_script(
internal_key: &XOnlyPublicKey,
template_hash: [u8; 32],
) -> ConclaveResult<CovenantScript> {
let mut script = Vec::new();
script.push(0x20); script.extend_from_slice(&template_hash);
script.push(0x7e);
script.push(0x20); script.extend_from_slice(&internal_key.serialize().0);
script.push(0xac);
Ok(CovenantScript {
script_hex: hex::encode(script),
internal_key: internal_key.to_string(),
})
}
pub fn verify_recursive_invariant(
&self,
script_witness: &[Vec<u8>],
expected_template_hash: [u8; 32],
) -> ConclaveResult<bool> {
if script_witness.len() < 2 {
return Err(ConclaveError::InvalidPayload);
}
let mut hasher = Sha256::new();
hasher.update(&script_witness[0]);
hasher.update(&script_witness[1]);
let result_hash = hasher.finalize();
if result_hash.as_slice() != expected_template_hash {
return Ok(false);
}
Ok(true)
}
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin::XOnlyPublicKey;
#[test]
fn test_generate_cat_vault_script() {
let pubkey = XOnlyPublicKey::from_byte_array(&[1u8; 32]).unwrap();
let hash = [2u8; 32];
let res = CovenantManager::generate_cat_vault_script(&pubkey, hash).unwrap();
assert!(res.script_hex.contains("7e")); assert!(res.script_hex.contains("ac")); }
#[test]
fn test_verify_recursive_invariant_harden() {
let mgr = CovenantManager;
let part1 = b"template_prefix".to_vec();
let part2 = b"template_suffix".to_vec();
let mut hasher = Sha256::new();
hasher.update(&part1);
hasher.update(&part2);
let expected_hash: [u8; 32] = hasher.finalize().into();
let witness = vec![part1.clone(), part2.clone()];
assert!(mgr
.verify_recursive_invariant(&witness, expected_hash)
.unwrap());
let wrong_hash = [0u8; 32];
assert!(!mgr
.verify_recursive_invariant(&witness, wrong_hash)
.unwrap());
assert!(mgr.verify_recursive_invariant(&[], expected_hash).is_err());
}
}