Skip to content

Commit f00da39

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 bf02479 commit f00da39

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
@@ -526,57 +526,80 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
526526

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

552564
/// Filters a policy by eliminating absolute timelock constraints
553565
/// that are not satisfied at the given `n` (`n OP_CHECKLOCKTIMEVERIFY`).
554-
pub fn at_lock_time(mut self, n: absolute::LockTime) -> Policy<Pk> {
566+
pub fn at_lock_time(self, n: absolute::LockTime) -> Policy<Pk> {
555567
use absolute::LockTime::*;
568+
use Policy::*;
556569

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

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

0 commit comments

Comments
 (0)