Skip to content
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

improve relative locktime API #2549

Merged
merged 10 commits into from Mar 22, 2024

Conversation

apoelstra
Copy link
Member

While implementing rust-bitcoin/rust-miniscript#654 I ran into a number of limitations of the relative::LockTime API. This fixes these by

  • Copying a ton of functions from absolute::LockTime to relative::LockTime, adjusting comments and functionality accordingly.
  • Adding conversion functions to/from Sequence numbers, as well as a method to check whether a locktime is satisfied by a given sequence number.

Fixes #2547
Fixes #2545
Fixes #2540

@github-actions github-actions bot added the C-bitcoin PRs modifying the bitcoin crate label Mar 7, 2024
@coveralls
Copy link

coveralls commented Mar 7, 2024

Pull Request Test Coverage Report for Build 8382382642

Details

  • 41 of 120 (34.17%) changed or added relevant lines in 4 files are covered.
  • 4 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.2%) to 83.425%

Changes Missing Coverage Covered Lines Changed/Added Lines %
bitcoin/src/blockdata/locktime/absolute.rs 2 5 40.0%
units/src/locktime/absolute.rs 5 12 41.67%
units/src/locktime/relative.rs 3 19 15.79%
bitcoin/src/blockdata/locktime/relative.rs 31 84 36.9%
Files with Coverage Reduction New Missed Lines %
bitcoin/src/blockdata/locktime/absolute.rs 1 82.82%
units/src/locktime/relative.rs 1 17.65%
bitcoin/src/blockdata/locktime/relative.rs 2 59.41%
Totals Coverage Status
Change from base Build 8377069484: -0.2%
Covered Lines: 19041
Relevant Lines: 22824

💛 - Coveralls

@apoelstra
Copy link
Member Author

CI failing because my doccomment links are broken. I should learn to check that locally..

@apoelstra
Copy link
Member Author

Ready for review.

