primitives: Enable pedantic lints

Enable all the pedantic lints and fix warnings.

Notable items:

- `enum_glob_used` import types with a single character alias
- `doc_markdown`: add a whitelist that includes SegWit and OpenSSL
This commit is contained in:
Tobin C. Harding 2025-03-03 15:06:44 +11:00
parent d96d0f72b5
commit df500e9b71
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
12 changed files with 301 additions and 177 deletions

View File

@ -1,3 +1,4 @@
msrv = "1.63.0" msrv = "1.63.0"
too-many-arguments-threshold = 13 too-many-arguments-threshold = 13
avoid-breaking-exported-api = false avoid-breaking-exported-api = false
doc-valid-idents = ["SegWit", "OpenSSL"]

View File

@ -40,3 +40,131 @@ rustdoc-args = ["--cfg", "docsrs"]
[lints.rust] [lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(mutate)'] } unexpected_cfgs = { level = "deny", check-cfg = ['cfg(mutate)'] }
[lints.clippy]
# Exclude lints we don't think are valuable.
needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
manual_range_contains = "allow" # More readable than clippy's format.
# Exhaustive list of pedantic clippy lints
assigning_clones = "warn"
bool_to_int_with_if = "warn"
borrow_as_ptr = "warn"
case_sensitive_file_extension_comparisons = "warn"
cast_lossless = "warn"
cast_possible_truncation = "allow" # All casts should include a code comment (except test code).
cast_possible_wrap = "allow" # Same as above re code comment.
cast_precision_loss = "warn"
cast_ptr_alignment = "warn"
cast_sign_loss = "allow" # All casts should include a code comment (except in test code).
checked_conversions = "warn"
cloned_instead_of_copied = "warn"
copy_iterator = "warn"
default_trait_access = "warn"
doc_link_with_quotes = "warn"
doc_markdown = "warn"
empty_enum = "warn"
enum_glob_use = "warn"
expl_impl_clone_on_copy = "warn"
explicit_deref_methods = "warn"
explicit_into_iter_loop = "warn"
explicit_iter_loop = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
float_cmp = "allow" # Bitcoin floats are typically limited to 8 decimal places and we want them exact.
fn_params_excessive_bools = "warn"
from_iter_instead_of_collect = "warn"
if_not_else = "warn"
ignored_unit_patterns = "warn"
implicit_clone = "warn"
implicit_hasher = "warn"
inconsistent_struct_constructor = "warn"
index_refutable_slice = "warn"
inefficient_to_string = "warn"
inline_always = "warn"
into_iter_without_iter = "warn"
invalid_upcast_comparisons = "warn"
items_after_statements = "warn"
iter_filter_is_ok = "warn"
iter_filter_is_some = "warn"
iter_not_returning_iterator = "warn"
iter_without_into_iter = "warn"
large_digit_groups = "warn"
large_futures = "warn"
large_stack_arrays = "warn"
large_types_passed_by_value = "warn"
linkedlist = "warn"
macro_use_imports = "warn"
manual_assert = "warn"
manual_instant_elapsed = "warn"
manual_is_power_of_two = "warn"
manual_is_variant_and = "warn"
manual_let_else = "warn"
manual_ok_or = "warn"
manual_string_new = "warn"
many_single_char_names = "warn"
map_unwrap_or = "warn"
match_bool = "allow" # Adds extra indentation and LOC.
match_on_vec_items = "warn"
match_same_arms = "allow" # Collapses things that are conceptually unrelated to each other.
match_wild_err_arm = "warn"
match_wildcard_for_single_variants = "warn"
maybe_infinite_iter = "warn"
mismatching_type_param_order = "warn"
missing_errors_doc = "allow" # TODO: Write errors section in docs.
missing_fields_in_debug = "warn"
missing_panics_doc = "warn"
must_use_candidate = "allow" # Useful for audit but many false positives.
mut_mut = "warn"
naive_bytecount = "warn"
needless_bitwise_bool = "warn"
needless_continue = "warn"
needless_for_each = "warn"
needless_pass_by_value = "warn"
needless_raw_string_hashes = "warn"
no_effect_underscore_binding = "warn"
no_mangle_with_rust_abi = "warn"
option_as_ref_cloned = "warn"
option_option = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
pub_underscore_fields = "warn"
range_minus_one = "warn"
range_plus_one = "warn"
redundant_closure_for_method_calls = "warn"
redundant_else = "warn"
ref_as_ptr = "warn"
ref_binding_to_reference = "warn"
ref_option = "warn"
ref_option_ref = "warn"
return_self_not_must_use = "warn"
same_functions_in_if_condition = "warn"
semicolon_if_nothing_returned = "warn"
should_panic_without_expect = "warn"
similar_names = "allow" # Too many (subjectively) false positives.
single_char_pattern = "warn"
single_match_else = "warn"
stable_sort_primitive = "warn"
str_split_at_newline = "warn"
string_add_assign = "warn"
struct_excessive_bools = "warn"
struct_field_names = "allow" # TODO: Triggers warning for `witness_elements`.
too_many_lines = "warn"
transmute_ptr_to_ptr = "warn"
trivially_copy_pass_by_ref = "warn"
unchecked_duration_subtraction = "warn"
unicode_not_nfc = "warn"
uninlined_format_args = "allow" # This is a subjective style choice.
unnecessary_box_returns = "warn"
unnecessary_join = "warn"
unnecessary_literal_bound = "warn"
unnecessary_wraps = "warn"
unnested_or_patterns = "allow" # TODO
unreadable_literal = "warn"
unsafe_derive_deserialize = "warn"
unused_async = "warn"
unused_self = "warn"
used_underscore_binding = "warn"
used_underscore_items = "warn"
verbose_bit_mask = "warn"
wildcard_imports = "warn"
zero_sized_map_values = "warn"

View File

@ -281,7 +281,7 @@ impl Version {
/// ///
/// A block is signalling for a soft fork under BIP-9 if the first 3 bits are `001` and /// A block is signalling for a soft fork under BIP-9 if the first 3 bits are `001` and
/// the version bit for the specific soft fork is toggled on. /// the version bit for the specific soft fork is toggled on.
pub fn is_signalling_soft_fork(&self, bit: u8) -> bool { pub fn is_signalling_soft_fork(self, bit: u8) -> bool {
// Only bits [0, 28] inclusive are used for signalling. // Only bits [0, 28] inclusive are used for signalling.
if bit > 28 { if bit > 28 {
return false; return false;
@ -362,36 +362,36 @@ mod tests {
#[test] #[test]
fn version_is_not_signalling_with_invalid_bit() { fn version_is_not_signalling_with_invalid_bit() {
let arbitrary_version = Version::from_consensus(1234567890); let arbitrary_version = Version::from_consensus(1_234_567_890);
// The max bit number to signal is 28. // The max bit number to signal is 28.
assert!(!Version::is_signalling_soft_fork(&arbitrary_version, 29)); assert!(!Version::is_signalling_soft_fork(arbitrary_version, 29));
} }
#[test] #[test]
fn version_is_not_signalling_when_use_version_bit_not_set() { fn version_is_not_signalling_when_use_version_bit_not_set() {
let version = Version::from_consensus(0b01000000000000000000000000000000); let version = Version::from_consensus(0b0100_0000_0000_0000_0000_0000_0000_0000);
// Top three bits must be 001 to signal. // Top three bits must be 001 to signal.
assert!(!Version::is_signalling_soft_fork(&version, 1)); assert!(!Version::is_signalling_soft_fork(version, 1));
} }
#[test] #[test]
fn version_is_signalling() { fn version_is_signalling() {
let version = Version::from_consensus(0b00100000000000000000000000000010); let version = Version::from_consensus(0b0010_0000_0000_0000_0000_0000_0000_0010);
assert!(Version::is_signalling_soft_fork(&version, 1)); assert!(Version::is_signalling_soft_fork(version, 1));
let version = Version::from_consensus(0b00110000000000000000000000000000); let version = Version::from_consensus(0b0011_0000_0000_0000_0000_0000_0000_0000);
assert!(Version::is_signalling_soft_fork(&version, 28)); assert!(Version::is_signalling_soft_fork(version, 28));
} }
#[test] #[test]
fn version_is_not_signalling() { fn version_is_not_signalling() {
let version = Version::from_consensus(0b00100000000000000000000000000010); let version = Version::from_consensus(0b0010_0000_0000_0000_0000_0000_0000_0010);
assert!(!Version::is_signalling_soft_fork(&version, 0)); assert!(!Version::is_signalling_soft_fork(version, 0));
} }
#[test] #[test]
fn version_to_consensus() { fn version_to_consensus() {
let version = Version::from_consensus(1234567890); let version = Version::from_consensus(1_234_567_890);
assert_eq!(version.to_consensus(), 1234567890); assert_eq!(version.to_consensus(), 1_234_567_890);
} }
// Check that the size of the header consensus serialization matches the const SIZE value // Check that the size of the header consensus serialization matches the const SIZE value

View File

@ -16,12 +16,6 @@
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(deprecated_in_future)] #![warn(deprecated_in_future)]
#![doc(test(attr(warn(unused))))] #![doc(test(attr(warn(unused))))]
// Pedantic lints that we enforce.
// #![warn(clippy::must_use_candidate)]
#![warn(clippy::return_self_not_must_use)]
// Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
extern crate alloc; extern crate alloc;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
//! Provides type [`LockTime`] that implements the logic around nLockTime/OP_CHECKLOCKTIMEVERIFY. //! Provides type [`LockTime`] that implements the logic around `nLockTime`/`OP_CHECKLOCKTIMEVERIFY`.
//! //!
//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by //! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
//! whether `LockTime < LOCKTIME_THRESHOLD`. //! whether `LockTime < LOCKTIME_THRESHOLD`.
@ -22,7 +22,7 @@ pub use units::locktime::absolute::{ConversionError, Height, ParseHeightError, P
/// since epoch). /// since epoch).
/// ///
/// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`Transaction::lock_time`] /// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`Transaction::lock_time`]
/// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`. /// in this library) and also for the argument to opcode `OP_CHECKLOCKTIMEVERIFY`.
/// ///
/// ### Note on ordering /// ### Note on ordering
/// ///
@ -42,13 +42,13 @@ pub use units::locktime::absolute::{ConversionError, Height, ParseHeightError, P
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bitcoin_primitives::absolute::{self, LockTime::*}; /// use bitcoin_primitives::absolute::{self, LockTime as L};
/// # let n = absolute::LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY /// # let n = absolute::LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
/// # let lock_time = absolute::LockTime::from_consensus(741521); // nLockTime /// # let lock_time = absolute::LockTime::from_consensus(741521); // nLockTime
/// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use: /// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
/// let _is_satisfied = match (n, lock_time) { /// let _is_satisfied = match (n, lock_time) {
/// (Blocks(n), Blocks(lock_time)) => n <= lock_time, /// (L::Blocks(n), L::Blocks(lock_time)) => n <= lock_time,
/// (Seconds(n), Seconds(lock_time)) => n <= lock_time, /// (L::Seconds(n), L::Seconds(lock_time)) => n <= lock_time,
/// _ => panic!("handle invalid comparison error"), /// _ => panic!("handle invalid comparison error"),
/// }; /// };
/// ``` /// ```
@ -126,7 +126,7 @@ impl LockTime {
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
} }
/// Constructs a new `LockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY. /// Constructs a new `LockTime` from an `nLockTime` value or the argument to `OP_CHEKCLOCKTIMEVERIFY`.
/// ///
/// # Examples /// # Examples
/// ///
@ -138,6 +138,7 @@ impl LockTime {
/// let lock_time = absolute::LockTime::from_consensus(n_lock_time); /// let lock_time = absolute::LockTime::from_consensus(n_lock_time);
/// assert_eq!(lock_time.to_consensus_u32(), n_lock_time); /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
#[inline] #[inline]
#[allow(clippy::missing_panics_doc)]
pub fn from_consensus(n: u32) -> Self { pub fn from_consensus(n: u32) -> Self {
if units::locktime::absolute::is_block_height(n) { if units::locktime::absolute::is_block_height(n) {
Self::Blocks(Height::from_consensus(n).expect("n is valid")) Self::Blocks(Height::from_consensus(n).expect("n is valid"))
@ -197,7 +198,7 @@ impl LockTime {
/// Returns true if both lock times use the same unit i.e., both height based or both time based. /// Returns true if both lock times use the same unit i.e., both height based or both time based.
#[inline] #[inline]
pub const fn is_same_unit(&self, other: LockTime) -> bool { pub const fn is_same_unit(self, other: LockTime) -> bool {
matches!( matches!(
(self, other), (self, other),
(LockTime::Blocks(_), LockTime::Blocks(_)) (LockTime::Blocks(_), LockTime::Blocks(_))
@ -207,11 +208,11 @@ impl LockTime {
/// Returns true if this lock time value is a block height. /// Returns true if this lock time value is a block height.
#[inline] #[inline]
pub const fn is_block_height(&self) -> bool { matches!(*self, LockTime::Blocks(_)) } pub const fn is_block_height(self) -> bool { matches!(self, LockTime::Blocks(_)) }
/// Returns true if this lock time value is a block time (UNIX timestamp). /// Returns true if this lock time value is a block time (UNIX timestamp).
#[inline] #[inline]
pub const fn is_block_time(&self) -> bool { !self.is_block_height() } pub const fn is_block_time(self) -> bool { !self.is_block_height() }
/// Returns true if this timelock constraint is satisfied by the respective `height`/`time`. /// Returns true if this timelock constraint is satisfied by the respective `height`/`time`.
/// ///
@ -219,7 +220,7 @@ impl LockTime {
/// blocktime based lock it is checked against `time`. /// blocktime based lock it is checked against `time`.
/// ///
/// A 'timelock constraint' refers to the `n` from `n OP_CHEKCLOCKTIMEVERIFY`, this constraint /// A 'timelock constraint' refers to the `n` from `n OP_CHEKCLOCKTIMEVERIFY`, this constraint
/// is satisfied if a transaction with nLockTime ([`Transaction::lock_time`]) set to /// is satisfied if a transaction with `nLockTime` ([`Transaction::lock_time`]) set to
/// `height`/`time` is valid. /// `height`/`time` is valid.
/// ///
/// # Examples /// # Examples
@ -236,12 +237,12 @@ impl LockTime {
/// } /// }
/// ```` /// ````
#[inline] #[inline]
pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool { pub fn is_satisfied_by(self, height: Height, time: Time) -> bool {
use LockTime::*; use LockTime as L;
match *self { match self {
Blocks(n) => n <= height, L::Blocks(n) => n <= height,
Seconds(n) => n <= time, L::Seconds(n) => n <= time,
} }
} }
@ -265,18 +266,18 @@ impl LockTime {
/// assert!(lock_time.is_implied_by(check)); /// assert!(lock_time.is_implied_by(check));
/// ``` /// ```
#[inline] #[inline]
pub fn is_implied_by(&self, other: LockTime) -> bool { pub fn is_implied_by(self, other: LockTime) -> bool {
use LockTime::*; use LockTime as L;
match (*self, other) { match (self, other) {
(Blocks(this), Blocks(other)) => this <= other, (L::Blocks(this), L::Blocks(other)) => this <= other,
(Seconds(this), Seconds(other)) => this <= other, (L::Seconds(this), L::Seconds(other)) => this <= other,
_ => false, // Not the same units. _ => false, // Not the same units.
} }
} }
/// Returns the inner `u32` value. This is the value used when creating this `LockTime` /// Returns the inner `u32` value. This is the value used when creating this `LockTime`
/// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime. /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or `nLockTime`.
/// ///
/// # Warning /// # Warning
/// ///
@ -287,13 +288,13 @@ impl LockTime {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use bitcoin_primitives::absolute::{self, LockTime::*}; /// use bitcoin_primitives::absolute::{self, LockTime as L};
/// # let n = absolute::LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY /// # let n = absolute::LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
/// # let lock_time = absolute::LockTime::from_consensus(741521 + 1); // nLockTime /// # let lock_time = absolute::LockTime::from_consensus(741521 + 1); // nLockTime
/// ///
/// let _is_satisfied = match (n, lock_time) { /// let _is_satisfied = match (n, lock_time) {
/// (Blocks(n), Blocks(lock_time)) => n <= lock_time, /// (L::Blocks(n), L::Blocks(lock_time)) => n <= lock_time,
/// (Seconds(n), Seconds(lock_time)) => n <= lock_time, /// (L::Seconds(n), L::Seconds(lock_time)) => n <= lock_time,
/// _ => panic!("invalid comparison"), /// _ => panic!("invalid comparison"),
/// }; /// };
/// ///
@ -324,28 +325,28 @@ impl From<Time> for LockTime {
impl fmt::Debug for LockTime { impl fmt::Debug for LockTime {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LockTime::*; use LockTime as L;
match *self { match *self {
Blocks(ref h) => write!(f, "{} blocks", h), L::Blocks(ref h) => write!(f, "{} blocks", h),
Seconds(ref t) => write!(f, "{} seconds", t), L::Seconds(ref t) => write!(f, "{} seconds", t),
} }
} }
} }
impl fmt::Display for LockTime { impl fmt::Display for LockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LockTime::*; use LockTime as L;
if f.alternate() { if f.alternate() {
match *self { match *self {
Blocks(ref h) => write!(f, "block-height {}", h), L::Blocks(ref h) => write!(f, "block-height {}", h),
Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t), L::Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
} }
} else { } else {
match *self { match *self {
Blocks(ref h) => fmt::Display::fmt(h, f), L::Blocks(ref h) => fmt::Display::fmt(h, f),
Seconds(ref t) => fmt::Display::fmt(t, f), L::Seconds(ref t) => fmt::Display::fmt(t, f),
} }
} }
} }
@ -404,7 +405,7 @@ mod tests {
#[test] #[test]
fn display_and_alternate() { fn display_and_alternate() {
let lock_by_height = LockTime::from_consensus(741521); let lock_by_height = LockTime::from_consensus(741_521);
let s = format!("{}", lock_by_height); let s = format!("{}", lock_by_height);
assert_eq!(&s, "741521"); assert_eq!(&s, "741521");
@ -415,25 +416,25 @@ mod tests {
#[test] #[test]
fn lock_time_from_hex_lower() { fn lock_time_from_hex_lower() {
let lock_by_time = LockTime::from_hex("0x6289c350").unwrap(); let lock_by_time = LockTime::from_hex("0x6289c350").unwrap();
assert_eq!(lock_by_time, LockTime::from_consensus(0x6289C350)); assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
} }
#[test] #[test]
fn lock_time_from_hex_upper() { fn lock_time_from_hex_upper() {
let lock_by_time = LockTime::from_hex("0X6289C350").unwrap(); let lock_by_time = LockTime::from_hex("0X6289C350").unwrap();
assert_eq!(lock_by_time, LockTime::from_consensus(0x6289C350)); assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
} }
#[test] #[test]
fn lock_time_from_unprefixed_hex_lower() { fn lock_time_from_unprefixed_hex_lower() {
let lock_by_time = LockTime::from_unprefixed_hex("6289c350").unwrap(); let lock_by_time = LockTime::from_unprefixed_hex("6289c350").unwrap();
assert_eq!(lock_by_time, LockTime::from_consensus(0x6289C350)); assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
} }
#[test] #[test]
fn lock_time_from_unprefixed_hex_upper() { fn lock_time_from_unprefixed_hex_upper() {
let lock_by_time = LockTime::from_unprefixed_hex("6289C350").unwrap(); let lock_by_time = LockTime::from_unprefixed_hex("6289C350").unwrap();
assert_eq!(lock_by_time, LockTime::from_consensus(0x6289C350)); assert_eq!(lock_by_time, LockTime::from_consensus(0x6289_C350));
} }
#[test] #[test]
@ -450,7 +451,7 @@ mod tests {
assert!(lock_by_height.is_block_height()); assert!(lock_by_height.is_block_height());
assert!(!lock_by_height.is_block_time()); assert!(!lock_by_height.is_block_time());
let t: u32 = 1653195600; // May 22nd, 5am UTC. let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
let lock_by_time = LockTime::from_consensus(t); let lock_by_time = LockTime::from_consensus(t);
assert!(!lock_by_time.is_block_height()); assert!(!lock_by_time.is_block_height());
@ -459,7 +460,7 @@ mod tests {
// Test is_same_unit() logic // Test is_same_unit() logic
assert!(lock_by_height.is_same_unit(LockTime::from_consensus(800_000))); assert!(lock_by_height.is_same_unit(LockTime::from_consensus(800_000)));
assert!(!lock_by_height.is_same_unit(lock_by_time)); assert!(!lock_by_height.is_same_unit(lock_by_time));
assert!(lock_by_time.is_same_unit(LockTime::from_consensus(1653282000))); assert!(lock_by_time.is_same_unit(LockTime::from_consensus(1_653_282_000)));
assert!(!lock_by_time.is_same_unit(lock_by_height)); assert!(!lock_by_time.is_same_unit(lock_by_height));
} }
@ -471,7 +472,7 @@ mod tests {
let height_above = Height::from_consensus(800_000).expect("failed to parse height"); let height_above = Height::from_consensus(800_000).expect("failed to parse height");
let height_below = Height::from_consensus(700_000).expect("failed to parse height"); let height_below = Height::from_consensus(700_000).expect("failed to parse height");
let t: u32 = 1653195600; // May 22nd, 5am UTC. let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
let time = Time::from_consensus(t).expect("invalid time value"); let time = Time::from_consensus(t).expect("invalid time value");
assert!(lock_by_height.is_satisfied_by(height_same, time)); assert!(lock_by_height.is_satisfied_by(height_same, time));
@ -481,11 +482,11 @@ mod tests {
#[test] #[test]
fn satisfied_by_time() { fn satisfied_by_time() {
let lock_by_time = LockTime::from_consensus(1653195600); // May 22nd 2022, 5am UTC. let lock_by_time = LockTime::from_consensus(1_653_195_600); // May 22nd 2022, 5am UTC.
let time_same = Time::from_consensus(1653195600).expect("May 22nd 2022, 5am UTC"); let time_same = Time::from_consensus(1_653_195_600).expect("May 22nd 2022, 5am UTC");
let time_after = Time::from_consensus(1653282000).expect("May 23rd 2022, 5am UTC"); let time_after = Time::from_consensus(1_653_282_000).expect("May 23rd 2022, 5am UTC");
let time_before = Time::from_consensus(1653109200).expect("May 21th 2022, 5am UTC"); let time_before = Time::from_consensus(1_653_109_200).expect("May 21th 2022, 5am UTC");
let height = Height::from_consensus(800_000).expect("failed to parse height"); let height = Height::from_consensus(800_000).expect("failed to parse height");
@ -505,17 +506,17 @@ mod tests {
#[test] #[test]
fn time_correctly_implies() { fn time_correctly_implies() {
let t: u32 = 1700000005; let t: u32 = 1_700_000_005;
let lock_by_time = LockTime::from_consensus(t); let lock_by_time = LockTime::from_consensus(t);
assert!(!lock_by_time.is_implied_by(LockTime::from_consensus(1700000004))); assert!(!lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_004)));
assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1700000005))); assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_005)));
assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1700000006))); assert!(lock_by_time.is_implied_by(LockTime::from_consensus(1_700_000_006)));
} }
#[test] #[test]
fn incorrect_units_do_not_imply() { fn incorrect_units_do_not_imply() {
let lock_by_height = LockTime::from_consensus(750_005); let lock_by_height = LockTime::from_consensus(750_005);
assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(1700000004))); assert!(!lock_by_height.is_implied_by(LockTime::from_consensus(1_700_000_004)));
} }
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
//! Provides type [`LockTime`] that implements the logic around nSequence/OP_CHECKSEQUENCEVERIFY. //! Provides type [`LockTime`] that implements the logic around `nSequence`/`OP_CHECKSEQUENCEVERIFY`.
//! //!
//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by //! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
//! whether bit 22 of the `u32` consensus value is set. //! whether bit 22 of the `u32` consensus value is set.
@ -18,7 +18,7 @@ pub use units::locktime::relative::{Height, Time, TimeOverflowError};
/// A relative lock time value, representing either a block height or time (512 second intervals). /// A relative lock time value, representing either a block height or time (512 second intervals).
/// ///
/// Used for sequence numbers (`nSequence` in Bitcoin Core and [`TxIn::sequence`] /// Used for sequence numbers (`nSequence` in Bitcoin Core and [`TxIn::sequence`]
/// in this library) and also for the argument to opcode 'OP_CHECKSEQUENCEVERIFY`. /// in this library) and also for the argument to opcode `OP_CHECKSEQUENCEVERIFY`.
/// ///
/// ### Note on ordering /// ### Note on ordering
/// ///
@ -64,7 +64,7 @@ impl LockTime {
/// The number of bytes that the locktime contributes to the size of a transaction. /// The number of bytes that the locktime contributes to the size of a transaction.
pub const SIZE: usize = 4; // Serialized length of a u32. pub const SIZE: usize = 4; // Serialized length of a u32.
/// Constructs a new `LockTime` from an nSequence value or the argument to OP_CHECKSEQUENCEVERIFY. /// Constructs a new `LockTime` from an `nSequence` value or the argument to `OP_CHECKSEQUENCEVERIFY`.
/// ///
/// This method will **not** round-trip with [`Self::to_consensus_u32`], because relative /// 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 /// locktimes only use some bits of the underlying `u32` value and discard the rest. If
@ -95,7 +95,7 @@ impl LockTime {
sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n)) sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n))
} }
/// Returns the `u32` value used to encode this locktime in an nSequence field or /// Returns the `u32` value used to encode this locktime in an `nSequence` field or
/// argument to `OP_CHECKSEQUENCEVERIFY`. /// argument to `OP_CHECKSEQUENCEVERIFY`.
/// ///
/// # Warning /// # Warning
@ -178,7 +178,7 @@ impl LockTime {
/// Returns true if both lock times use the same unit i.e., both height based or both time based. /// Returns true if both lock times use the same unit i.e., both height based or both time based.
#[inline] #[inline]
pub const fn is_same_unit(&self, other: LockTime) -> bool { pub const fn is_same_unit(self, other: LockTime) -> bool {
matches!( matches!(
(self, other), (self, other),
(LockTime::Blocks(_), LockTime::Blocks(_)) | (LockTime::Time(_), LockTime::Time(_)) (LockTime::Blocks(_), LockTime::Blocks(_)) | (LockTime::Time(_), LockTime::Time(_))
@ -187,11 +187,11 @@ impl LockTime {
/// Returns true if this lock time value is in units of block height. /// Returns true if this lock time value is in units of block height.
#[inline] #[inline]
pub const fn is_block_height(&self) -> bool { matches!(*self, LockTime::Blocks(_)) } pub const fn is_block_height(self) -> bool { matches!(self, LockTime::Blocks(_)) }
/// Returns true if this lock time value is in units of time. /// Returns true if this lock time value is in units of time.
#[inline] #[inline]
pub const fn is_block_time(&self) -> bool { !self.is_block_height() } pub const fn is_block_time(self) -> bool { !self.is_block_height() }
/// Returns true if this [`relative::LockTime`] is satisfied by either height or time. /// Returns true if this [`relative::LockTime`] is satisfied by either height or time.
/// ///
@ -211,7 +211,7 @@ impl LockTime {
/// assert!(lock.is_satisfied_by(current_height(), current_time())); /// assert!(lock.is_satisfied_by(current_height(), current_time()));
/// ``` /// ```
#[inline] #[inline]
pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool { pub fn is_satisfied_by(self, h: Height, t: Time) -> bool {
if let Ok(true) = self.is_satisfied_by_height(h) { if let Ok(true) = self.is_satisfied_by_height(h) {
true true
} else { } else {
@ -249,12 +249,12 @@ impl LockTime {
/// assert!(satisfied); /// assert!(satisfied);
/// ``` /// ```
#[inline] #[inline]
pub fn is_implied_by(&self, other: LockTime) -> bool { pub fn is_implied_by(self, other: LockTime) -> bool {
use LockTime::*; use LockTime as L;
match (*self, other) { match (self, other) {
(Blocks(this), Blocks(other)) => this.value() <= other.value(), (L::Blocks(this), L::Blocks(other)) => this.value() <= other.value(),
(Time(this), Time(other)) => this.value() <= other.value(), (L::Time(this), L::Time(other)) => this.value() <= other.value(),
_ => false, // Not the same units. _ => false, // Not the same units.
} }
} }
@ -280,7 +280,7 @@ impl LockTime {
/// # Ok::<_, bitcoin_primitives::relative::DisabledLockTimeError>(()) /// # Ok::<_, bitcoin_primitives::relative::DisabledLockTimeError>(())
/// ``` /// ```
#[inline] #[inline]
pub fn is_implied_by_sequence(&self, other: Sequence) -> bool { pub fn is_implied_by_sequence(self, other: Sequence) -> bool {
if let Ok(other) = LockTime::from_sequence(other) { if let Ok(other) = LockTime::from_sequence(other) {
self.is_implied_by(other) self.is_implied_by(other)
} else { } else {
@ -305,12 +305,12 @@ impl LockTime {
/// assert!(lock.is_satisfied_by_height(relative::Height::from(required_height + 1)).expect("a height")); /// assert!(lock.is_satisfied_by_height(relative::Height::from(required_height + 1)).expect("a height"));
/// ``` /// ```
#[inline] #[inline]
pub fn is_satisfied_by_height(&self, height: Height) -> Result<bool, IncompatibleHeightError> { pub fn is_satisfied_by_height(self, height: Height) -> Result<bool, IncompatibleHeightError> {
use LockTime::*; use LockTime as L;
match *self { match self {
Blocks(ref required_height) => Ok(required_height.value() <= height.value()), L::Blocks(ref required_height) => Ok(required_height.value() <= height.value()),
Time(time) => Err(IncompatibleHeightError { height, time }), L::Time(time) => Err(IncompatibleHeightError { height, time }),
} }
} }
@ -331,12 +331,12 @@ impl LockTime {
/// assert!(lock.is_satisfied_by_time(relative::Time::from_512_second_intervals(intervals + 10)).expect("a time")); /// assert!(lock.is_satisfied_by_time(relative::Time::from_512_second_intervals(intervals + 10)).expect("a time"));
/// ``` /// ```
#[inline] #[inline]
pub fn is_satisfied_by_time(&self, time: Time) -> Result<bool, IncompatibleTimeError> { pub fn is_satisfied_by_time(self, time: Time) -> Result<bool, IncompatibleTimeError> {
use LockTime::*; use LockTime as L;
match *self { match self {
Time(ref t) => Ok(t.value() <= time.value()), L::Time(ref t) => Ok(t.value() <= time.value()),
Blocks(height) => Err(IncompatibleTimeError { time, height }), L::Blocks(height) => Err(IncompatibleTimeError { time, height }),
} }
} }
} }
@ -353,17 +353,17 @@ impl From<Time> for LockTime {
impl fmt::Display for LockTime { impl fmt::Display for LockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LockTime::*; use LockTime as L;
if f.alternate() { if f.alternate() {
match *self { match *self {
Blocks(ref h) => write!(f, "block-height {}", h), L::Blocks(ref h) => write!(f, "block-height {}", h),
Time(ref t) => write!(f, "block-time {} (512 second intervals)", t), L::Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
} }
} else { } else {
match *self { match *self {
Blocks(ref h) => fmt::Display::fmt(h, f), L::Blocks(ref h) => fmt::Display::fmt(h, f),
Time(ref t) => fmt::Display::fmt(t, f), L::Time(ref t) => fmt::Display::fmt(t, f),
} }
} }
} }

View File

@ -31,6 +31,7 @@ pub struct Opcode {
code: u8, code: u8,
} }
#[allow(clippy::wildcard_imports)]
use self::all::*; use self::all::*;
macro_rules! all_opcodes { macro_rules! all_opcodes {
@ -41,7 +42,7 @@ macro_rules! all_opcodes {
/// get all the `OP_FOO` opcodes without getting other types defined in `opcodes` (e.g. `Opcode`, `Class`). /// get all the `OP_FOO` opcodes without getting other types defined in `opcodes` (e.g. `Opcode`, `Class`).
/// ///
/// This module is guaranteed to never contain anything except opcode constants and all opcode /// This module is guaranteed to never contain anything except opcode constants and all opcode
/// constants are guaranteed to begin with OP_. /// constants are guaranteed to begin with `OP_`.
pub mod all { pub mod all {
use super::Opcode; use super::Opcode;
$( $(
@ -52,13 +53,13 @@ macro_rules! all_opcodes {
/// Push an empty array onto the stack. /// Push an empty array onto the stack.
pub static OP_0: Opcode = OP_PUSHBYTES_0; pub static OP_0: Opcode = OP_PUSHBYTES_0;
/// Empty stack is also FALSE. /// Empty stack is also `FALSE`.
pub static OP_FALSE: Opcode = OP_PUSHBYTES_0; pub static OP_FALSE: Opcode = OP_PUSHBYTES_0;
/// Number 1 is also TRUE. /// Number 1 is also TRUE.
pub static OP_TRUE: Opcode = OP_PUSHNUM_1; pub static OP_TRUE: Opcode = OP_PUSHNUM_1;
/// Previously called OP_NOP2. /// Previously called `OP_NOP2`.
pub static OP_NOP2: Opcode = OP_CLTV; pub static OP_NOP2: Opcode = OP_CLTV;
/// Previously called OP_NOP3. /// Previously called `OP_NOP3`.
pub static OP_NOP3: Opcode = OP_CSV; pub static OP_NOP3: Opcode = OP_CSV;
impl fmt::Display for Opcode { impl fmt::Display for Opcode {
@ -402,10 +403,10 @@ impl Opcode {
// 16 opcodes of PushNum class // 16 opcodes of PushNum class
(op, _) if op.code >= OP_PUSHNUM_1.code && op.code <= OP_PUSHNUM_16.code => (op, _) if op.code >= OP_PUSHNUM_1.code && op.code <= OP_PUSHNUM_16.code =>
Class::PushNum(1 + self.code as i32 - OP_PUSHNUM_1.code as i32), Class::PushNum(1 + i32::from(self.code) - i32::from(OP_PUSHNUM_1.code)),
// 76 opcodes of PushBytes class // 76 opcodes of PushBytes class
(op, _) if op.code <= OP_PUSHBYTES_75.code => Class::PushBytes(self.code as u32), (op, _) if op.code <= OP_PUSHBYTES_75.code => Class::PushBytes(u32::from(self.code)),
// opcodes of Ordinary class: 61 for Legacy and 60 for TapScript context // opcodes of Ordinary class: 61 for Legacy and 60 for TapScript context
(_, _) => Class::Ordinary(Ordinary::with(self)), (_, _) => Class::Ordinary(Ordinary::with(self)),
@ -640,6 +641,7 @@ mod tests {
} }
#[test] #[test]
#[allow(clippy::too_many_lines)] // This is fine, we never need to read it.
fn str_roundtrip() { fn str_roundtrip() {
let mut unique = HashSet::new(); let mut unique = HashSet::new();
roundtrip!(unique, OP_PUSHBYTES_0); roundtrip!(unique, OP_PUSHBYTES_0);

View File

@ -52,9 +52,9 @@ mod tests {
#[test] #[test]
fn compact_target_ordering() { fn compact_target_ordering() {
let lower = CompactTarget::from_consensus(0x1d00fffe); let lower = CompactTarget::from_consensus(0x1d00_fffe);
let lower_copy = CompactTarget::from_consensus(0x1d00fffe); let lower_copy = CompactTarget::from_consensus(0x1d00_fffe);
let higher = CompactTarget::from_consensus(0x1d00ffff); let higher = CompactTarget::from_consensus(0x1d00_ffff);
assert!(lower < higher); assert!(lower < higher);
assert!(lower == lower_copy); assert!(lower == lower_copy);
@ -62,9 +62,9 @@ mod tests {
#[test] #[test]
fn compact_target_formatting() { fn compact_target_formatting() {
let compact_target = CompactTarget::from_consensus(0x1d00ffff); let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
assert_eq!(format!("{:x}", compact_target), "1d00ffff"); assert_eq!(format!("{:x}", compact_target), "1d00ffff");
assert_eq!(format!("{:X}", compact_target), "1D00FFFF"); assert_eq!(format!("{:X}", compact_target), "1D00FFFF");
assert_eq!(compact_target.to_consensus(), 0x1d00ffff); assert_eq!(compact_target.to_consensus(), 0x1d00_ffff);
} }
} }

View File

@ -13,6 +13,7 @@ use hashes::{hash160, sha256};
use hex::DisplayHex; use hex::DisplayHex;
use internals::script::{self, PushDataLenLen}; use internals::script::{self, PushDataLenLen};
#[allow(clippy::wildcard_imports)]
use crate::opcodes::all::*; use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode}; use crate::opcodes::{self, Opcode};
use crate::prelude::rc::Rc; use crate::prelude::rc::Rc;
@ -522,14 +523,6 @@ impl<'de> serde::Deserialize<'de> for &'de Script {
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
if deserializer.is_human_readable() {
use crate::serde::de::Error;
return Err(D::Error::custom(
"deserialization of `&Script` from human-readable formats is not possible",
));
}
struct Visitor; struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor { impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = &'de Script; type Value = &'de Script;
@ -545,6 +538,15 @@ impl<'de> serde::Deserialize<'de> for &'de Script {
Ok(Script::from_bytes(v)) Ok(Script::from_bytes(v))
} }
} }
if deserializer.is_human_readable() {
use crate::serde::de::Error;
return Err(D::Error::custom(
"deserialization of `&Script` from human-readable formats is not possible",
));
}
deserializer.deserialize_bytes(Visitor) deserializer.deserialize_bytes(Visitor)
} }
} }

View File

@ -36,7 +36,7 @@ impl Sequence {
/// The maximum allowable sequence number. /// The maximum allowable sequence number.
/// ///
/// The sequence number that disables replace-by-fee, absolute lock time and relative lock time. /// The sequence number that disables replace-by-fee, absolute lock time and relative lock time.
pub const MAX: Self = Sequence(0xFFFFFFFF); pub const MAX: Self = Sequence(0xFFFF_FFFF);
/// Zero value sequence. /// Zero value sequence.
/// ///
/// This sequence number enables replace-by-fee and absolute lock time. /// This sequence number enables replace-by-fee and absolute lock time.
@ -49,12 +49,12 @@ impl Sequence {
/// The sequence number that enables replace-by-fee and absolute lock time but /// The sequence number that enables replace-by-fee and absolute lock time but
/// disables relative lock time. /// disables relative lock time.
#[deprecated(since = "TBD", note = "use `ENABLE_LOCKTIME_AND_RBF` instead")] #[deprecated(since = "TBD", note = "use `ENABLE_LOCKTIME_AND_RBF` instead")]
pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD); pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFF_FFFD);
/// The maximum sequence number that enables replace-by-fee and absolute lock time but /// The maximum sequence number that enables replace-by-fee and absolute lock time but
/// disables relative lock time. /// disables relative lock time.
/// ///
/// This sequence number has no meaning other than to enable RBF and the absolute locktime. /// This sequence number has no meaning other than to enable RBF and the absolute locktime.
pub const ENABLE_LOCKTIME_AND_RBF: Self = Sequence(0xFFFFFFFD); pub const ENABLE_LOCKTIME_AND_RBF: Self = Sequence(0xFFFF_FFFD);
/// The number of bytes that a sequence number contributes to the size of a transaction. /// The number of bytes that a sequence number contributes to the size of a transaction.
pub const SIZE: usize = 4; // Serialized length of a u32. pub const SIZE: usize = 4; // Serialized length of a u32.
@ -66,15 +66,15 @@ impl Sequence {
/// (Explicit Signalling [BIP-125]). /// (Explicit Signalling [BIP-125]).
/// ///
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]> /// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]>
const MIN_NO_RBF: Self = Sequence(0xFFFFFFFE); const MIN_NO_RBF: Self = Sequence(0xFFFF_FFFE);
/// BIP-68 relative lock time disable flag mask. /// BIP-68 relative lock time disable flag mask.
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x80000000; const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x8000_0000;
/// BIP-68 relative lock time type flag mask. /// BIP-68 relative lock time type flag mask.
const LOCK_TYPE_MASK: u32 = 0x00400000; const LOCK_TYPE_MASK: u32 = 0x0040_0000;
/// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]). /// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]).
#[inline] #[inline]
pub fn enables_absolute_lock_time(&self) -> bool { *self != Sequence::MAX } pub fn enables_absolute_lock_time(self) -> bool { self != Sequence::MAX }
/// Returns `true` if the sequence number indicates that the transaction is finalized. /// Returns `true` if the sequence number indicates that the transaction is finalized.
/// ///
@ -96,30 +96,30 @@ impl Sequence {
/// ///
/// [BIP-112]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki> /// [BIP-112]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>
#[inline] #[inline]
pub fn is_final(&self) -> bool { !self.enables_absolute_lock_time() } pub fn is_final(self) -> bool { !self.enables_absolute_lock_time() }
/// Returns true if the transaction opted-in to BIP125 replace-by-fee. /// Returns true if the transaction opted-in to BIP125 replace-by-fee.
/// ///
/// Replace by fee is signaled by the sequence being less than 0xfffffffe which is checked by /// Replace by fee is signaled by the sequence being less than 0xfffffffe which is checked by
/// this method. Note, this is the highest "non-final" value (see [`Sequence::is_final`]). /// this method. Note, this is the highest "non-final" value (see [`Sequence::is_final`]).
#[inline] #[inline]
pub fn is_rbf(&self) -> bool { *self < Sequence::MIN_NO_RBF } pub fn is_rbf(self) -> bool { self < Sequence::MIN_NO_RBF }
/// Returns `true` if the sequence has a relative lock-time. /// Returns `true` if the sequence has a relative lock-time.
#[inline] #[inline]
pub fn is_relative_lock_time(&self) -> bool { pub fn is_relative_lock_time(self) -> bool {
self.0 & Sequence::LOCK_TIME_DISABLE_FLAG_MASK == 0 self.0 & Sequence::LOCK_TIME_DISABLE_FLAG_MASK == 0
} }
/// Returns `true` if the sequence number encodes a block based relative lock-time. /// Returns `true` if the sequence number encodes a block based relative lock-time.
#[inline] #[inline]
pub fn is_height_locked(&self) -> bool { pub fn is_height_locked(self) -> bool {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK == 0) self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK == 0)
} }
/// Returns `true` if the sequence number encodes a time interval based relative lock-time. /// Returns `true` if the sequence number encodes a time interval based relative lock-time.
#[inline] #[inline]
pub fn is_time_locked(&self) -> bool { pub fn is_time_locked(self) -> bool {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0) self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0)
} }
@ -206,7 +206,7 @@ impl Sequence {
/// ///
/// BIP-68 only uses the low 16 bits for relative lock value. /// BIP-68 only uses the low 16 bits for relative lock value.
#[inline] #[inline]
fn low_u16(&self) -> u16 { self.0 as u16 } fn low_u16(self) -> u16 { self.0 as u16 }
} }
impl Default for Sequence { impl Default for Sequence {
@ -319,9 +319,9 @@ mod tests {
#[test] #[test]
fn sequence_properties() { fn sequence_properties() {
let seq_max = Sequence(0xFFFFFFFF); let seq_max = Sequence(0xFFFF_FFFF);
let seq_no_rbf = Sequence(0xFFFFFFFE); let seq_no_rbf = Sequence(0xFFFF_FFFE);
let seq_rbf = Sequence(0xFFFFFFFD); let seq_rbf = Sequence(0xFFFF_FFFD);
assert!(seq_max.is_final()); assert!(seq_max.is_final());
assert!(!seq_no_rbf.is_final()); assert!(!seq_no_rbf.is_final());
@ -332,12 +332,12 @@ mod tests {
assert!(seq_rbf.is_rbf()); assert!(seq_rbf.is_rbf());
assert!(!seq_no_rbf.is_rbf()); assert!(!seq_no_rbf.is_rbf());
let seq_relative = Sequence(0x7FFFFFFF); let seq_relative = Sequence(0x7FFF_FFFF);
assert!(seq_relative.is_relative_lock_time()); assert!(seq_relative.is_relative_lock_time());
assert!(!seq_max.is_relative_lock_time()); assert!(!seq_max.is_relative_lock_time());
let seq_height_locked = Sequence(0x00399999); let seq_height_locked = Sequence(0x0039_9999);
let seq_time_locked = Sequence(0x00400000); let seq_time_locked = Sequence(0x0040_0000);
assert!(seq_height_locked.is_height_locked()); assert!(seq_height_locked.is_height_locked());
assert!(seq_time_locked.is_time_locked()); assert!(seq_time_locked.is_time_locked());
assert!(!seq_time_locked.is_height_locked()); assert!(!seq_time_locked.is_height_locked());
@ -346,12 +346,12 @@ mod tests {
#[test] #[test]
fn sequence_formatting() { fn sequence_formatting() {
let sequence = Sequence(0x7FFFFFFF); let sequence = Sequence(0x7FFF_FFFF);
assert_eq!(format!("{:x}", sequence), "7fffffff"); assert_eq!(format!("{:x}", sequence), "7fffffff");
assert_eq!(format!("{:X}", sequence), "7FFFFFFF"); assert_eq!(format!("{:X}", sequence), "7FFFFFFF");
// Test From<Sequence> for u32 // Test From<Sequence> for u32
let sequence_u32: u32 = sequence.into(); let sequence_u32: u32 = sequence.into();
assert_eq!(sequence_u32, 0x7FFFFFFF); assert_eq!(sequence_u32, 0x7FFF_FFFF);
} }
} }

View File

@ -265,7 +265,7 @@ fn hash_transaction(tx: &Transaction, uses_segwit_serialization: bool) -> sha256
enc.input(compact_size::encode(script_sig_bytes.len()).as_slice()); enc.input(compact_size::encode(script_sig_bytes.len()).as_slice());
enc.input(script_sig_bytes); enc.input(script_sig_bytes);
enc.input(&input.sequence.0.to_le_bytes()) enc.input(&input.sequence.0.to_le_bytes());
} }
// Encode outputs with leading compact size encoded int. // Encode outputs with leading compact size encoded int.
@ -285,7 +285,7 @@ fn hash_transaction(tx: &Transaction, uses_segwit_serialization: bool) -> sha256
for input in &tx.input { for input in &tx.input {
// Same as `Encodable for Witness`. // Same as `Encodable for Witness`.
enc.input(compact_size::encode(input.witness.len()).as_slice()); enc.input(compact_size::encode(input.witness.len()).as_slice());
for element in input.witness.iter() { for element in &input.witness {
enc.input(compact_size::encode(element.len()).as_slice()); enc.input(compact_size::encode(element.len()).as_slice());
enc.input(element); enc.input(element);
} }
@ -322,9 +322,9 @@ pub struct TxIn {
/// the miner behavior cannot be enforced. /// the miner behavior cannot be enforced.
pub sequence: Sequence, pub sequence: Sequence,
/// Witness data: an array of byte-arrays. /// Witness data: an array of byte-arrays.
/// Note that this field is *not* (de)serialized with the rest of the TxIn in /// Note that this field is *not* (de)serialized with the rest of the `TxIn` in
/// Encodable/Decodable, as it is (de)serialized at the end of the full /// Encodable/Decodable, as it is (de)serialized at the end of the full
/// Transaction. It *is* (de)serialized with the rest of the TxIn in other /// Transaction. It *is* (de)serialized with the rest of the `TxIn` in other
/// (de)serialization routines. /// (de)serialization routines.
pub witness: Witness, pub witness: Witness,
} }
@ -459,14 +459,14 @@ impl From<Infallible> for ParseOutPointError {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl fmt::Display for ParseOutPointError { impl fmt::Display for ParseOutPointError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseOutPointError::*; use ParseOutPointError as E;
match *self { match *self {
Txid(ref e) => write_err!(f, "error parsing TXID"; e), E::Txid(ref e) => write_err!(f, "error parsing TXID"; e),
Vout(ref e) => write_err!(f, "error parsing vout"; e), E::Vout(ref e) => write_err!(f, "error parsing vout"; e),
Format => write!(f, "OutPoint not in <txid>:<vout> format"), E::Format => write!(f, "OutPoint not in <txid>:<vout> format"),
TooLong => write!(f, "vout should be at most 10 digits"), E::TooLong => write!(f, "vout should be at most 10 digits"),
VoutNotCanonical => write!(f, "no leading zeroes or + allowed in vout part"), E::VoutNotCanonical => write!(f, "no leading zeroes or + allowed in vout part"),
} }
} }
} }
@ -474,12 +474,12 @@ impl fmt::Display for ParseOutPointError {
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl std::error::Error for ParseOutPointError { impl std::error::Error for ParseOutPointError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use ParseOutPointError::*; use ParseOutPointError as E;
match self { match self {
Txid(e) => Some(e), E::Txid(e) => Some(e),
Vout(e) => Some(e), E::Vout(e) => Some(e),
Format | TooLong | VoutNotCanonical => None, E::Format | E::TooLong | E::VoutNotCanonical => None,
} }
} }
} }
@ -561,8 +561,8 @@ impl Version {
/// As of Bitcoin Core 28.0 ([release notes](https://bitcoincore.org/en/releases/28.0/)), /// As of Bitcoin Core 28.0 ([release notes](https://bitcoincore.org/en/releases/28.0/)),
/// versions 1, 2, and 3 are considered standard. /// versions 1, 2, and 3 are considered standard.
#[inline] #[inline]
pub fn is_standard(&self) -> bool { pub fn is_standard(self) -> bool {
*self == Version::ONE || *self == Version::TWO || *self == Version::THREE self == Version::ONE || self == Version::TWO || self == Version::THREE
} }
} }
@ -657,11 +657,11 @@ mod tests {
witness: Witness::new(), witness: Witness::new(),
}; };
let txout = TxOut { value: Amount::from_sat(123456789), script_pubkey: ScriptBuf::new() }; let txout = TxOut { value: Amount::from_sat(123_456_789), script_pubkey: ScriptBuf::new() };
let tx_orig = Transaction { let tx_orig = Transaction {
version: Version::ONE, version: Version::ONE,
lock_time: absolute::LockTime::from_consensus(1738968231), // The time this was written lock_time: absolute::LockTime::from_consensus(1_738_968_231), // The time this was written
input: vec![txin.clone()], input: vec![txin.clone()],
output: vec![txout.clone()], output: vec![txout.clone()],
}; };
@ -669,9 +669,9 @@ mod tests {
// Test changing the transaction // Test changing the transaction
let mut tx = tx_orig.clone(); let mut tx = tx_orig.clone();
tx.inputs_mut()[0].previous_output.txid = Txid::from_byte_array([0xFF; 32]); tx.inputs_mut()[0].previous_output.txid = Txid::from_byte_array([0xFF; 32]);
tx.outputs_mut()[0].value = Amount::from_sat(987654321); tx.outputs_mut()[0].value = Amount::from_sat(987_654_321);
assert_eq!(tx.inputs()[0].previous_output.txid.to_byte_array(), [0xFF; 32]); assert_eq!(tx.inputs()[0].previous_output.txid.to_byte_array(), [0xFF; 32]);
assert_eq!(tx.outputs()[0].value.to_sat(), 987654321); assert_eq!(tx.outputs()[0].value.to_sat(), 987_654_321);
// Test uses_segwit_serialization // Test uses_segwit_serialization
assert!(!tx.uses_segwit_serialization()); assert!(!tx.uses_segwit_serialization());

View File

@ -100,7 +100,7 @@ impl Witness {
/// Convenience method to create an array of byte-arrays from this witness. /// Convenience method to create an array of byte-arrays from this witness.
#[inline] #[inline]
pub fn to_vec(&self) -> Vec<Vec<u8>> { self.iter().map(|s| s.to_vec()).collect() } pub fn to_vec(&self) -> Vec<Vec<u8>> { self.iter().map(<[u8]>::to_vec).collect() }
/// Returns `true` if the witness contains no element. /// Returns `true` if the witness contains no element.
#[inline] #[inline]
@ -172,17 +172,6 @@ impl Witness {
.copy_from_slice(new_element); .copy_from_slice(new_element);
} }
/// Note `index` is the index into the `content` vector and should be the result of calling
/// `decode_cursor`, which returns a valid index.
fn element_at(&self, index: usize) -> Option<&[u8]> {
let mut slice = &self.content[index..]; // Start of element.
let element_len = compact_size::decode_unchecked(&mut slice);
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
let end = element_len as usize;
Some(&slice[..end])
}
/// Returns the last element in the witness, if any. /// Returns the last element in the witness, if any.
#[inline] #[inline]
pub fn last(&self) -> Option<&[u8]> { pub fn last(&self) -> Option<&[u8]> {
@ -217,7 +206,13 @@ impl Witness {
#[inline] #[inline]
pub fn nth(&self, index: usize) -> Option<&[u8]> { pub fn nth(&self, index: usize) -> Option<&[u8]> {
let pos = decode_cursor(&self.content, self.indices_start, index)?; let pos = decode_cursor(&self.content, self.indices_start, index)?;
self.element_at(pos)
let mut slice = &self.content[pos..]; // Start of element.
let element_len = compact_size::decode_unchecked(&mut slice);
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
let end = element_len as usize;
Some(&slice[..end])
} }
} }
@ -247,9 +242,10 @@ fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<
/// - Number of witness elements /// - Number of witness elements
/// - Total bytes across all elements /// - Total bytes across all elements
/// - List of hex-encoded witness elements /// - List of hex-encoded witness elements
#[allow(clippy::missing_fields_in_debug)] // We don't want to show `indices_start`.
impl fmt::Debug for Witness { impl fmt::Debug for Witness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let total_bytes: usize = self.iter().map(|elem| elem.len()).sum(); let total_bytes: usize = self.iter().map(<[u8]>::len).sum();
f.debug_struct("Witness") f.debug_struct("Witness")
.field("num_elements", &self.witness_elements) .field("num_elements", &self.witness_elements)
@ -257,7 +253,7 @@ impl fmt::Debug for Witness {
.field( .field(
"elements", "elements",
&WrapDebug(|f| { &WrapDebug(|f| {
f.debug_list().entries(self.iter().map(|elem| elem.as_hex())).finish() f.debug_list().entries(self.iter().map(DisplayHex::as_hex)).finish()
}), }),
) )
.finish() .finish()
@ -323,7 +319,7 @@ impl serde::Serialize for Witness {
let mut seq = serializer.serialize_seq(Some(self.witness_elements))?; let mut seq = serializer.serialize_seq(Some(self.witness_elements))?;
// Note that the `Iter` strips the varints out when iterating. // Note that the `Iter` strips the varints out when iterating.
for elem in self.iter() { for elem in self {
if human_readable { if human_readable {
seq.serialize_element(&internals::serde::SerializeBytesAsHex(elem))?; seq.serialize_element(&internals::serde::SerializeBytesAsHex(elem))?;
} else { } else {
@ -355,7 +351,7 @@ impl<'de> serde::Deserialize<'de> for Witness {
mut a: A, mut a: A,
) -> Result<Self::Value, A::Error> { ) -> Result<Self::Value, A::Error> {
use hex::FromHex; use hex::FromHex;
use hex::HexToBytesError::*; use hex::HexToBytesError as E;
use serde::de::{self, Unexpected}; use serde::de::{self, Unexpected};
let mut ret = match a.size_hint() { let mut ret = match a.size_hint() {
@ -365,7 +361,7 @@ impl<'de> serde::Deserialize<'de> for Witness {
while let Some(elem) = a.next_element::<String>()? { while let Some(elem) = a.next_element::<String>()? {
let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e { let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e {
InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) { E::InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) {
Some(c) => de::Error::invalid_value( Some(c) => de::Error::invalid_value(
Unexpected::Char(c), Unexpected::Char(c),
&"a valid hex character", &"a valid hex character",
@ -375,7 +371,7 @@ impl<'de> serde::Deserialize<'de> for Witness {
&"a valid hex character", &"a valid hex character",
), ),
}, },
OddLengthString(ref e) => E::OddLengthString(ref e) =>
de::Error::invalid_length(e.length(), &"an even length string"), de::Error::invalid_length(e.length(), &"an even length string"),
})?; })?;
ret.push(vec); ret.push(vec);
@ -459,7 +455,7 @@ mod test {
let mut got = Witness::new(); let mut got = Witness::new();
got.push([]); got.push([]);
let want = single_empty_element(); let want = single_empty_element();
assert_eq!(got, want) assert_eq!(got, want);
} }
#[test] #[test]