Skip to content

Commit 9b90283

Browse files
committed
Merge rust-bitcoin#5027: units: Improve the decoders
b8af0d7 Update API text files (Tobin C. Harding) 6175586 units: Re-export the AmountDecoder (Tobin C. Harding) d340c4d Update API text files (Tobin C. Harding) b02b3ad units: Implement constructor for decoders (Tobin C. Harding) c6ce97e units: Wrap decoder error types (Tobin C. Harding) 11811e0 units: Hide amount decoder error internals (Tobin C. Harding) Pull request description: Improve all the decoders and decoder error types in `units`. Based on recent discussion. This PR introduces policy on how we implement decoder error types (patch 1 and 2). LFG, decoders done by Nashville! ACKs for top commit: apoelstra: ACK b8af0d7; successfully ran local tests Tree-SHA512: bcc18be8b29ec067cf30d66d59d14da88c3edfbaf0dcdc5363ee6d2380cf149c8b0c63bb92f5d571c85167b74a59b36d097d58a21524895c9da20c7ac77f94c7
2 parents d062e71 + b8af0d7 commit 9b90283

File tree

9 files changed

+330
-41
lines changed

9 files changed

+330
-41
lines changed

api/units/all-features.txt

Lines changed: 122 additions & 13 deletions
Large diffs are not rendered by default.

