use std::sync::Arc;
use elements::taproot::{TaprootBuilder, TaprootSpendInfo};
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> {
let prog_bytes = crate::hex_or_base64(prog_b64).map_err(ParseError::Base64)?;
let iter = BitIter::new(prog_bytes.iter().copied());
let commit_prog = CommitNode::decode(iter).map_err(ParseError::Decode)?;
let redeem_prog = wit_hex
.map(|wit_hex| {
let wit_bytes = crate::hex_or_base64(wit_hex).map_err(ParseError::Base64)?;
let prog_iter = BitIter::new(prog_bytes.into_iter());
let wit_iter = BitIter::new(wit_bytes.into_iter());
RedeemNode::decode(prog_iter, wit_iter).map_err(ParseError::Decode)
})
.transpose()?;
Ok(Self {
commit_prog,
redeem_prog,
})
}
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<&Arc<RedeemNode<J>>> {
self.redeem_prog.as_ref()
}
}
#[rustfmt::skip] pub fn unspendable_internal_key() -> secp256k1::XOnlyPublicKey {
secp256k1::XOnlyPublicKey::from_slice(&[
0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e,
0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0,
])
.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())
}
pub fn taproot_spend_info(
internal_key: secp256k1::XOnlyPublicKey,
state: Option<[u8; 32]>,
cmr: simplicity::Cmr,
) -> TaprootSpendInfo {
let builder = TaprootBuilder::new();
let (script, version) = script_ver(cmr);
let builder = if let Some(state) = state {
use elements::hashes::{sha256, Hash as _, HashEngine as _};
let tag = sha256::Hash::hash(b"TapData");
let mut eng = sha256::Hash::engine();
eng.input(tag.as_byte_array());
eng.input(tag.as_byte_array());
eng.input(&state);
let state_hash = sha256::Hash::from_engine(eng);
builder
.add_leaf_with_ver(1, script, version)
.expect("tap tree should be valid")
.add_hidden(1, state_hash)
.expect("tap tree should be valid")
} else {
builder.add_leaf_with_ver(0, script, version).expect("tap tree should be valid")
};
builder.finalize(secp256k1::SECP256K1, internal_key).expect("tap tree should be valid")
}
pub fn elements_address(
cmr: simplicity::Cmr,
state: Option<[u8; 32]>,
params: &'static elements::AddressParams,
) -> elements::Address {
let info = taproot_spend_info(unspendable_internal_key(), state, 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);
}
}