Skip to content

Commit cdf28e0

Browse files
committed
refactor: Simplify policy satisfier
Use hidden nodes to signify unsatisfiability of sub-expressions. When a sub-expression is unsatisfiable, then it makes no sense to execute it. We can just as well keep its CMR in a hidden node. This simplifies the code and it solves a potential problem of the previous design: a hidden node could be marked as satisfiable.
1 parent ba5c7ef commit cdf28e0

File tree

1 file changed

+62
-103
lines changed

1 file changed

+62
-103
lines changed

src/policy/satisfy.rs

+62-103
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,13 @@ pub enum SatisfierError {
105105
AssemblyFailed(crate::bit_machine::ExecutionError),
106106
}
107107

108-
#[derive(Clone, Debug)]
109-
struct SatResult {
110-
serialization: Hiding<Arc<WitnessNode<Elements>>>,
111-
is_satisfied: bool,
108+
type SatResult = Hiding<Arc<WitnessNode<Elements>>>;
109+
110+
fn ok_if(condition: bool, expr: SatResult) -> SatResult {
111+
match condition {
112+
true => expr,
113+
false => expr.hide(),
114+
}
112115
}
113116

114117
impl<Pk: ToXOnlyPubkey> Policy<Pk> {
@@ -117,127 +120,97 @@ impl<Pk: ToXOnlyPubkey> Policy<Pk> {
117120
inference_context: &types::Context,
118121
satisfier: &S,
119122
) -> Result<SatResult, SatisfierError> {
120-
let node = match *self {
121-
Policy::Unsatisfiable(entropy) => SatResult {
122-
serialization: super::serialize::unsatisfiable(inference_context, entropy),
123-
is_satisfied: false,
124-
},
125-
Policy::Trivial => SatResult {
126-
serialization: super::serialize::trivial(inference_context),
127-
is_satisfied: true,
128-
},
123+
let node: SatResult = match *self {
124+
Policy::Unsatisfiable(entropy) => {
125+
super::serialize::unsatisfiable::<SatResult>(inference_context, entropy).hide()
126+
}
127+
Policy::Trivial => super::serialize::trivial(inference_context),
129128
Policy::Key(ref key) => {
130129
let signature = satisfier
131130
.lookup_tap_leaf_script_sig(key, &TapLeafHash::all_zeros())
132131
.map(|sig| sig.sig.serialize())
133132
.map(Value::u512);
134-
SatResult {
135-
is_satisfied: signature.is_some(),
136-
serialization: super::serialize::key(inference_context, key, signature),
137-
}
133+
ok_if(
134+
signature.is_some(),
135+
super::serialize::key(inference_context, key, signature),
136+
)
138137
}
139138
Policy::After(n) => {
140139
let height = Height::from_consensus(n).expect("timelock is valid");
141-
SatResult {
142-
serialization: super::serialize::after(inference_context, n),
143-
is_satisfied: satisfier.check_after(elements::LockTime::Blocks(height)),
144-
}
140+
ok_if(
141+
satisfier.check_after(elements::LockTime::Blocks(height)),
142+
super::serialize::after(inference_context, n),
143+
)
145144
}
146-
Policy::Older(n) => SatResult {
147-
serialization: super::serialize::older(inference_context, n),
148-
is_satisfied: satisfier.check_older(elements::Sequence(n.into())),
149-
},
145+
Policy::Older(n) => ok_if(
146+
satisfier.check_older(elements::Sequence(n.into())),
147+
super::serialize::older(inference_context, n),
148+
),
150149
Policy::Sha256(ref hash) => {
151150
let preimage = satisfier.lookup_sha256(hash).map(Value::u256);
152-
SatResult {
153-
is_satisfied: preimage.is_some(),
154-
serialization: super::serialize::sha256::<Pk, _, _>(
155-
inference_context,
156-
hash,
157-
preimage,
158-
),
159-
}
151+
ok_if(
152+
preimage.is_some(),
153+
super::serialize::sha256::<Pk, _, _>(inference_context, hash, preimage),
154+
)
160155
}
161156
Policy::And {
162157
ref left,
163158
ref right,
164159
} => {
165-
let SatResult {
166-
serialization: left,
167-
is_satisfied: left_ok,
168-
} = left.satisfy_internal(inference_context, satisfier)?;
169-
let SatResult {
170-
serialization: right,
171-
is_satisfied: right_ok,
172-
} = right.satisfy_internal(inference_context, satisfier)?;
173-
SatResult {
174-
serialization: super::serialize::and(&left, &right),
175-
is_satisfied: left_ok && right_ok,
176-
}
160+
let left_res = left.satisfy_internal(inference_context, satisfier)?;
161+
let right_res = right.satisfy_internal(inference_context, satisfier)?;
162+
super::serialize::and(&left_res, &right_res)
177163
}
178164
Policy::Or {
179165
ref left,
180166
ref right,
181167
} => {
182-
let SatResult {
183-
serialization: left,
184-
is_satisfied: left_ok,
185-
} = left.satisfy_internal(inference_context, satisfier)?;
186-
let SatResult {
187-
serialization: right,
188-
is_satisfied: right_ok,
189-
} = right.satisfy_internal(inference_context, satisfier)?;
190-
let take_right = match (left_ok, right_ok) {
191-
(false, false) => {
168+
let left_res = left.satisfy_internal(inference_context, satisfier)?;
169+
let right_res = right.satisfy_internal(inference_context, satisfier)?;
170+
let take_right = match (left_res.as_node(), right_res.as_node()) {
171+
(Some(left), Some(right)) => {
192172
let left_cost = left
193-
.as_node()
194-
.expect("node exists if satisfiable")
195173
.finalize_unpruned()
196174
.expect("serialization should be sound")
197175
.bounds()
198176
.cost;
199177
let right_cost = right
200-
.as_node()
201-
.expect("node exists if satisfiable")
202178
.finalize_unpruned()
203179
.expect("serialization should be sound")
204180
.bounds()
205181
.cost;
206182
right_cost < left_cost
207183
}
208-
(false, true) => true,
209-
(true, false) => false,
184+
(None, Some(..)) => true,
185+
(Some(..), None) => false,
210186
// If both children are unsatisfiable, then the choice doesn't matter.
211187
// The entire expression will be pruned out.
212-
(true, true) => false,
188+
(None, None) => false,
213189
};
214190

215-
let serialization = if take_right {
216-
super::serialize::or(&left, &right, Some(Value::u1(1)))
191+
let ret = if take_right {
192+
super::serialize::or(&left_res, &right_res, Some(Value::u1(1)))
217193
} else {
218-
super::serialize::or(&left, &right, Some(Value::u1(0)))
194+
super::serialize::or(&left_res, &right_res, Some(Value::u1(0)))
219195
};
220-
SatResult {
221-
serialization,
222-
is_satisfied: left_ok || right_ok,
223-
}
196+
ok_if(
197+
left_res.as_node().is_some() || right_res.as_node().is_some(),
198+
ret,
199+
)
224200
}
225201
Policy::Threshold(k, ref subs) => {
226-
let node_results: Vec<SatResult> = subs
202+
let subs_res: Vec<SatResult> = subs
227203
.iter()
228204
.map(|sub| sub.satisfy_internal(inference_context, satisfier))
229205
.collect::<Result<_, SatisfierError>>()?;
230-
let costs: Vec<Cost> = node_results
206+
let costs: Vec<Cost> = subs_res
231207
.iter()
232-
.map(|result| match result.is_satisfied {
233-
true => result
234-
.serialization
235-
.as_node()
236-
.expect("node exists if satisfiable")
208+
.map(|result| match result.as_node() {
209+
Some(node) => node
237210
.finalize_unpruned()
238211
.map(|redeem| redeem.bounds().cost)
239212
.unwrap_or(Cost::CONSENSUS_MAX),
240-
false => Cost::CONSENSUS_MAX,
213+
None => Cost::CONSENSUS_MAX,
241214
})
242215
.collect();
243216
let selected_node_indices = {
@@ -248,30 +221,19 @@ impl<Pk: ToXOnlyPubkey> Policy<Pk> {
248221
};
249222
let all_selected_ok = selected_node_indices
250223
.iter()
251-
.all(|&i| node_results[i].is_satisfied);
252-
let witness_bits: Vec<Option<Value>> = (0..node_results.len())
224+
.all(|&i| subs_res[i].as_node().is_some());
225+
let witness_bits: Vec<Option<Value>> = (0..subs_res.len())
253226
.map(|i| Some(Value::u1(u8::from(selected_node_indices.contains(&i)))))
254227
.collect();
255-
256228
let k = u32::try_from(k).expect("k should be less than 2^32");
257-
let nodes: Vec<_> = node_results
258-
.into_iter()
259-
.map(|result| result.serialization)
260-
.collect();
261-
SatResult {
262-
serialization: super::serialize::threshold(k, &nodes, &witness_bits),
263-
is_satisfied: all_selected_ok,
264-
}
229+
ok_if(
230+
all_selected_ok,
231+
super::serialize::threshold(k, &subs_res, &witness_bits),
232+
)
265233
}
266234
Policy::Assembly(cmr) => match satisfier.lookup_asm_program(cmr) {
267-
Some(program) => SatResult {
268-
serialization: Hiding::from(program),
269-
is_satisfied: true,
270-
},
271-
None => SatResult {
272-
serialization: Hiding::hidden(cmr, inference_context.shallow_clone()),
273-
is_satisfied: false,
274-
},
235+
Some(program) => Hiding::from(program),
236+
None => Hiding::hidden(cmr, inference_context.shallow_clone()),
275237
},
276238
};
277239
Ok(node)
@@ -286,17 +248,14 @@ impl<Pk: ToXOnlyPubkey> Policy<Pk> {
286248
satisfier: &S,
287249
env: &ElementsEnv<Arc<elements::Transaction>>,
288250
) -> Result<Arc<RedeemNode<Elements>>, SatisfierError> {
289-
let SatResult {
290-
serialization: witness_program,
291-
is_satisfied,
292-
} = self.satisfy_internal(&types::Context::new(), satisfier)?;
293-
match (witness_program.get_node(), is_satisfied) {
294-
(Some(program), true) => program
251+
let result = self.satisfy_internal(&types::Context::new(), satisfier)?;
252+
match result.get_node() {
253+
Some(program) => program
295254
.finalize_unpruned()
296255
.expect("serialization should be sound")
297256
.prune(env)
298257
.map_err(SatisfierError::AssemblyFailed), // execution fails iff assembly fragment fails
299-
_ => Err(SatisfierError::Unsatisfiable),
258+
None => Err(SatisfierError::Unsatisfiable),
300259
}
301260
}
302261
}

0 commit comments

Comments
 (0)