units/src/amount/error.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,24 @@ impl std::error::Error for PossiblyConfusingDenominationError {
394394
/// An error consensus decoding an `Amount`.
395395
#[cfg(feature = "encoding")]
396396
#[derive(Debug, Clone, PartialEq, Eq)]
397-
#[non_exhaustive]
398-
pub enum AmountDecoderError {
397+
pub struct AmountDecoderError(pub(super) AmountDecoderErrorInner);
398+
399+
#[cfg(feature = "encoding")]
400+
impl AmountDecoderError {
401+
/// Constructs an EOF error.
402+
pub(super) fn eof(e: encoding::UnexpectedEofError) -> Self {
403+
Self(AmountDecoderErrorInner::UnexpectedEof(e))
404+
}
405+
406+
/// Constructs an out of range (`Amount::from_sat`) error.
407+
pub(super) fn out_of_range(e: OutOfRangeError) -> Self {
408+
Self(AmountDecoderErrorInner::OutOfRange(e))
409+
}
410+
}
411+
412+
#[cfg(feature = "encoding")]
413+
#[derive(Debug, Clone, PartialEq, Eq)]
414+
pub(super) enum AmountDecoderErrorInner {
399415
/// Not enough bytes given to decoder.
400416
UnexpectedEof(encoding::UnexpectedEofError),
401417
/// Decoded amount is too big.
@@ -407,27 +423,26 @@ impl From<Infallible> for AmountDecoderError {
407423
fn from(never: Infallible) -> Self { match never {} }
408424
}
409425

410-
#[cfg(feature = "encoding")]
411-
impl From<encoding::UnexpectedEofError> for AmountDecoderError {
412-
fn from(e: encoding::UnexpectedEofError) -> Self { Self::UnexpectedEof(e) }
413-
}
414-
415426
#[cfg(feature = "encoding")]
416427
impl fmt::Display for AmountDecoderError {
417428
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
418-
match *self {
419-
Self::UnexpectedEof(ref e) => write_err!(f, "decode error"; e),
420-
Self::OutOfRange(ref e) => write_err!(f, "decode error"; e),
429+
use AmountDecoderErrorInner as E;
430+
431+
match self.0 {
432+
E::UnexpectedEof(ref e) => write_err!(f, "decode error"; e),
433+
E::OutOfRange(ref e) => write_err!(f, "decode error"; e),
421434
}
422435
}
423436
}
424437

425438
#[cfg(all(feature = "std", feature = "encoding"))]
426439
impl std::error::Error for AmountDecoderError {
427440
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
428-
match *self {
429-
Self::UnexpectedEof(ref e) => Some(e),
430-
Self::OutOfRange(ref e) => Some(e),
441+
use AmountDecoderErrorInner as E;
442+
443+
match self.0 {
444+
E::UnexpectedEof(ref e) => Some(e),
445+
E::OutOfRange(ref e) => Some(e),
431446
}
432447
}
433448
}

units/src/amount/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ pub use self::{
3737
signed::SignedAmount,
3838
unsigned::Amount,
3939
};
40+
#[doc(inline)]
41+
#[cfg(feature = "encoding")]
42+
pub use self::unsigned::AmountDecoder;
4043
#[cfg(feature = "encoding")]
4144
#[doc(no_inline)]
4245
pub use self::error::AmountDecoderError;

units/src/amount/unsigned.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,20 +582,31 @@ impl encoding::Encodable for Amount {
582582
#[cfg(feature = "encoding")]
583583
pub struct AmountDecoder(encoding::ArrayDecoder<8>);
584584

585+
#[cfg(feature = "encoding")]
586+
impl AmountDecoder {
587+
/// Constructs a new [`Amount`] decoder.
588+
pub fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
589+
}
590+
591+
#[cfg(feature = "encoding")]
592+
impl Default for AmountDecoder {
593+
fn default() -> Self { Self::new() }
594+
}
595+
585596
#[cfg(feature = "encoding")]
586597
impl encoding::Decoder for AmountDecoder {
587598
type Output = Amount;
588599
type Error = AmountDecoderError;
589600

590601
#[inline]
591602
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
592-
Ok(self.0.push_bytes(bytes)?)
603+
self.0.push_bytes(bytes).map_err(AmountDecoderError::eof)
593604
}
594605

595606
#[inline]
596607
fn end(self) -> Result<Self::Output, Self::Error> {
597-
let a = u64::from_le_bytes(self.0.end()?);
598-
Ok(Amount::from_sat(a).map_err(AmountDecoderError::OutOfRange)?)
608+
let a = u64::from_le_bytes(self.0.end().map_err(AmountDecoderError::eof)?);
609+
Amount::from_sat(a).map_err(AmountDecoderError::out_of_range)
599610
}
600611
}
601612

units/src/block.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111
//! The difference between these types and the locktime types is that these types are thin wrappers
1212
//! whereas the locktime types contain more complex locktime specific abstractions.
1313
14+
#[cfg(feature = "encoding")]
15+
use core::convert::Infallible;
1416
use core::{fmt, ops};
1517

1618
#[cfg(feature = "arbitrary")]
1719
use arbitrary::{Arbitrary, Unstructured};
20+
#[cfg(feature = "encoding")]
21+
use internals::write_err;
1822
#[cfg(feature = "serde")]
1923
use serde::{Deserialize, Deserializer, Serialize, Serializer};
2024

@@ -161,19 +165,30 @@ impl encoding::Encodable for BlockHeight {
161165
#[cfg(feature = "encoding")]
162166
pub struct BlockHeightDecoder(encoding::ArrayDecoder<4>);
163167

168+
#[cfg(feature = "encoding")]
169+
impl Default for BlockHeightDecoder {
170+
fn default() -> Self { Self::new() }
171+
}
172+
173+
#[cfg(feature = "encoding")]
174+
impl BlockHeightDecoder {
175+
/// Constructs a new [`BlockHeight`] decoder.
176+
pub fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
177+
}
178+
164179
#[cfg(feature = "encoding")]
165180
impl encoding::Decoder for BlockHeightDecoder {
166181
type Output = BlockHeight;
167-
type Error = encoding::UnexpectedEofError;
182+
type Error = BlockHeightDecoderError;
168183

169184
#[inline]
170185
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
171-
self.0.push_bytes(bytes)
186+
self.0.push_bytes(bytes).map_err(BlockHeightDecoderError)
172187
}
173188

174189
#[inline]
175190
fn end(self) -> Result<Self::Output, Self::Error> {
176-
let n = u32::from_le_bytes(self.0.end()?);
191+
let n = u32::from_le_bytes(self.0.end().map_err(BlockHeightDecoderError)?);
177192
Ok(BlockHeight::from_u32(n))
178193
}
179194
}
@@ -184,6 +199,28 @@ impl encoding::Decodable for BlockHeight {
184199
fn decoder() -> Self::Decoder { BlockHeightDecoder(encoding::ArrayDecoder::<4>::new()) }
185200
}
186201

202+
/// An error consensus decoding an `BlockHeight`.
203+
#[cfg(feature = "encoding")]
204+
#[derive(Debug, Clone, PartialEq, Eq)]
205+
pub struct BlockHeightDecoderError(encoding::UnexpectedEofError);
206+
207+
#[cfg(feature = "encoding")]
208+
impl From<Infallible> for BlockHeightDecoderError {
209+
fn from(never: Infallible) -> Self { match never {} }
210+
}
211+
212+
#[cfg(feature = "encoding")]
213+
impl fmt::Display for BlockHeightDecoderError {
214+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215+
write_err!(f, "block height decoder error"; self.0)
216+
}
217+
}
218+
219+
#[cfg(all(feature = "std", feature = "encoding"))]
220+
impl std::error::Error for BlockHeightDecoderError {
221+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
222+
}
223+
187224
impl_u32_wrapper! {
188225
/// An unsigned block interval.
189226
///

units/src/locktime/absolute/error.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,34 @@ use core::convert::Infallible;
66
use core::fmt;
77

88
use internals::error::InputString;
9+
#[cfg(feature = "encoding")]
10+
use internals::write_err;
911

1012
use super::{Height, MedianTimePast, LOCK_TIME_THRESHOLD};
1113
use crate::parse_int::ParseIntError;
1214

15+
/// An error consensus decoding an `LockTime`.
16+
#[cfg(feature = "encoding")]
17+
#[derive(Debug, Clone, PartialEq, Eq)]
18+
pub struct LockTimeDecoderError(pub(super) encoding::UnexpectedEofError);
19+
20+
#[cfg(feature = "encoding")]
21+
impl From<Infallible> for LockTimeDecoderError {
22+
fn from(never: Infallible) -> Self { match never {} }
23+
}
24+
25+
#[cfg(feature = "encoding")]
26+
impl fmt::Display for LockTimeDecoderError {
27+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28+
write_err!(f, "lock time decoder error"; self.0)
29+
}
30+
}
31+
32+
#[cfg(all(feature = "std", feature = "encoding"))]
33+
impl std::error::Error for LockTimeDecoderError {
34+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
35+
}
36+
1337
/// Tried to satisfy a lock-by-time lock using a height value.
1438
#[derive(Debug, Clone, PartialEq, Eq)]
1539
pub struct IncompatibleHeightError {

units/src/locktime/absolute/mod.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
2424
pub use self::error::{
2525
ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError, ParseTimeError,
2626
};
27+
#[cfg(feature = "encoding")]
28+
pub use self::error::LockTimeDecoderError;
2729

2830
/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
2931
///
@@ -421,19 +423,30 @@ impl encoding::Encodable for LockTime {
421423
#[cfg(feature = "encoding")]
422424
pub struct LockTimeDecoder(encoding::ArrayDecoder<4>);
423425

426+
#[cfg(feature = "encoding")]
427+
impl LockTimeDecoder {
428+
/// Constructs a new [`LockTime`] decoder.
429+
pub fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
430+
}
431+
432+
#[cfg(feature = "encoding")]
433+
impl Default for LockTimeDecoder {
434+
fn default() -> Self { Self::new() }
435+
}
436+
424437
#[cfg(feature = "encoding")]
425438
impl encoding::Decoder for LockTimeDecoder {
426439
type Output = LockTime;
427-
type Error = encoding::UnexpectedEofError;
440+
type Error = LockTimeDecoderError;
428441

429442
#[inline]
430443
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
431-
self.0.push_bytes(bytes)
444+
Ok(self.0.push_bytes(bytes).map_err(LockTimeDecoderError)?)
432445
}
433446

434447
#[inline]
435448
fn end(self) -> Result<Self::Output, Self::Error> {
436-
let n = u32::from_le_bytes(self.0.end()?);
449+
let n = u32::from_le_bytes(self.0.end().map_err(LockTimeDecoderError)?);
437450
Ok(LockTime::from_consensus(n))
438451
}
439452
}

units/src/sequence.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
//! [BIP-0068]: <https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki>
1515
//! [BIP-0125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki>
1616
17+
#[cfg(feature = "encoding")]
18+
use core::convert::Infallible;
1719
use core::fmt;
1820

1921
#[cfg(feature = "arbitrary")]
2022
use arbitrary::{Arbitrary, Unstructured};
23+
#[cfg(feature = "encoding")]
24+
use internals::write_err;
2125
#[cfg(feature = "serde")]
2226
use serde::{Deserialize, Serialize};
2327

@@ -284,19 +288,30 @@ impl encoding::Encodable for Sequence {
284288
#[cfg(feature = "encoding")]
285289
pub struct SequenceDecoder(encoding::ArrayDecoder<4>);
286290

291+
#[cfg(feature = "encoding")]
292+
impl Default for SequenceDecoder {
293+
fn default() -> Self { Self::new() }
294+
}
295+
296+
#[cfg(feature = "encoding")]
297+
impl SequenceDecoder {
298+
/// Constructs a new [`Sequence`] decoder.
299+
pub fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
300+
}
301+
287302
#[cfg(feature = "encoding")]
288303
impl encoding::Decoder for SequenceDecoder {
289304
type Output = Sequence;
290-
type Error = encoding::UnexpectedEofError;
305+
type Error = SequenceDecoderError;
291306

292307
#[inline]
293308
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
294-
self.0.push_bytes(bytes)
309+
self.0.push_bytes(bytes).map_err(SequenceDecoderError)
295310
}
296311

297312
#[inline]
298313
fn end(self) -> Result<Self::Output, Self::Error> {
299-
let n = u32::from_le_bytes(self.0.end()?);
314+
let n = u32::from_le_bytes(self.0.end().map_err(SequenceDecoderError)?);
300315
Ok(Sequence::from_consensus(n))
301316
}
302317
}
@@ -307,6 +322,28 @@ impl encoding::Decodable for Sequence {
307322
fn decoder() -> Self::Decoder { SequenceDecoder(encoding::ArrayDecoder::<4>::new()) }
308323
}
309324

325+
/// An error consensus decoding an `Sequence`.
326+
#[cfg(feature = "encoding")]
327+
#[derive(Debug, Clone, PartialEq, Eq)]
328+
pub struct SequenceDecoderError(encoding::UnexpectedEofError);
329+
330+
#[cfg(feature = "encoding")]
331+
impl From<Infallible> for SequenceDecoderError {
332+
fn from(never: Infallible) -> Self { match never {} }
333+
}
334+
335+
#[cfg(feature = "encoding")]
336+
impl fmt::Display for SequenceDecoderError {
337+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338+
write_err!(f, "sequence decoder error"; self.0)
339+
}
340+
}
341+
342+
#[cfg(all(feature = "std", feature = "encoding"))]
343+
impl std::error::Error for SequenceDecoderError {
344+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
345+
}
346+
310347
#[cfg(feature = "arbitrary")]
311348
#[cfg(feature = "alloc")]
312349
impl<'a> Arbitrary<'a> for Sequence {

0 commit comments

Comments
 (0)