-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement TreeLike
for policy::Concrete
#594
Implement TreeLike
for policy::Concrete
#594
Conversation
TreeLike
for policy::Concrete
01a31db
to
0c7575b
Compare
Unless I'm missing something, if this goes in, we can remove the |
@apoelstra hats off to your |
0c7575b
to
74cafab
Compare
Are you able to split this into two commits: one that drops 74cafab looks good to me. |
Can do! |
74cafab
to
b0151f3
Compare
I did that then went a bit wild using |
src/policy/concrete.rs
Outdated
Policy::Threshold(_k, ref subs) => { | ||
subs.iter().flat_map(|sub| sub.keys()).collect::<Vec<_>>() | ||
let mut keys = vec![]; | ||
for data in self.post_order_iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 062e400:
I think we should use the pre-order iterator here, which is more intuitively correct. (In general, the post-order iterator matches recursive logic, but I think that, since we're only counting leaf nodes, the two iterators will actually have the same effect.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh my bad, I had it in my head that the post order iter was the simpler of the two, I was wrong. Defaulting to pre-order iter when we don't need references to the child nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, post-order is definitely the most common because it allows you to simulate depth-first recursive algorithms. But conceptually I think pre-order is more natural.
Threshold(k, subs) if *k == 1 => (0..subs.len()).map(num_for_child_n).sum(), | ||
_ => 1, | ||
}; | ||
nums.push(num); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 7e73ffe:
We don't need to maintain a vector of numbers here. We can just do a running sum and avoid some allocations and unwraps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we can, don't we need to sum the results of nested Or
s? I don't think we can do that without recursion or keeping the nums vec. Am I correct in thinking a tree like this should return 4
or
/ \
or or
/ \ /\ /\ /\
k k k k k k k
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, you're right, I looked at the algorithm too quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No sweat, it made me double check it which was not a bad thing :)
src/policy/concrete.rs
Outdated
if let Policy::Key(ref pk) = data.node { | ||
if !pred(pk) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 37f3796:
I also think this one should use pre-order iter; I also think it makes no difference here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
b0151f3
to
f145ac0
Compare
Changes in force push:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK f145ac0
The `concrete::PolicyArc` type is identical to `Policy` except that it uses `Arc` to wrap the nested `Policy` structs. In preparation for implementing `TreeLike` and removing recursive functions that process the `Policy` type remove the `PolicyArc` and add `Arc` wrappers around the nested `Policy` fields. Note, this patch does a bunch on cloning in the recursive functions. This will dissapear when they are re-written to use tree iteration. There are more optimizaitons available by taking `Arc<Policy<Pk>>` as a parameter to functions in the `compiler` module, left for later. The unit tests are bit klunky, much mapping of iterators to add `Arc`, I think this will come out in the wash as we keep working.
In preparation for removing recursion in the `policy::concrete` module implement `TreeLike` for `policy::Concrete` (reference, and `Arc`).
We are about to start using the `TreeLike` trait everywhere, hence it will become an easily recognizable pattern. The current single case has a code comment that is noisy if duplicated many times.
In preparation for modifying the `translate_unsatisfiable_pk` function add a basic unit test.
We just implemented `TreeLike` for `concrete::Policy`, use it to implement `translate_unsatisfiable_pk`, removing recursive calls from the function.
In preparation for modifying the `keys` function add a basic unit test.
Remove recursive calls and use `TreeLike`'s post order iterator to get all the keys from a `concrete::Policy`.
In preparation for modifying the `num_tap_leaves` function add a basic unit test.
Remove recursive calls and use `TreeLike`'s post order iterator to get the total number of tap leaves for a `concrete::Policy`.
In preparation for modifying the `for_each_key` add an additional unit test that checks we correctly handle a failing predicate. While we are at it rename the existing test.
Remove recursive calls and use `TreeLike`'s post order iterator to implement `for_each_key` for the `concrete::Policy`. No additional unit tests required, this code path is already tested.
In preparation for modifying the `translate_pk` function add a basic unit test.
Remove recursive calls and use `TreeLike`'s post order iterator to implement `translate_pk` for the `concrete::Policy`. Note, no additional unit tests added for this code path, this is a familiar code pattern now.
Add a unit test that attempts to parse a `concrete::Policy` with both absolute height and time locks. Done in preparation for patching `check_timelocks_helper`.
Remove recursive calls and use `TreeLike`'s post order iterator to implement `check_timelocks_helper` for the `concrete::Policy`. Note, no additional unit tests added for this code path, this is a familiar code pattern now.
Rename the helper function and local variable at its call site to better describe the functionality. Refactor only, no logic changes.
f145ac0
to
1efc7a3
Compare
Rebase and run formatter on each commit. |
/// A list of sub-policies, one of which must be satisfied, along with | ||
/// relative probabilities for each one. | ||
Or(Vec<(usize, Policy<Pk>)>), | ||
Or(Vec<(usize, Arc<Policy<Pk>>)>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/me gently banging head on table crying, "please god no, don't let this be a vector when it is only ever exactly 2 policies"
@apoelstra or @sanket1729 are Or
and And
policies only ever comprised of exactly 2 sub-policies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might have such a restriction right now (you'll find some panic
s enforcing it) but we intend to lift this restriction eventually.
The main issue is the computational complexity of compiling n-ary or
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but we intend to lift this restriction eventually.
By we do you mean miniscript in general or rust-miniscript? From the miniscript docs it looks like its limited to 2 also but I couldn't tell from the syntax if that was guaranteed. Is it ambiguous for the same reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Miniscript in general.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, so leave these as vecs then. Perhaps I'll add some comments. I literally nearly fell apart yesterday, after 3 days of mental modelling of vectors and tree iteration to find this "2 limit". I'm better today :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad to hear it :). Sure, some comments would be appreciated.
// Currently these vectors are limited to two elements. Eventually we would like to extend these
// to be n-ary, but first we need to decide on a game plan for how to efficiently compile n-ary
// disjunctions
or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no such length restrictions on the Policy::Threshold
so when k == 1
we essentially have unbounded Or
and when k == v.len()
we have unbounded And
- should we be restricting the length of the vector in these two cases also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR I'm playing around with creating a Thresh<T>
type to maintain the invariants v.len() > 0
, k != 0,
k <= v.len()`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 1efc7a3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 1efc7a3
@@ -68,3 +68,29 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for Arc<Miniscript<Pk, Ctx> | |||
} | |||
} | |||
} | |||
|
|||
impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete<Pk> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really not a concern for this PR, but I think policy stuff should belong in policy module. Not here. See #603 for more.
Make a start on removing recursion in the
policy::concrete
module.PolicyArc
TreeLike
forconcrete::Policy
(does not use it)There is still some to go but this is already a pretty heavy review burden.