use std::sync::Arc;
use simplicity::bitcoin::secp256k1;
use simplicity::jet::Jet;
use simplicity::{BitIter, CommitNode, DecodeError, ParseError, RedeemNode};
pub struct Program<J: Jet> {
commit_prog: Arc<CommitNode<J>>,
redeem_prog: Option<Arc<RedeemNode<J>>>,
}
impl<J: Jet> Program<J> {
pub fn from_str(prog_b64: &str, wit_hex: Option<&str>) -> Result<Self, ParseError> {
Ok(Self {
commit_prog: CommitNode::from_str(prog_b64)?,
redeem_prog: wit_hex.map(|hex| RedeemNode::from_str(prog_b64, hex)).transpose()?,
})
}
pub fn from_bytes(prog_bytes: &[u8], wit_bytes: Option<&[u8]>) -> Result<Self, DecodeError> {
let prog_iter = BitIter::from(prog_bytes);
let wit_iter = wit_bytes.map(BitIter::from);
Ok(Self {
commit_prog: CommitNode::decode(prog_iter.clone())?,
redeem_prog: wit_iter.map(|iter| RedeemNode::decode(prog_iter, iter)).transpose()?,
})
}
pub fn cmr(&self) -> simplicity::Cmr {
self.commit_prog.cmr()
}
pub fn amr(&self) -> Option<simplicity::Amr> {
self.redeem_prog.as_ref().map(Arc::as_ref).map(RedeemNode::amr)
}
pub fn ihr(&self) -> Option<simplicity::Ihr> {
self.redeem_prog.as_ref().map(Arc::as_ref).map(RedeemNode::ihr)
}
pub fn commit_prog(&self) -> &CommitNode<J> {
&self.commit_prog
}
pub fn redeem_node(&self) -> Option<&RedeemNode<J>> {
self.redeem_prog.as_ref().map(Arc::as_ref)
}
}
fn unspendable_internal_key() -> secp256k1::XOnlyPublicKey {
secp256k1::XOnlyPublicKey::from_slice(&[
0xf5, 0x91, 0x9f, 0xa6, 0x4c, 0xe4, 0x5f, 0x83, 0x06, 0x84, 0x90, 0x72, 0xb2, 0x6c, 0x1b,
0xfd, 0xd2, 0x93, 0x7e, 0x6b, 0x81, 0x77, 0x47, 0x96, 0xff, 0x37, 0x2b, 0xd1, 0xeb, 0x53,
0x62, 0xd2,
])
.expect("key should be valid")
}
fn script_ver(cmr: simplicity::Cmr) -> (elements::Script, elements::taproot::LeafVersion) {
let script = elements::script::Script::from(cmr.as_ref().to_vec());
(script, simplicity::leaf_version())
}
fn taproot_spend_info(cmr: simplicity::Cmr) -> elements::taproot::TaprootSpendInfo {
let builder = elements::taproot::TaprootBuilder::new();
let (script, version) = script_ver(cmr);
let builder = builder.add_leaf_with_ver(0, script, version).expect("tap tree should be valid");
builder
.finalize(secp256k1::SECP256K1, unspendable_internal_key())
.expect("tap tree should be valid")
}
pub fn elements_address(
cmr: simplicity::Cmr,
params: &'static elements::AddressParams,
) -> elements::Address {
let info = taproot_spend_info(cmr);
let blinder = None;
elements::Address::p2tr(
secp256k1::SECP256K1,
info.internal_key(),
info.merkle_root(),
blinder,
params,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fixed_hex_vector_1() {
let b64 = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA";
let prog = Program::<simplicity::jet::Core>::from_str(b64, Some("")).unwrap();
assert_eq!(
prog.cmr(),
"abdd773fc7a503908739b4a63198416fdd470948830cb5a6516b98fe0a3bfa85".parse().unwrap()
);
assert_eq!(
prog.amr(),
Some(
"1362ee53ae75218ed51dc4bd46cdbfa585f934ac6c6c3ff787e27dce91ccd80b".parse().unwrap()
)
);
assert_eq!(
prog.ihr(),
Some(
"251c6778129e0f12da3f2388ab30184e815e9d9456b5931e54802a6715d9ca42".parse().unwrap()
),
);
let b64 = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA";
let prog = Program::<simplicity::jet::Core>::from_str(b64, None).unwrap();
assert_eq!(
prog.cmr(),
"abdd773fc7a503908739b4a63198416fdd470948830cb5a6516b98fe0a3bfa85".parse().unwrap()
);
assert_eq!(prog.amr(), None);
assert_eq!(prog.ihr(), None);
}
}