Skip to content

Commit d9f9d43

Browse files
committed
semantic: Remove recursion in at_age and at_lock_time
Done as part of the effort to remove all the recursion crate wide. Use the `TreeLike` trait to iterate over policy nodes and remove the recursive call in `semantic::Policy::at_age` and `semantic::Policy::at_age`. Done together because they are basically identical.
1 parent c3c5e72 commit d9f9d43

File tree

1 file changed

+64
-41
lines changed

1 file changed

+64
-41
lines changed

src/policy/semantic.rs

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -517,57 +517,80 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
517517

518518
/// Filters a policy by eliminating relative timelock constraints
519519
/// that are not satisfied at the given `age`.
520-
pub fn at_age(mut self, age: Sequence) -> Policy<Pk> {
521-
self = match self {
522-
Policy::Older(t) => {
523-
if t.is_height_locked() && age.is_time_locked()
524-
|| t.is_time_locked() && age.is_height_locked()
525-
|| t.to_consensus_u32() > age.to_consensus_u32()
526-
{
527-
Policy::Unsatisfiable
528-
} else {
529-
Policy::Older(t)
520+
pub fn at_age(self, age: Sequence) -> Policy<Pk> {
521+
use Policy::*;
522+
523+
let mut at_age = vec![];
524+
for data in Arc::new(self).post_order_iter() {
525+
let child_n = |n| Arc::clone(&at_age[data.child_indices[n]]);
526+
527+
let new_policy = match data.node.as_ref() {
528+
Older(ref t) => {
529+
if t.is_height_locked() && age.is_time_locked()
530+
|| t.is_time_locked() && age.is_height_locked()
531+
|| t.to_consensus_u32() > age.to_consensus_u32()
532+
{
533+
Some(Policy::Unsatisfiable)
534+
} else {
535+
Some(Policy::Older(*t))
536+
}
537+
}
538+
Threshold(k, ref subs) => {
539+
Some(Threshold(*k, (0..subs.len()).map(child_n).collect()))
530540
}
541+
_ => None,
542+
};
543+
match new_policy {
544+
Some(new_policy) => at_age.push(Arc::new(new_policy)),
545+
None => at_age.push(Arc::clone(&data.node)),
531546
}
532-
Policy::Threshold(k, subs) => Policy::Threshold(
533-
k,
534-
subs.into_iter()
535-
.map(|sub| Arc::new(sub.as_ref().clone().at_age(age)))
536-
.collect(),
537-
),
538-
x => x,
539-
};
540-
self.normalized()
547+
}
548+
// Unwrap is ok because we know we processed at least one node.
549+
let root_node = at_age.pop().unwrap();
550+
// Unwrap is ok because we know `root_node` is the only strong reference.
551+
let policy = Arc::try_unwrap(root_node).unwrap();
552+
policy.normalized()
541553
}
542554

543555
/// Filters a policy by eliminating absolute timelock constraints
544556
/// that are not satisfied at the given `n` (`n OP_CHECKLOCKTIMEVERIFY`).
545-
pub fn at_lock_time(mut self, n: absolute::LockTime) -> Policy<Pk> {
557+
pub fn at_lock_time(self, n: absolute::LockTime) -> Policy<Pk> {
546558
use absolute::LockTime::*;
559+
use Policy::*;
547560

548-
self = match self {
549-
Policy::After(t) => {
550-
let t = absolute::LockTime::from(t);
551-
let is_satisfied_by = match (t, n) {
552-
(Blocks(t), Blocks(n)) => t <= n,
553-
(Seconds(t), Seconds(n)) => t <= n,
554-
_ => false,
555-
};
556-
if !is_satisfied_by {
557-
Policy::Unsatisfiable
558-
} else {
559-
Policy::After(t.into())
561+
let mut at_age = vec![];
562+
for data in Arc::new(self).post_order_iter() {
563+
let child_n = |n| Arc::clone(&at_age[data.child_indices[n]]);
564+
565+
let new_policy = match data.node.as_ref() {
566+
After(t) => {
567+
let t = absolute::LockTime::from(*t);
568+
let is_satisfied_by = match (t, n) {
569+
(Blocks(t), Blocks(n)) => t <= n,
570+
(Seconds(t), Seconds(n)) => t <= n,
571+
_ => false,
572+
};
573+
if !is_satisfied_by {
574+
Some(Unsatisfiable)
575+
} else {
576+
Some(After(t.into()))
577+
}
578+
}
579+
Threshold(k, ref subs) => {
580+
Some(Threshold(*k, (0..subs.len()).map(child_n).collect()))
560581
}
582+
_ => None,
583+
};
584+
match new_policy {
585+
Some(new_policy) => at_age.push(Arc::new(new_policy)),
586+
None => at_age.push(Arc::clone(&data.node)),
561587
}
562-
Policy::Threshold(k, subs) => Policy::Threshold(
563-
k,
564-
subs.into_iter()
565-
.map(|sub| Arc::new(sub.as_ref().clone().at_lock_time(n)))
566-
.collect(),
567-
),
568-
x => x,
569-
};
570-
self.normalized()
588+
}
589+
// Unwrap is ok because we know we processed at least one node.
590+
let root_node = at_age.pop().unwrap();
591+
// Unwrap is ok because we know `root_node` is the only strong reference.
592+
let policy = Arc::try_unwrap(root_node).unwrap();
593+
policy.normalized()
571594
}
572595

573596
/// Counts the number of public keys and keyhashes referenced in a policy.

0 commit comments

Comments
 (0)