Skip to content

Commit 68d3507

Browse files
committed
locktime: use bitcoin-units locktime types
In bitcoin-units 1.0 we have types that represent locktime times and heights. The locktimes themselves are not in units (they will be exposed in bitcoin-primitives once that comes out) but the essential hard logic is implemented on these supporting types. Importantly, the new types don't implement serde, which is why we needed to do breaking serde changes in the last couple of commits. (But we should have done these changes a long time ago, anyway.)
1 parent bb4e4ec commit 68d3507

File tree

2 files changed

+12
-309
lines changed

2 files changed

+12
-309
lines changed

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ mod transaction;
7171
mod endian;
7272
// re-export bitcoin deps which we re-use
7373
pub use bitcoin::hashes;
74+
// Re-export units which are identical in Bitcoin and Elements
75+
pub use bitcoin_units::{
76+
BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime,
77+
MathOp, NumOpError, NumOpResult,
78+
Weight,
79+
};
7480
// export everything at the top level so it can be used as `elements::Transaction` etc.
7581
pub use crate::address::{Address, AddressError, AddressParams};
7682
pub use crate::blind::{

src/locktime.rs

Lines changed: 6 additions & 309 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,14 @@
1919
2020
use std::{mem, fmt};
2121
use std::cmp::{PartialOrd, Ordering};
22-
use std::convert::TryFrom;
23-
use std::str::FromStr;
2422
use std::io::{Read, Write};
25-
use crate::error::ParseIntError;
26-
use crate::parse;
2723

2824
use crate::encode::{self, Decodable, Encodable};
29-
use crate::error::write_err;
3025
use crate::parse::impl_parse_str_through_int;
3126

32-
/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
33-
///
34-
/// `LockTime` values _below_ the threshold are interpreted as block heights, values _above_ (or
35-
/// equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch).
36-
///
37-
/// Elements is able to safely use this value because a block height greater than 500,000,000 would
38-
/// never occur because it would represent a height in approximately 950 years. Conversely, block
39-
/// times under 500,000,000 will never happen because they would represent times before 1986 which
40-
/// are, for obvious reasons, not useful within any Elements network.
41-
///
42-
/// [Bitcoin Core]: https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
43-
pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
27+
pub use bitcoin_units::locktime::absolute::{Height, Time};
28+
pub use bitcoin_units::locktime::absolute::ConversionError;
29+
pub use bitcoin_units::locktime::absolute::LOCK_TIME_THRESHOLD;
4430

4531
/// A lock time value, representing either a block height or a UNIX timestamp (seconds since epoch).
4632
///
@@ -96,7 +82,7 @@ pub enum LockTime {
9682
impl LockTime {
9783
/// If [`crate::Transaction::lock_time`] is set to zero it is ignored, in other words a
9884
/// transaction with nLocktime==0 is able to be included immediately in any block.
99-
pub const ZERO: LockTime = LockTime::Blocks(Height(0));
85+
pub const ZERO: LockTime = LockTime::Blocks(Height::ZERO);
10086

10187
/// Constructs a `LockTime` from an nLockTime value or the argument to `OP_CHEKCLOCKTIMEVERIFY`.
10288
///
@@ -130,7 +116,7 @@ impl LockTime {
130116
/// assert!(LockTime::from_height(1653195600).is_err());
131117
/// ```
132118
#[inline]
133-
pub fn from_height(n: u32) -> Result<Self, Error> {
119+
pub fn from_height(n: u32) -> Result<Self, ConversionError> {
134120
let height = Height::from_consensus(n)?;
135121
Ok(LockTime::Blocks(height))
136122
}
@@ -146,7 +132,7 @@ impl LockTime {
146132
/// assert!(LockTime::from_time(741521).is_err());
147133
/// ```
148134
#[inline]
149-
pub fn from_time(n: u32) -> Result<Self, Error> {
135+
pub fn from_time(n: u32) -> Result<Self, ConversionError> {
150136
let time = Time::from_consensus(n)?;
151137
Ok(LockTime::Seconds(time))
152138
}
@@ -307,300 +293,11 @@ impl Decodable for LockTime {
307293
}
308294
}
309295

310-
/// An absolute block height, guaranteed to always contain a valid height value.
311-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
312-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313-
pub struct Height(u32);
314-
315-
impl Height {
316-
/// Height zero
317-
pub const ZERO: Self = Height(0);
318-
319-
/// Constructs a new block height.
320-
///
321-
/// # Errors
322-
///
323-
/// If `n` does not represent a block height value (see documentation on [`LockTime`]).
324-
///
325-
/// # Examples
326-
/// ```rust
327-
/// use elements::locktime::Height;
328-
///
329-
/// let h: u32 = 741521;
330-
/// let height = Height::from_consensus(h).expect("invalid height value");
331-
/// assert_eq!(height.to_consensus_u32(), h);
332-
/// ```
333-
#[inline]
334-
pub fn from_consensus(n: u32) -> Result<Height, Error> {
335-
if is_block_height(n) {
336-
Ok(Self(n))
337-
} else {
338-
Err(ConversionError::invalid_height(n).into())
339-
}
340-
}
341-
342-
/// Converts this `Height` to its inner `u32` value.
343-
///
344-
/// # Examples
345-
/// ```rust
346-
/// use elements::LockTime;
347-
///
348-
/// let n_lock_time: u32 = 741521;
349-
/// let lock_time = LockTime::from_consensus(n_lock_time);
350-
/// assert!(lock_time.is_block_height());
351-
/// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
352-
#[inline]
353-
pub fn to_consensus_u32(self) -> u32 {
354-
self.0
355-
}
356-
}
357-
358-
impl fmt::Display for Height {
359-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360-
fmt::Display::fmt(&self.0, f)
361-
}
362-
}
363-
364-
impl FromStr for Height {
365-
type Err = Error;
366-
367-
fn from_str(s: &str) -> Result<Self, Self::Err> {
368-
let n = parse::int(s)?;
369-
Height::from_consensus(n)
370-
}
371-
}
372-
373-
impl TryFrom<&str> for Height {
374-
type Error = Error;
375-
376-
fn try_from(s: &str) -> Result<Self, Self::Error> {
377-
let n = parse::int(s)?;
378-
Height::from_consensus(n)
379-
}
380-
}
381-
382-
impl TryFrom<String> for Height {
383-
type Error = Error;
384-
385-
fn try_from(s: String) -> Result<Self, Self::Error> {
386-
let n = parse::int(s)?;
387-
Height::from_consensus(n)
388-
}
389-
}
390-
391-
/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
392-
///
393-
/// Note that there is no manipulation of the inner value during construction or when using
394-
/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x -
395-
/// threshold) seconds since epoch'.
396-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
397-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
398-
pub struct Time(u32);
399-
400-
impl Time {
401-
/// Constructs a new block time.
402-
///
403-
/// # Errors
404-
///
405-
/// If `n` does not encode a UNIX time stamp (see documentation on [`LockTime`]).
406-
///
407-
/// # Examples
408-
/// ```rust
409-
/// use elements::locktime::Time;
410-
///
411-
/// let t: u32 = 1653195600; // May 22nd, 5am UTC.
412-
/// let time = Time::from_consensus(t).expect("invalid time value");
413-
/// assert_eq!(time.to_consensus_u32(), t);
414-
/// ```
415-
#[inline]
416-
pub fn from_consensus(n: u32) -> Result<Time, Error> {
417-
if is_block_time(n) {
418-
Ok(Self(n))
419-
} else {
420-
Err(ConversionError::invalid_time(n).into())
421-
}
422-
}
423-
424-
/// Converts this `Time` to its inner `u32` value.
425-
///
426-
/// # Examples
427-
/// ```rust
428-
/// use elements::LockTime;
429-
///
430-
/// let n_lock_time: u32 = 1653195600; // May 22nd, 5am UTC.
431-
/// let lock_time = LockTime::from_consensus(n_lock_time);
432-
/// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
433-
/// ```
434-
#[inline]
435-
pub fn to_consensus_u32(self) -> u32 {
436-
self.0
437-
}
438-
}
439-
440-
impl fmt::Display for Time {
441-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
442-
fmt::Display::fmt(&self.0, f)
443-
}
444-
}
445-
446-
impl FromStr for Time {
447-
type Err = Error;
448-
449-
fn from_str(s: &str) -> Result<Self, Self::Err> {
450-
let n = parse::int(s)?;
451-
Time::from_consensus(n)
452-
}
453-
}
454-
455-
impl TryFrom<&str> for Time {
456-
type Error = Error;
457-
458-
fn try_from(s: &str) -> Result<Self, Self::Error> {
459-
let n = parse::int(s)?;
460-
Time::from_consensus(n)
461-
}
462-
}
463-
464-
impl TryFrom<String> for Time {
465-
type Error = Error;
466-
467-
fn try_from(s: String) -> Result<Self, Self::Error> {
468-
let n = parse::int(s)?;
469-
Time::from_consensus(n)
470-
}
471-
}
472-
473296
/// Returns true if `n` is a block height i.e., less than 500,000,000.
474297
fn is_block_height(n: u32) -> bool {
475298
n < LOCK_TIME_THRESHOLD
476299
}
477300

478-
/// Returns true if `n` is a UNIX timestamp i.e., greater than or equal to 500,000,000.
479-
fn is_block_time(n: u32) -> bool {
480-
n >= LOCK_TIME_THRESHOLD
481-
}
482-
483-
/// Catchall type for errors that relate to time locks.
484-
#[derive(Debug, Clone, PartialEq, Eq)]
485-
#[non_exhaustive]
486-
pub enum Error {
487-
/// An error occurred while converting a `u32` to a lock time variant.
488-
Conversion(ConversionError),
489-
/// An error occurred while operating on lock times.
490-
Operation(OperationError),
491-
/// An error occurred while parsing a string into an `u32`.
492-
Parse(ParseIntError),
493-
}
494-
495-
impl fmt::Display for Error {
496-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497-
match *self {
498-
Self::Conversion(ref e) => write_err!(f, "error converting lock time value"; e),
499-
Self::Operation(ref e) => write_err!(f, "error during lock time operation"; e),
500-
Self::Parse(ref e) => write_err!(f, "failed to parse lock time from string"; e),
501-
}
502-
}
503-
}
504-
505-
impl std::error::Error for Error {
506-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
507-
match *self {
508-
Self::Conversion(ref e) => Some(e),
509-
Self::Operation(ref e) => Some(e),
510-
Self::Parse(ref e) => Some(e),
511-
}
512-
}
513-
}
514-
515-
impl From<ConversionError> for Error {
516-
fn from(e: ConversionError) -> Self {
517-
Error::Conversion(e)
518-
}
519-
}
520-
521-
impl From<OperationError> for Error {
522-
fn from(e: OperationError) -> Self {
523-
Error::Operation(e)
524-
}
525-
}
526-
527-
impl From<ParseIntError> for Error {
528-
fn from(e: ParseIntError) -> Self {
529-
Error::Parse(e)
530-
}
531-
}
532-
533-
/// An error that occurs when converting a `u32` to a lock time variant.
534-
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
535-
pub struct ConversionError {
536-
/// The expected timelock unit, height (blocks) or time (seconds).
537-
unit: LockTimeUnit,
538-
/// The invalid input value.
539-
input: u32,
540-
}
541-
542-
impl ConversionError {
543-
/// Constructs a `ConversionError` from an invalid `n` when expecting a height value.
544-
fn invalid_height(n: u32) -> Self {
545-
Self {
546-
unit: LockTimeUnit::Blocks,
547-
input: n,
548-
}
549-
}
550-
551-
/// Constructs a `ConversionError` from an invalid `n` when expecting a time value.
552-
fn invalid_time(n: u32) -> Self {
553-
Self {
554-
unit: LockTimeUnit::Seconds,
555-
input: n,
556-
}
557-
}
558-
}
559-
560-
impl fmt::Display for ConversionError {
561-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
562-
write!(f, "invalid lock time value {}, {}", self.input, self.unit)
563-
}
564-
}
565-
566-
impl std::error::Error for ConversionError {}
567-
568-
/// Describes the two types of locking, lock-by-blockheight and lock-by-blocktime.
569-
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
570-
enum LockTimeUnit {
571-
/// Lock by blockheight.
572-
Blocks,
573-
/// Lock by blocktime.
574-
Seconds,
575-
}
576-
577-
impl fmt::Display for LockTimeUnit {
578-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579-
match *self {
580-
Self::Blocks => write!(f, "expected lock-by-blockheight (must be < {})", LOCK_TIME_THRESHOLD),
581-
Self::Seconds => write!(f, "expected lock-by-blocktime (must be >= {})", LOCK_TIME_THRESHOLD),
582-
}
583-
}
584-
}
585-
586-
/// Errors than occur when operating on lock times.
587-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
588-
#[non_exhaustive]
589-
pub enum OperationError {
590-
/// Cannot compare different lock time units (height vs time).
591-
InvalidComparison,
592-
}
593-
594-
impl fmt::Display for OperationError {
595-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
596-
match *self {
597-
Self::InvalidComparison => f.write_str("cannot compare different lock units (height vs time)"),
598-
}
599-
}
600-
}
601-
602-
impl std::error::Error for OperationError {}
603-
604301
#[cfg(test)]
605302
mod tests {
606303
use super::*;

0 commit comments

Comments
 (0)