-
Notifications
You must be signed in to change notification settings - Fork 146
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
Detect miniscripts that have a satisfaction with a combination of time locks and height locks #121
Conversation
src/miniscript/types/extra_props.rs
Outdated
/// contains_timelock[1] = csv with times | ||
/// contains_timelock[2] = cltv with heights | ||
/// contains_timelock[3] = cltv with times | ||
/// contains_timelock[4] = combination of any heightlocks and timelocks |
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.
Please create a structure for this instead of using a [bool; 5]
:)
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.
You can impl Default
on it so you can construct it 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.
To be clear, I can't review this PR any further until this is fixed :)
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.
@apoelstra, This meaning the logic condition? I can change that quickly
// disjunction | ||
assert!(ConcretePol::from_str("or(after(1000000000),after(100))").is_ok()); | ||
// conjunction | ||
assert!(ConcretePol::from_str("and(after(1000000000),after(100))").is_err()); |
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.
Why should this fail?
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.
A conjunction of height lock and timelock?
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, right, I forgot that timelocks are defined implicitly by having a value >500000000.
I really don't like this, we shuold modify Miniscript to use a different syntax for them.
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.
Any suggestions? For starters, I like the way shesek did it for minsc. https://min.sc/#time-durations
ca46d42
to
bb4991a
Compare
src/miniscript/satisfy.rs
Outdated
@@ -94,7 +95,13 @@ pub struct Older(pub u32); | |||
|
|||
impl<Pk: MiniscriptKey> Satisfier<Pk> for Older { | |||
fn check_older(&self, n: u32) -> bool { | |||
n <= self.0 | |||
if !((n < HEIGHT_TIME_THRESHOLD && self.0 < HEIGHT_TIME_THRESHOLD) |
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.
It may be simpler to check either
(n < HEIGHT_TIME_THRESHOLD) == (self.0 < HEIGHT_TIME_THRESHOLD)
or just
n < HEIGHT_TIME_THRESHOLD && self.0 >= HEIGHT_TIME_THRESHOLD
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.
My logic for these is trying to implement "as it is" written in the BIP.
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.
Which BIP?
My suggestion is logically equivalent to what's written here, for what it's worth, it's just shorter.
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.
https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki . I can change this too, just was a bit pedantic in trying to copy the logic conditions directly.
src/miniscript/satisfy.rs
Outdated
@@ -104,7 +111,13 @@ pub struct After(pub u32); | |||
|
|||
impl<Pk: MiniscriptKey> Satisfier<Pk> for After { | |||
fn check_after(&self, n: u32) -> bool { | |||
n <= self.0 | |||
if !((n < HEIGHT_TIME_THRESHOLD && self.0 < HEIGHT_TIME_THRESHOLD) | |||
|| (n >= HEIGHT_TIME_THRESHOLD && self.0 >= HEIGHT_TIME_THRESHOLD)) |
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.
ditto here
src/policy/concrete.rs
Outdated
@@ -115,6 +118,9 @@ impl fmt::Display for PolicyError { | |||
"Policy entailment only supports {} terminals", | |||
ENTAILMENT_MAX_TERMINALS | |||
), | |||
PolicyError::HeightTimeLockCombination => { | |||
f.write_str("Cannot lift polcies that have a heightlock and timelock combination") |
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.
typo policies
4f65119
to
1e55cad
Compare
src/policy/concrete.rs
Outdated
// timelocks[1]: satisfaction contains csv timelock | ||
// timelocks[2]: satisfaction contains cltv heightlock | ||
// timelocks[3]: satisfaction contains cltv timelock | ||
// timelocks[4]: satisfaction contains an invalid combination |
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.
delete this comment
src/miniscript/types/extra_props.rs
Outdated
// handy function for combining `and` timelocks | ||
// This can be operator overloaded in future | ||
pub(crate) fn comb_and_timelocks(a: Self, b: Self) -> Self { | ||
Self::combine_thresh_timelocks(2, vec![a, b].into_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.
Can you do once(a).chain(once(b))
here rather than Vec::into_iter
. (You need to use std::iter::once
.) Saves the allocation.
/// cltv with times | ||
pub cltv_with_time: bool, | ||
/// combination of any heightlocks and timelocks | ||
pub contains_combination: bool, |
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.
Do we actually need this field? Or can we just check (csv_with_height && csv_with_time) || (cltv_with_height && cltv_with_time)
?
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 - it is there to cover the disjunction case, where you may have a heighlock branch and a timelock branch, but they are independent so it is ok.
src/miniscript/types/extra_props.rs
Outdated
// 2) hieghtlocks and timelocks are distributed across two branches: | ||
// and we check if any of the existing branches has correponding lock | ||
// that cannot be combined | ||
// If k == 1, then this is no combination possible. |
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 find this whole comment block confusing. I think you should replace it with
// Propagate all fields of `TimelockInfo` from each of the node's children to the node
// itself (by taking the logical-or of all of them). In case `k == 1` (this is a disjunction)
// this is all we need to do: the node may behave like any of its children, for purposes
// of timelock accounting.
//
// If `k > 1` we have the additional consideration that if any two children have conflicting
// timelock requirements, this represents an inaccessible spending branch. If there are
// two such children we must set the `contains_combination` field.
src/miniscript/types/extra_props.rs
Outdated
|| (timelock_info.csv_with_time && sub_timelock.csv_with_height) | ||
|| (timelock_info.cltv_with_time && sub_timelock.cltv_with_height) | ||
|| (timelock_info.cltv_with_height && sub_timelock.cltv_with_time); | ||
} |
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.
You can move this block out of the loop and simplify it:
if k >= 2 {
timelock_info.contains_combination |= timelock_info.csv_with_time && timelock_info.csv_with_height;
timelock_info.contains_combination |= timelock_info.cltv_with_time && timelock_info.cltv_with_height;
}
(or however you want to format it)
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.
Once it is out of the loop you can replace the loop with fold
, as
let mut ret = sub_timelocks.fold(TimelockInfo::default(), |acc, x| TimelockInfo {
csv_with_time: acc.csv_with_time | x.csv_with_time,
csv_with_height: acc.csv_with_height | x.csv_with_height,
cltv_with_time: acc.cltv_with_time | x.cltv_with_time;
cltv_with_height: acc.cltv_with_height | x.cltv_with_height;
contains_combination: acc.contains_combination | x.contains_combination;
});
if k > 1 {
ret.contains_combination |= ret.csv_with_time && ret.csv_with_height;
ret.contains_combination |= ret.cltv_with_time && ret.cltv_with_height;
}
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 incorrect. As it will set contains_combination
for the following policy too.
thresh(2,pk(),pk(),or(time,height))
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.
Unforetunately, I think it is necessary to check k>2 inside of loop.
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 your existing code has the same issue.
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, no, I see -- the distinction is that only the one child has any timelocks at all. Interesting.
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.
Ok, so I leave it up to you whether you want to bother using fold
instead of a loop. But can you add a comment above the k >= 2
line saying
// If more than one branch may be taken, and some other branch has a requirement
// that conflicts with this one, set `contains_combination`
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.
Changing to fold
1e55cad
to
aba41d0
Compare
aba41d0
to
a662105
Compare
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 a662105
No description provided.