diff --git a/chronik/bitcoinsuite-core/src/error.rs b/chronik/bitcoinsuite-core/src/error.rs index 0b5357014..c04241d18 100644 --- a/chronik/bitcoinsuite-core/src/error.rs +++ b/chronik/bitcoinsuite-core/src/error.rs @@ -1,26 +1,37 @@ // Copyright (c) 2023 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. //! Module for errors in this crate. use thiserror::Error; /// Errors indicating some data doesn't map to some object. #[derive(Debug, Error, PartialEq)] pub enum DataError { /// Expect a fixed length which was not met. #[error( "Invalid length, expected {expected} bytes but got {actual} bytes" )] InvalidLength { /// Expected number of bytes. expected: usize, /// Actual number of bytes. actual: usize, }, + /// Expected bytes with multiple allowed lengths, none of which were met. + #[error( + "Invalid length, expected one of {expected:?} but got {actual} bytes" + )] + InvalidLengthMulti { + /// List of expected number of bytes. + expected: Vec, + /// Actual number of bytes. + actual: usize, + }, + /// Hex contains invalid characters, odd length, etc. #[error("Invalid hex: {0}")] InvalidHex(hex::FromHexError), } diff --git a/chronik/bitcoinsuite-core/src/script/mod.rs b/chronik/bitcoinsuite-core/src/script/mod.rs index 156cf1281..43a9532e0 100644 --- a/chronik/bitcoinsuite-core/src/script/mod.rs +++ b/chronik/bitcoinsuite-core/src/script/mod.rs @@ -1,17 +1,19 @@ // Copyright (c) 2023 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. //! Module for structs and definitions regarding Script. pub mod opcode; mod pubkey; +mod pubkey_variant; #[allow(clippy::module_inception)] mod script; mod script_mut; mod uncompressed_pubkey; pub use self::pubkey::*; +pub use self::pubkey_variant::*; pub use self::script::*; pub use self::script_mut::*; pub use self::uncompressed_pubkey::*; diff --git a/chronik/bitcoinsuite-core/src/script/pubkey_variant.rs b/chronik/bitcoinsuite-core/src/script/pubkey_variant.rs new file mode 100644 index 000000000..a516769bf --- /dev/null +++ b/chronik/bitcoinsuite-core/src/script/pubkey_variant.rs @@ -0,0 +1,67 @@ +// Copyright (c) 2023 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +use std::str::FromStr; + +use crate::{ + error::DataError, + script::{PubKey, UncompressedPubKey}, +}; + +/// Either a compressed [`PubKey`] or a [`UncompressedPubKey`], determined by +/// the number of bytes: +/// ``` +/// # use bitcoinsuite_core::{ +/// # error::DataError, +/// # script::{PubKey, PubKeyVariant, UncompressedPubKey}, +/// # }; +/// assert_eq!( +/// PubKeyVariant::try_from([2; 33].as_ref()), +/// Ok(PubKeyVariant::Compressed(PubKey([2; 33]))), +/// ); +/// assert_eq!( +/// PubKeyVariant::try_from([4; 65].as_ref()), +/// Ok(PubKeyVariant::Uncompressed(UncompressedPubKey([4; 65]))), +/// ); +/// assert_eq!( +/// PubKeyVariant::try_from([4; 66].as_ref()), +/// Err(DataError::InvalidLengthMulti { +/// expected: vec![33, 65], +/// actual: 66, +/// }), +/// ); +/// ``` +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PubKeyVariant { + /// Compressed public key. + Compressed(PubKey), + /// Uncompressed public key. + Uncompressed(UncompressedPubKey), +} + +impl TryFrom<&'_ [u8]> for PubKeyVariant { + type Error = DataError; + + fn try_from(value: &'_ [u8]) -> Result { + if let Ok(array) = <[u8; PubKey::SIZE]>::try_from(value) { + return Ok(PubKeyVariant::Compressed(PubKey(array))); + } + if let Ok(array) = <[u8; UncompressedPubKey::SIZE]>::try_from(value) { + return Ok(PubKeyVariant::Uncompressed(UncompressedPubKey(array))); + } + Err(DataError::InvalidLengthMulti { + expected: vec![PubKey::SIZE, UncompressedPubKey::SIZE], + actual: value.len(), + }) + } +} + +impl FromStr for PubKeyVariant { + type Err = DataError; + + fn from_str(s: &str) -> Result { + let pubkey = hex::decode(s).map_err(DataError::InvalidHex)?; + pubkey.as_slice().try_into() + } +}