@@ -40,6 +40,46 @@ pub enum LockTime {
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, there was From<u16>, well, I don't really like that...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this type is basically only used within the LockTime::Height enum variant, where it is very clear what a bare number is supposed to mean, I think it's fine. Same with Time , though the interpretation of the number as "512-second intervals" is maybe surprising.

But sure, I can drop the From impls in a new commit if they bother you. They are not very discoverable and gain little ergonomics over just using LockTime::from_height/from_time.


/// Constructs a `LockTime` from `n`, expecting `n` to be a 16-bit count of blocks.
#[inline]
pub fn from_height(n: u16) -> Self { LockTime::Blocks(Height(n)) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be const.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// This function is a little awkward to use, and users may wish to instead use
/// [`Self::from_seconds_floor`] or [`Self::from_seconds_ceil`].
#[inline]
pub fn from_512_second_intervals(intervals: u16) -> Self { LockTime::Time(Time(intervals)) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
pub fn from_seconds_floor(seconds: u32) -> Result<Self, Error> {
Time::from_seconds_floor(seconds).map(LockTime::Time)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be const with more complicated code. I'm not sure if worth it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in new commit. Wasn't too bad, I think it was worth it.

/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, Error> {
Time::from_seconds_ceil(seconds).map(LockTime::Time)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complicated const again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in new commit. Wasn't too bad, I think it was worth it.

Ok(Time::from_512_second_intervals(interval))
} else {
Err(Error::IntegerOverflow(seconds))
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be const too with a bit uglier code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in new commit. Wasn't too bad, I think it was worth it.

@@ -222,6 +262,10 @@ impl Height {
/// The maximum relative block height.
pub const MAX: Self = Height(u16::max_value());

/// Create a [`Height`] using a count of blocks.
#[inline]
pub fn from_height(blocks: u16) -> Self { Height(blocks) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial const.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

///
/// This method will **not** round-trip with [`Self::to_consensus_u32`], because relative
/// locktimes only use some bits of the underlying `u32` value and discard the rest. If
/// you want to preserve the full value, you should use the [`Sequence`] type instead.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, this looks like a foot gun. Is it really needed? Just decoding Sequence and converting that is less error-prone and more explicit.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I considered this. I could go either way on it. I'm not sure that making users go through Sequence makes this flow any less footgunny.

But I can remove this if you feel strongly about it.

I'd maybe like to add a method to script to encode a Sequence number directly into script, if there isn't one, so that users would never need a u32.

bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
match *self {
LockTime::Blocks(_) => true,
LockTime::Time(_) => false,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just use matches!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 8e43684

bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
@apoelstra
Copy link
Member Author

In one of the resolved conversations it was noted that this doesn't implement ArbitraryOrd. I opened this issue to track this #2566

@tcharding tcharding added this to the 0.32.0 milestone Mar 11, 2024
@tcharding
Copy link
Member

Needs rebase mate.

@github-actions github-actions bot added the C-units PRs modifying the units crate label Mar 18, 2024
bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Outdated Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Outdated Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Show resolved Hide resolved
bitcoin/src/blockdata/locktime/relative.rs Outdated Show resolved Hide resolved
@tcharding
Copy link
Member

Wow, super bad process by me. I previously ack'ed this PR and now I've given a 9-comment review. Therefore, every comment is a can-do-in-followup-PR

tcharding
tcharding previously approved these changes Mar 18, 2024
Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK b72cc8c

@apoelstra
Copy link
Member Author

Addressed all comments except the one about the ZERO constant.

tcharding
tcharding previously approved these changes Mar 19, 2024
Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK b72cc8c

@tcharding
Copy link
Member

Thanks man!

@tcharding
Copy link
Member

Needs rebase again bro

@apoelstra
Copy link
Member Author

Done.

tcharding
tcharding previously approved these changes Mar 21, 2024
Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error getter can be done later, and the failure to add it is a review fail by me for not noticing earlier. I"m planning on going over all the errors ... again ... next release.

In commit 2799ecdf relative locktime: add "obvious" constructors the last line of the commit log is stale, from_consensus change is in a later PR.

ACK d3fe9ac

/// Error returned when a sequence number is parsed as a lock time, but its
/// "disable" flag is set.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DisabledLockTimeError(u32);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a leaf error type so we should probably provide a getter if I remembered correctly and that is the policy we settled upon.

Adds constructors to allow directly creating locktimes from time or
block counts; adds a flooring constructor to Time to match the ceiling
one; adds an explicit constructor to Height since the From<u16> was not
very discoverable.
This gives a way to determine whether a CSV will pass, given a sequence
number, in a type-safe way where you can't get the two things backward.
Copy these from absolute::LockTime. While we are at it, make the
functions in absolute::LockTime const.
@apoelstra
Copy link
Member Author

Rebased on master, added an accessor to the error type, and fixed the commit message.

Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 04715e3

@tcharding
Copy link
Member

@sanket1729 have you got time to review this guy please? I'd love to have a play with the rust-miniscript bitcoin upgrade some more once this merges - it would be great to be able to get rid of RelLockTime with this release.

@apoelstra
Copy link
Member Author

@tcharding I don't think we can get rid of RelLockTime. We still need the a wrapper that implements Ord and we still need some other parsing-related properties that relative::LockTime doesn't have. Same reason we still have AbsLockTime even though we've made some improvements to absolute::LockTime here.

So really all this PR will do for us is simplify the implementation of RelLockTime.

@tcharding
Copy link
Member

The Ord stuff is handled by #2581. What parsing stuff? After this PR goes in relative::LockTime and Sequence are interoperable so to speak, so RelLockTime(Sequence) seems like it should be able to go away?

Oooh, I see AbsLockTIme has an additional invariant (can't be 0) so it has to stay even with ArbitraryOrd, bother. No real benefit removing one and not the other.

rust-miniscript resists my attempts to improve it ... again :)

@tcharding
Copy link
Member

Sorry for the noise @sanket1729

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 04715e3

@apoelstra
Copy link
Member Author

Oooh, I see AbsLockTIme has an additional invariant (can't be 0) so it has to stay even with ArbitraryOrd, bother. No real benefit removing one and not the other.

Oh that's right! RelLockTime has the same invariant.

I knew there was something dumb in Miniscript that necessitated these types..

@apoelstra
Copy link
Member Author

rust-miniscript resists my attempts to improve it ... again :)

Well, rust-miniscript does have a few APIs (satisfaction/PSBT) that interact with the "real" types that come with rust-bitcoin. These are currently a bit awkward and weird and will definitely be improved by this PR.

@apoelstra apoelstra merged commit 3851441 into rust-bitcoin:master Mar 22, 2024
187 checks passed
@apoelstra apoelstra deleted the 2024-03--relative-locktime branch March 22, 2024 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bitcoin PRs modifying the bitcoin crate C-units PRs modifying the units crate P-high High priority
Projects
None yet
5 participants