Skip to content

Commit ebc5ec4

Browse files
committed
Merge rust-bitcoin#5008: Units decoders with phantoms
43077e8 consensus_encoding: API update (Nick Johnson) 9f721bb consensus_encoding: fix up error message (Nick Johnson) 88c6c3b units: Implement decoding traits (Tobin C. Harding) e36cc96 consensus_encoding: rename UnexptectedEof (Nick Johnson) Pull request description: tcharding 's decoder work rebased on top of the phantom errors (rust-bitcoin#5001) if we go that route. Plus some encoding feature flag gates. ACKs for top commit: apoelstra: ACK 43077e8; successfully ran local tests tcharding: ACK 43077e8 Tree-SHA512: 95ab4798d87282495331877792c9dc5b24d8bf61dfcbeaf840ab7dbcaa79793057f31b3d3bf5d09b64af357a55b26af234f0e0ca9fb975bc196fb338c13753a9
2 parents b84245f + 43077e8 commit ebc5ec4

File tree

12 files changed

+309
-36
lines changed

12 files changed

+309
-36
lines changed

api/units/all-features.txt

Lines changed: 91 additions & 0 deletions
Large diffs are not rendered by default.

consensus_encoding/src/decode/decoders.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,6 @@
44
55
use super::Decoder;
66

7-
/// Not enough bytes given to decoder.
8-
#[derive(Debug, Clone, PartialEq, Eq)]
9-
pub struct UnexpectedEof {
10-
/// Number of bytes missing to complete decoder.
11-
missing: usize,
12-
}
13-
14-
impl core::fmt::Display for UnexpectedEof {
15-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16-
write!(f, "not enough bytes for decoder, {} more bytes required", self.missing)
17-
}
18-
}
19-
20-
#[cfg(feature = "std")]
21-
impl std::error::Error for UnexpectedEof {}
22-
237
/// A decoder that expects exactly N bytes and returns them as an array.
248
pub struct ArrayDecoder<const N: usize> {
259
buffer: [u8; N],
@@ -37,7 +21,7 @@ impl<const N: usize> Default for ArrayDecoder<N> {
3721

3822
impl<const N: usize> Decoder for ArrayDecoder<N> {
3923
type Output = [u8; N];
40-
type Error = UnexpectedEof;
24+
type Error = UnexpectedEofError;
4125

4226
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
4327
let remaining_space = N - self.bytes_written;
@@ -59,7 +43,7 @@ impl<const N: usize> Decoder for ArrayDecoder<N> {
5943
if self.bytes_written == N {
6044
Ok(self.buffer)
6145
} else {
62-
Err(UnexpectedEof { missing: N - self.bytes_written })
46+
Err(UnexpectedEofError { missing: N - self.bytes_written })
6347
}
6448
}
6549
}
@@ -355,3 +339,19 @@ where
355339
Ok((first, second, third, fourth, fifth, sixth))
356340
}
357341
}
342+
343+
/// Not enough bytes given to decoder.
344+
#[derive(Debug, Clone, PartialEq, Eq)]
345+
pub struct UnexpectedEofError {
346+
/// Number of bytes missing to complete decoder.
347+
missing: usize,
348+
}
349+
350+
impl core::fmt::Display for UnexpectedEofError {
351+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352+
write!(f, "not enough bytes for decoder, {} more bytes required", self.missing)
353+
}
354+
}
355+
356+
#[cfg(feature = "std")]
357+
impl std::error::Error for UnexpectedEofError {}

consensus_encoding/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ mod decode;
2323
mod encode;
2424

2525
pub use self::decode::decoders::{
26-
ArrayDecoder, Decoder2, Decoder3, Decoder4, Decoder6, UnexpectedEof,
26+
ArrayDecoder, Decoder2, Decoder3, Decoder4, Decoder6, UnexpectedEofError,
2727
};
2828
pub use self::decode::{Decodable, Decoder};
2929
#[cfg(feature = "alloc")]

consensus_encoding/tests/composition.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use consensus_encoding::{
66
ArrayDecoder, ArrayEncoder, Decodable, Decoder, Decoder2, Decoder6, Encodable, Encoder,
7-
Encoder2, Encoder6, UnexpectedEof,
7+
Encoder2, Encoder6, UnexpectedEofError,
88
};
99

