elements_miniscript/extensions/
outputs_pref.rs1use std::fmt;
6
7use bitcoin::hashes::{sha256d, Hash};
8use bitcoin::hex::{DisplayHex, FromHex};
9use elements::encode::serialize;
10
11use super::{FromTokenIterError, ParseableExt, TxEnv};
12use crate::descriptor::CovError;
13use crate::miniscript::astelem::StackCtxOperations;
14use crate::miniscript::context::ScriptContextError;
15use crate::miniscript::lex::{Token as Tk, TokenIter};
16use crate::miniscript::limits::{MAX_SCRIPT_ELEMENT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEM_SIZE};
17use crate::miniscript::satisfy::{Satisfaction, Witness};
18use crate::miniscript::types::extra_props::{OpLimits, TimelockInfo};
19use crate::miniscript::types::{Base, Correctness, Dissat, ExtData, Input, Malleability};
20use crate::policy::{self, Liftable};
21use crate::{expression, interpreter, Error, Extension, MiniscriptKey, Satisfier, ToPublicKey};
22
23#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
37pub struct LegacyOutputsPref {
38 pub pref: Vec<u8>,
40}
41
42impl fmt::Display for LegacyOutputsPref {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "outputs_pref({})", self.pref.as_hex())
45 }
46}
47
48impl<Pk: MiniscriptKey> Liftable<Pk> for LegacyOutputsPref {
49 fn lift(&self) -> Result<policy::Semantic<Pk>, Error> {
50 Err(Error::CovError(CovError::CovenantLift))
51 }
52}
53
54impl Extension for LegacyOutputsPref {
55 fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> {
56 if self.pref.len() > MAX_SCRIPT_ELEMENT_SIZE {
57 Err(ScriptContextError::CovElementSizeExceeded)
58 } else {
59 Ok(())
60 }
61 }
62
63 fn corr_prop(&self) -> Correctness {
64 Correctness {
65 base: Base::B,
66 input: Input::Any, dissatisfiable: true, unit: true,
69 }
70 }
71
72 fn mall_prop(&self) -> Malleability {
73 Malleability {
74 dissat: Dissat::Unknown,
75 safe: false,
76 non_malleable: true,
77 }
78 }
79
80 fn extra_prop(&self) -> ExtData {
81 let max_wit_sz = MAX_SCRIPT_ELEMENT_SIZE - self.pref.len();
83 ExtData {
84 pk_cost: 8 + self.pref.len() + 1 + 6, has_free_verify: true,
86 stack_elem_count_sat: Some(7),
87 stack_elem_count_dissat: Some(7),
88 max_sat_size: Some((max_wit_sz, max_wit_sz)),
89 max_dissat_size: Some((0, 0)), timelock_info: TimelockInfo::default(),
91 exec_stack_elem_count_sat: Some(3), exec_stack_elem_count_dissat: Some(3),
93 ops: OpLimits {
94 count: 13,
95 sat: Some(0),
96 nsat: Some(0),
97 },
98 }
99 }
100
101 fn script_size(&self) -> usize {
102 8 + self.pref.len() + 1 + 6 }
107
108 fn from_name_tree(
109 name: &str,
110 children: &[expression::Tree<'_>],
111 ) -> Result<Self, FromTokenIterError> {
112 if children.len() == 1 && name == "outputs_pref" {
113 let pref = expression::terminal(&children[0], Vec::<u8>::from_hex)
114 .map_err(|_| FromTokenIterError)?;
115 Ok(Self { pref })
116 } else {
117 Err(FromTokenIterError)
119 }
120 }
121}
122
123impl ParseableExt for LegacyOutputsPref {
124 fn satisfy<Pk, S>(&self, sat: &S) -> Satisfaction
125 where
126 Pk: ToPublicKey,
127 S: Satisfier<Pk>,
128 {
129 let wit = match sat.lookup_outputs() {
130 Some(outs) => {
131 let mut ser_out = Vec::new();
132 let num_wit_elems =
133 MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE + 1;
134 let mut witness = Vec::with_capacity(num_wit_elems);
135 for out in outs {
136 ser_out.extend(serialize(out));
137 }
138 if ser_out.len() > MAX_SCRIPT_ELEMENT_SIZE {
141 Witness::Impossible
142 } else if ser_out.starts_with(&self.pref) {
143 let mut iter = ser_out.into_iter().skip(self.pref.len()).peekable();
144
145 while iter.peek().is_some() {
146 let chk_size = MAX_STANDARD_P2WSH_STACK_ITEM_SIZE;
147 let chunk: Vec<u8> = iter.by_ref().take(chk_size).collect();
148 witness.push(chunk);
149 }
150 while witness.len() < num_wit_elems {
153 witness.push(vec![]);
154 }
155 Witness::Stack(witness)
156 } else {
157 Witness::Impossible
158 }
159 }
160 None => Witness::Unavailable,
163 };
164 Satisfaction {
165 stack: wit,
166 has_sig: false,
167 }
168 }
169
170 fn dissatisfy<Pk, S>(&self, sat: &S) -> Satisfaction
171 where
172 Pk: ToPublicKey,
173 S: Satisfier<Pk>,
174 {
175 let wit = match sat.lookup_outputs() {
176 Some(outs) => {
177 let mut ser_out = Vec::new();
178 for out in outs {
179 ser_out.extend(serialize(out));
180 }
181 let num_wit_elems = MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE;
182 let mut witness = Vec::with_capacity(num_wit_elems);
183 if self.pref != ser_out.as_slice() {
184 while witness.len() < num_wit_elems {
185 witness.push(vec![]);
186 }
187 Witness::Stack(witness)
188 } else if self.pref.len() != MAX_SCRIPT_ELEMENT_SIZE {
189 witness.push(vec![1]);
192 while witness.len() < num_wit_elems {
193 witness.push(vec![]);
194 }
195 Witness::Stack(witness)
196 } else {
197 Witness::Impossible
199 }
200 }
201 None => Witness::Unavailable,
204 };
205 Satisfaction {
206 stack: wit,
207 has_sig: false,
208 }
209 }
210
211 fn push_to_builder(&self, builder: elements::script::Builder) -> elements::script::Builder {
212 builder.check_item_pref(4, &self.pref)
213 }
214
215 fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result<Self, FromTokenIterError> {
216 let outputs_pref = {
217 let sl = tokens.peek_slice(15).ok_or(FromTokenIterError)?;
218 if let Tk::Push(pref) = &sl[6] {
219 if sl[0] == Tk::Cat
220 && sl[1] == Tk::Cat
221 && sl[2] == Tk::Cat
222 && sl[3] == Tk::Cat
223 && sl[4] == Tk::Cat
224 && sl[5] == Tk::Cat
225 && sl[7] == Tk::Swap
226 && sl[8] == Tk::Cat
227 && sl[9] == Tk::Hash256
228 && sl[11] == Tk::Num(4)
229 && sl[12] == Tk::Sub
230 && sl[13] == Tk::Pick
231 && sl[14] == Tk::Equal
232 {
233 Self { pref: pref.clone() }
234 } else {
235 return Err(FromTokenIterError);
236 }
237 } else {
238 return Err(FromTokenIterError);
239 }
240 };
241 tokens.advance(15).expect("Size checked previously");
242 Ok(outputs_pref)
243 }
244
245 fn evaluate(
246 &self,
247 stack: &mut interpreter::Stack,
248 _txenv: Option<&TxEnv>,
249 ) -> Result<bool, interpreter::Error> {
250 let hash_outputs = stack[3];
252 let hash_outputs = hash_outputs.try_push()?;
253 let max_elems = MAX_SCRIPT_ELEMENT_SIZE / MAX_STANDARD_P2WSH_STACK_ITEM_SIZE + 1;
255 if hash_outputs.len() == 32 {
256 if stack.len() < max_elems {
258 return Err(interpreter::Error::UnexpectedStackEnd);
259 }
260 let mut outputs_builder = Vec::new();
261 outputs_builder.extend(&self.pref);
262 let len = stack.len();
263 for i in 0..max_elems {
265 outputs_builder.extend(stack[len - max_elems + i].into_slice());
266 }
267 for _ in 0..max_elems {
269 stack.pop().unwrap();
270 }
271 if sha256d::Hash::hash(&outputs_builder).as_byte_array() == hash_outputs {
272 stack.push(interpreter::Element::Satisfied);
273 Ok(true)
274 } else {
275 Ok(false)
276 }
277 } else {
278 Err(interpreter::Error::CovWitnessSizeErr {
279 pos: 9,
280 expected: 32,
281 actual: hash_outputs.len(),
282 })
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use bitcoin::PublicKey;
290
291 use super::*;
292 use crate::{Miniscript, Segwitv0};
293
294 #[test]
295 fn test_outputs_pref() {
296 type MsExtVer = Miniscript<PublicKey, Segwitv0, LegacyOutputsPref>;
297
298 let ms = MsExtVer::from_str_insane("outputs_pref(aa)").unwrap();
299 assert_eq!(ms.to_string(), "outputs_pref(aa)");
301 assert_eq!(ms, MsExtVer::parse_insane(&ms.encode()).unwrap())
303 }
304}