hal_simplicity/
hal_simplicity.rs1use std::sync::Arc;
5
6use elements::taproot::{TaprootBuilder, TaprootSpendInfo};
7use simplicity::bitcoin::secp256k1;
8use simplicity::jet::Jet;
9use simplicity::{BitIter, CommitNode, DecodeError, ParseError, RedeemNode};
10
11pub struct Program<J: Jet> {
14 commit_prog: Arc<CommitNode<J>>,
23 redeem_prog: Option<Arc<RedeemNode<J>>>,
26}
27
28impl<J: Jet> Program<J> {
29 pub fn from_str(prog_b64: &str, wit_hex: Option<&str>) -> Result<Self, ParseError> {
38 let prog_bytes = crate::hex_or_base64(prog_b64).map_err(ParseError::Base64)?;
39 let iter = BitIter::new(prog_bytes.iter().copied());
40 let commit_prog = CommitNode::decode(iter).map_err(ParseError::Decode)?;
41
42 let redeem_prog = wit_hex
43 .map(|wit_hex| {
44 let wit_bytes = crate::hex_or_base64(wit_hex).map_err(ParseError::Base64)?;
45 let prog_iter = BitIter::new(prog_bytes.into_iter());
46 let wit_iter = BitIter::new(wit_bytes.into_iter());
47 RedeemNode::decode(prog_iter, wit_iter).map_err(ParseError::Decode)
48 })
49 .transpose()?;
50
51 Ok(Self {
52 commit_prog,
53 redeem_prog,
54 })
55 }
56
57 pub fn from_bytes(prog_bytes: &[u8], wit_bytes: Option<&[u8]>) -> Result<Self, DecodeError> {
59 let prog_iter = BitIter::from(prog_bytes);
60 let wit_iter = wit_bytes.map(BitIter::from);
61 Ok(Self {
62 commit_prog: CommitNode::decode(prog_iter.clone())?,
63 redeem_prog: wit_iter.map(|iter| RedeemNode::decode(prog_iter, iter)).transpose()?,
64 })
65 }
66
67 pub fn cmr(&self) -> simplicity::Cmr {
69 self.commit_prog.cmr()
70 }
71
72 pub fn amr(&self) -> Option<simplicity::Amr> {
74 self.redeem_prog.as_ref().map(Arc::as_ref).map(RedeemNode::amr)
75 }
76
77 pub fn ihr(&self) -> Option<simplicity::Ihr> {
79 self.redeem_prog.as_ref().map(Arc::as_ref).map(RedeemNode::ihr)
80 }
81
82 pub fn commit_prog(&self) -> &CommitNode<J> {
84 &self.commit_prog
85 }
86
87 pub fn redeem_node(&self) -> Option<&Arc<RedeemNode<J>>> {
89 self.redeem_prog.as_ref()
90 }
91}
92
93#[rustfmt::skip] pub fn unspendable_internal_key() -> secp256k1::XOnlyPublicKey {
99 secp256k1::XOnlyPublicKey::from_slice(&[
100 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e,
101 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0,
102 ])
103 .expect("key should be valid")
104}
105
106fn script_ver(cmr: simplicity::Cmr) -> (elements::Script, elements::taproot::LeafVersion) {
107 let script = elements::script::Script::from(cmr.as_ref().to_vec());
108 (script, simplicity::leaf_version())
109}
110
111pub fn taproot_spend_info(
114 internal_key: secp256k1::XOnlyPublicKey,
115 state: Option<[u8; 32]>,
116 cmr: simplicity::Cmr,
117) -> TaprootSpendInfo {
118 let builder = TaprootBuilder::new();
119 let (script, version) = script_ver(cmr);
120 let builder = if let Some(state) = state {
121 use elements::hashes::{sha256, Hash as _, HashEngine as _};
122 let tag = sha256::Hash::hash(b"TapData");
123 let mut eng = sha256::Hash::engine();
124 eng.input(tag.as_byte_array());
125 eng.input(tag.as_byte_array());
126 eng.input(&state);
127 let state_hash = sha256::Hash::from_engine(eng);
128
129 builder
130 .add_leaf_with_ver(1, script, version)
131 .expect("tap tree should be valid")
132 .add_hidden(1, state_hash)
133 .expect("tap tree should be valid")
134 } else {
135 builder.add_leaf_with_ver(0, script, version).expect("tap tree should be valid")
136 };
137
138 builder.finalize(secp256k1::SECP256K1, internal_key).expect("tap tree should be valid")
139}
140
141pub fn elements_address(
145 cmr: simplicity::Cmr,
146 state: Option<[u8; 32]>,
147 params: &'static elements::AddressParams,
148) -> elements::Address {
149 let info = taproot_spend_info(unspendable_internal_key(), state, cmr);
150 let blinder = None;
151 elements::Address::p2tr(
152 secp256k1::SECP256K1,
153 info.internal_key(),
154 info.merkle_root(),
155 blinder,
156 params,
157 )
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn fixed_hex_vector_1() {
166 let b64 = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA";
168 let prog = Program::<simplicity::jet::Core>::from_str(b64, Some("")).unwrap();
169
170 assert_eq!(
171 prog.cmr(),
172 "abdd773fc7a503908739b4a63198416fdd470948830cb5a6516b98fe0a3bfa85".parse().unwrap()
173 );
174 assert_eq!(
175 prog.amr(),
176 Some(
177 "1362ee53ae75218ed51dc4bd46cdbfa585f934ac6c6c3ff787e27dce91ccd80b".parse().unwrap()
178 )
179 );
180 assert_eq!(
181 prog.ihr(),
182 Some(
183 "251c6778129e0f12da3f2388ab30184e815e9d9456b5931e54802a6715d9ca42".parse().unwrap()
184 ),
185 );
186
187 let b64 = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA";
192 let prog = Program::<simplicity::jet::Core>::from_str(b64, None).unwrap();
193
194 assert_eq!(
195 prog.cmr(),
196 "abdd773fc7a503908739b4a63198416fdd470948830cb5a6516b98fe0a3bfa85".parse().unwrap()
197 );
198 assert_eq!(prog.amr(), None);
199 assert_eq!(prog.ihr(), None);
200 }
201}