1010
const EMPTY: &[u8] = &[];
@@ -30,17 +30,17 @@ impl Encodable for CompositeData {
3030
/// A unified error type for [`CompositeDataDecoder`].
3131
#[derive(Debug, Clone, PartialEq, Eq)]
3232
enum CompositeError {
33-
Eof(UnexpectedEof),
33+
Eof(UnexpectedEofError),
3434
}
3535

36-
impl From<UnexpectedEof> for CompositeError {
37-
fn from(eof: UnexpectedEof) -> Self { CompositeError::Eof(eof) }
36+
impl From<UnexpectedEofError> for CompositeError {
37+
fn from(eof: UnexpectedEofError) -> Self { CompositeError::Eof(eof) }
3838
}
3939

4040
impl core::fmt::Display for CompositeError {
4141
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4242
match self {
43-
CompositeError::Eof(eof) => write!(f, "Error in first array: {}", eof),
43+
CompositeError::Eof(eof) => write!(f, "Composite error: {}", eof),
4444
}
4545
}
4646
}
@@ -115,7 +115,7 @@ fn composition_nested() {
115115
}
116116
assert_eq!(encoded_bytes, data);
117117

118-
let mut decoder6: Decoder6<_, _, _, _, _, _, UnexpectedEof> = Decoder6::new(
118+
let mut decoder6: Decoder6<_, _, _, _, _, _, UnexpectedEofError> = Decoder6::new(
119119
ArrayDecoder::<1>::new(),
120120
ArrayDecoder::<1>::new(),
121121
ArrayDecoder::<1>::new(),
@@ -139,7 +139,7 @@ fn composition_nested() {
139139
#[test]
140140
fn composition_extra_bytes() {
141141
// Test that Decoder2 consumes exactly what it needs and leaves extra bytes unconsumed.
142-
let mut decoder2: Decoder2<_, _, UnexpectedEof> =
142+
let mut decoder2: Decoder2<_, _, UnexpectedEofError> =
143143
Decoder2::new(ArrayDecoder::<2>::new(), ArrayDecoder::<3>::new());
144144
let mut bytes = &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08][..];
145145
let original_len = bytes.len();
@@ -167,22 +167,22 @@ fn composition_error_unification() {
167167
#[derive(Debug, Clone, PartialEq, Eq)]
168168
enum NestedError {
169169
BadChecksum,
170-
UnexpectedEof(UnexpectedEof),
170+
UnexpectedEof(UnexpectedEofError),
171171
}
172172

173-
impl From<UnexpectedEof> for NestedError {
174-
fn from(eof: UnexpectedEof) -> Self { NestedError::UnexpectedEof(eof) }
173+
impl From<UnexpectedEofError> for NestedError {
174+
fn from(eof: UnexpectedEofError) -> Self { NestedError::UnexpectedEof(eof) }
175175
}
176176

177177
/// Error for top level encoder.
178178
#[derive(Debug, Clone, PartialEq, Eq)]
179179
enum TopLevelError {
180-
UnexpectedEof(UnexpectedEof),
180+
UnexpectedEof(UnexpectedEofError),
181181
Validation(NestedError),
182182
}
183183

184-
impl From<UnexpectedEof> for TopLevelError {
185-
fn from(eof: UnexpectedEof) -> Self { TopLevelError::UnexpectedEof(eof) }
184+
impl From<UnexpectedEofError> for TopLevelError {
185+
fn from(eof: UnexpectedEofError) -> Self { TopLevelError::UnexpectedEof(eof) }
186186
}
187187

188188
impl From<NestedError> for TopLevelError {

consensus_encoding/tests/decode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
//! Integration tests for decode module.
44
5-
use consensus_encoding::{ArrayDecoder, Decoder, UnexpectedEof};
5+
use consensus_encoding::{ArrayDecoder, Decoder, UnexpectedEofError};
66

77
const EMPTY: &[u8] = &[];
88

@@ -50,5 +50,5 @@ fn decode_array_insufficient_data_error() {
5050
assert_eq!(data, EMPTY);
5151

5252
let err = decoder.end().unwrap_err();
53-
assert!(matches!(err, UnexpectedEof { .. }));
53+
assert!(matches!(err, UnexpectedEofError { .. }));
5454
}

units/src/amount/error.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ impl fmt::Display for ParseAmountError {
137137
E::TooPrecise(ref error) => write_err!(f, "amount has a too high precision"; error),
138138
E::MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
139139
E::InputTooLarge(ref error) => write_err!(f, "the input is too large"; error),
140-
E::InvalidCharacter(ref error) =>
141-
write_err!(f, "invalid character in the input"; error),
140+
E::InvalidCharacter(ref error) => {
141+
write_err!(f, "invalid character in the input"; error)
142+
}
142143
}
143144
}
144145
}
@@ -389,3 +390,44 @@ impl fmt::Display for PossiblyConfusingDenominationError {
389390
impl std::error::Error for PossiblyConfusingDenominationError {
390391
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
391392
}
393+
394+
/// An error consensus decoding an `Amount`.
395+
#[cfg(feature = "encoding")]
396+
#[derive(Debug, Clone, PartialEq, Eq)]
397+
#[non_exhaustive]
398+
pub enum AmountDecoderError {
399+
/// Not enough bytes given to decoder.
400+
UnexpectedEof(encoding::UnexpectedEofError),
401+
/// Decoded amount is too big.
402+
OutOfRange(OutOfRangeError),
403+
}
404+
405+
#[cfg(feature = "encoding")]
406+
impl From<Infallible> for AmountDecoderError {
407+
fn from(never: Infallible) -> Self { match never {} }
408+
}
409+
410+
#[cfg(feature = "encoding")]
411+
impl From<encoding::UnexpectedEofError> for AmountDecoderError {
412+
fn from(e: encoding::UnexpectedEofError) -> Self { Self::UnexpectedEof(e) }
413+
}
414+
415+
#[cfg(feature = "encoding")]
416+
impl fmt::Display for AmountDecoderError {
417+
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),
421+
}
422+
}
423+
}
424+
425+
#[cfg(all(feature = "std", feature = "encoding"))]
426+
impl std::error::Error for AmountDecoderError {
427+
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),
431+
}
432+
}
433+
}

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+
#[cfg(feature = "encoding")]
41+
#[doc(no_inline)]
42+
pub use self::error::AmountDecoderError;
4043
#[doc(no_inline)]
4144
pub use self::error::{OutOfRangeError, ParseAmountError, ParseDenominationError, ParseError};
4245

units/src/amount/unsigned.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use arbitrary::{Arbitrary, Unstructured};
1212
use internals::const_casts;
1313
use NumOpResult as R;
1414

15+
#[cfg(feature = "encoding")]
16+
use super::error::AmountDecoderError;
1517
use super::error::{ParseAmountErrorInner, ParseErrorInner};
1618
use super::{
1719
parse_signed_to_satoshi, split_amount_and_denomination, Denomination, Display, DisplayStyle,
@@ -576,6 +578,33 @@ impl encoding::Encodable for Amount {
576578
}
577579
}
578580

581+
/// The decoder for the [`Amount`] type.
582+
#[cfg(feature = "encoding")]
583+
pub struct AmountDecoder(encoding::ArrayDecoder<8>);
584+
585+
#[cfg(feature = "encoding")]
586+
impl encoding::Decoder for AmountDecoder {
587+
type Output = Amount;
588+
type Error = AmountDecoderError;
589+
590+
#[inline]
591+
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
592+
Ok(self.0.push_bytes(bytes)?)
593+
}
594+
595+
#[inline]
596+
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)?)
599+
}
600+
}
601+
602+
#[cfg(feature = "encoding")]
603+
impl encoding::Decodable for Amount {
604+
type Decoder = AmountDecoder;
605+
fn decoder() -> Self::Decoder { AmountDecoder(encoding::ArrayDecoder::<8>::new()) }
606+
}
607+
579608
#[cfg(feature = "arbitrary")]
580609
impl<'a> Arbitrary<'a> for Amount {
581610
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {

units/src/block.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,33 @@ impl encoding::Encodable for BlockHeight {
157157
}
158158
}
159159

160+
/// The decoder for the [`BlockHeight`] type.
161+
#[cfg(feature = "encoding")]
162+
pub struct BlockHeightDecoder(encoding::ArrayDecoder<4>);
163+
164+
#[cfg(feature = "encoding")]
165+
impl encoding::Decoder for BlockHeightDecoder {
166+
type Output = BlockHeight;
167+
type Error = encoding::UnexpectedEofError;
168+
169+
#[inline]
170+
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
171+
self.0.push_bytes(bytes)
172+
}
173+
174+
#[inline]
175+
fn end(self) -> Result<Self::Output, Self::Error> {
176+
let n = u32::from_le_bytes(self.0.end()?);
177+
Ok(BlockHeight::from_u32(n))
178+
}
179+
}
180+
181+
#[cfg(feature = "encoding")]
182+
impl encoding::Decodable for BlockHeight {
183+
type Decoder = BlockHeightDecoder;
184+
fn decoder() -> Self::Decoder { BlockHeightDecoder(encoding::ArrayDecoder::<4>::new()) }
185+
}
186+
160187
impl_u32_wrapper! {
161188
/// An unsigned block interval.
162189
///

units/src/locktime/absolute/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,33 @@ impl encoding::Encodable for LockTime {
417417
}
418418
}
419419

420+
/// The decoder for the [`LockTime`] type.
421+
#[cfg(feature = "encoding")]
422+
pub struct LockTimeDecoder(encoding::ArrayDecoder<4>);
423+
424+
#[cfg(feature = "encoding")]
425+
impl encoding::Decoder for LockTimeDecoder {
426+
type Output = LockTime;
427+
type Error = encoding::UnexpectedEofError;
428+
429+
#[inline]
430+
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
431+
self.0.push_bytes(bytes)
432+
}
433+
434+
#[inline]
435+
fn end(self) -> Result<Self::Output, Self::Error> {
436+
let n = u32::from_le_bytes(self.0.end()?);
437+
Ok(LockTime::from_consensus(n))
438+
}
439+
}
440+
441+
#[cfg(feature = "encoding")]
442+
impl encoding::Decodable for LockTime {
443+
type Decoder = LockTimeDecoder;
444+
fn decoder() -> Self::Decoder { LockTimeDecoder(encoding::ArrayDecoder::<4>::new()) }
445+
}
446+
420447
impl From<Height> for LockTime {
421448
#[inline]
422449
fn from(h: Height) -> Self { LockTime::Blocks(h) }

0 commit comments

Comments
 (0)