Merge rust-bitcoin/rust-bitcoin#2642: Automated nightly rustfmt (2024-03-31)

a565db9fdd 2024-03-31 automated rustfmt nightly (Fmt Bot)

Pull request description:

  Automated nightly `rustfmt` changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

ACKs for top commit:
  apoelstra:
    ACK a565db9fdd checked that it matches `cargo fmt` locally but did not actually read the diff because it is too big

Tree-SHA512: 48f55c043a39f496b954d646c3ffe4f90942bb02b4dfe0cc3ed44f4bc09cc44f12ee8abaabeb25a524c5a4f8c82aa85562cc247ec39d5cf878f97984735e2a00
This commit is contained in:
Andrew Poelstra 2024-04-01 14:18:46 +00:00
commit c20cb4da53
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
37 changed files with 423 additions and 362 deletions

View File

@ -101,7 +101,11 @@ impl TooShortError {
impl fmt::Display for TooShortError { impl fmt::Display for TooShortError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "base58 decoded data was not long enough, must be at least 4 byte: {}", self.length) write!(
f,
"base58 decoded data was not long enough, must be at least 4 byte: {}",
self.length
)
} }
} }

View File

@ -268,10 +268,7 @@ mod tests {
Some(hex!("00f8917303bfa8ef24f292e8fa1419b20460ba064d")) Some(hex!("00f8917303bfa8ef24f292e8fa1419b20460ba064d"))
); );
// Non Base58 char. // Non Base58 char.
assert_eq!( assert_eq!(decode("¢").unwrap_err(), InvalidCharacterError { invalid: 194 });
decode("¢").unwrap_err(),
InvalidCharacterError { invalid: 194 }
);
} }
#[test] #[test]

View File

@ -133,7 +133,11 @@ impl ColdStorage {
fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() } fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() }
/// Signs `psbt` with this signer. /// Signs `psbt` with this signer.
fn sign_psbt<C: Signing + Verification>(&self, secp: &Secp256k1<C>, mut psbt: Psbt) -> Result<Psbt> { fn sign_psbt<C: Signing + Verification>(
&self,
secp: &Secp256k1<C>,
mut psbt: Psbt,
) -> Result<Psbt> {
match psbt.sign(&self.master_xpriv, secp) { match psbt.sign(&self.master_xpriv, secp) {
Ok(keys) => assert_eq!(keys.len(), 1), Ok(keys) => assert_eq!(keys.len(), 1),
Err((_, e)) => { Err((_, e)) => {

View File

@ -379,10 +379,7 @@ mod test {
use crate::blockdata::locktime::absolute; use crate::blockdata::locktime::absolute;
use crate::blockdata::transaction; use crate::blockdata::transaction;
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
use crate::{ use crate::{Amount, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid, Witness};
Amount, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid,
Witness,
};
fn dummy_tx(nonce: &[u8]) -> Transaction { fn dummy_tx(nonce: &[u8]) -> Transaction {
Transaction { Transaction {

View File

@ -17,8 +17,8 @@ use secp256k1::{Secp256k1, XOnlyPublicKey};
use crate::crypto::key::{CompressedPublicKey, Keypair, PrivateKey}; use crate::crypto::key::{CompressedPublicKey, Keypair, PrivateKey};
use crate::internal_macros::impl_bytes_newtype; use crate::internal_macros::impl_bytes_newtype;
use crate::prelude::*;
use crate::network::NetworkKind; use crate::network::NetworkKind;
use crate::prelude::*;
/// Version bytes for extended public keys on the Bitcoin network. /// Version bytes for extended public keys on the Bitcoin network.
const VERSION_BYTES_MAINNET_PUBLIC: [u8; 4] = [0x04, 0x88, 0xB2, 0x1E]; const VERSION_BYTES_MAINNET_PUBLIC: [u8; 4] = [0x04, 0x88, 0xB2, 0x1E];
@ -490,7 +490,7 @@ pub enum Error {
/// `PublicKey` hex should be 66 or 130 digits long. /// `PublicKey` hex should be 66 or 130 digits long.
InvalidPublicKeyHexLength(usize), InvalidPublicKeyHexLength(usize),
/// Base58 decoded data was an invalid length. /// Base58 decoded data was an invalid length.
InvalidBase58PayloadLength(InvalidBase58PayloadLengthError) InvalidBase58PayloadLength(InvalidBase58PayloadLengthError),
} }
internals::impl_from_infallible!(Error); internals::impl_from_infallible!(Error);
@ -885,7 +885,11 @@ impl InvalidBase58PayloadLengthError {
impl fmt::Display for InvalidBase58PayloadLengthError { impl fmt::Display for InvalidBase58PayloadLengthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "decoded base58 xpriv/xpub data was an invalid length: {} (expected 78)", self.length) write!(
f,
"decoded base58 xpriv/xpub data was an invalid length: {} (expected 78)",
self.length
)
} }
} }

View File

@ -357,9 +357,7 @@ pub struct DisabledLockTimeError(u32);
impl DisabledLockTimeError { impl DisabledLockTimeError {
/// Accessor for the `u32` whose "disable" flag was set, preventing /// Accessor for the `u32` whose "disable" flag was set, preventing
/// it from being parsed as a relative locktime. /// it from being parsed as a relative locktime.
pub fn disabled_locktime_value(&self) -> u32 { pub fn disabled_locktime_value(&self) -> u32 { self.0 }
self.0
}
} }
impl fmt::Display for DisabledLockTimeError { impl fmt::Display for DisabledLockTimeError {

View File

@ -184,8 +184,7 @@ impl Script {
let ver_opcode = Opcode::from(self.0[0]); // Version 0 or PUSHNUM_1-PUSHNUM_16 let ver_opcode = Opcode::from(self.0[0]); // Version 0 or PUSHNUM_1-PUSHNUM_16
let push_opbyte = self.0[1]; // Second byte push opcode 2-40 bytes let push_opbyte = self.0[1]; // Second byte push opcode 2-40 bytes
if push_opbyte < OP_PUSHBYTES_2.to_u8() || push_opbyte > OP_PUSHBYTES_40.to_u8() if push_opbyte < OP_PUSHBYTES_2.to_u8() || push_opbyte > OP_PUSHBYTES_40.to_u8() {
{
return None; return None;
} }
// Check that the rest of the script has the correct size // Check that the rest of the script has the correct size
@ -400,7 +399,6 @@ impl Script {
#[deprecated(since = "0.32.0", note = "use minimal_non_dust and friends")] #[deprecated(since = "0.32.0", note = "use minimal_non_dust and friends")]
pub fn dust_value(&self) -> crate::Amount { self.minimal_non_dust() } pub fn dust_value(&self) -> crate::Amount { self.minimal_non_dust() }
/// Returns the minimum value an output with this script should have in order to be /// Returns the minimum value an output with this script should have in order to be
/// broadcastable on today's Bitcoin network. /// broadcastable on today's Bitcoin network.
/// ///

View File

@ -16,8 +16,8 @@ use crate::blockdata::script::{
use crate::key::{ use crate::key::{
PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey, WPubkeyHash, PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey, WPubkeyHash,
}; };
use crate::taproot::TapNodeHash;
use crate::prelude::*; use crate::prelude::*;
use crate::taproot::TapNodeHash;
/// An owned, growable script. /// An owned, growable script.
/// ///

View File

@ -25,11 +25,11 @@ use crate::blockdata::script::{Script, ScriptBuf};
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::blockdata::FeeRate; use crate::blockdata::FeeRate;
use crate::consensus::{encode, Decodable, Encodable}; use crate::consensus::{encode, Decodable, Encodable};
use crate::error::{PrefixedHexError, UnprefixedHexError, ContainsPrefixError, MissingPrefixError}; use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError};
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode}; use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
use crate::prelude::*;
#[cfg(doc)] #[cfg(doc)]
use crate::sighash::{EcdsaSighashType, TapSighashType}; use crate::sighash::{EcdsaSighashType, TapSighashType};
use crate::prelude::*;
use crate::{Amount, SignedAmount, VarInt}; use crate::{Amount, SignedAmount, VarInt};
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
@ -835,13 +835,7 @@ impl Transaction {
size += self size += self
.input .input
.iter() .iter()
.map(|input| { .map(|input| if uses_segwit { input.total_size() } else { input.base_size() })
if uses_segwit {
input.total_size()
} else {
input.base_size()
}
})
.sum::<usize>(); .sum::<usize>();
size += VarInt::from(self.output.len()).size(); size += VarInt::from(self.output.len()).size();
@ -1796,10 +1790,7 @@ mod tests {
let tx_bytes = hex!("0000fd000001021921212121212121212121f8b372b0239cc1dff600000000004f4f4f4f4f4f4f4f000000000000000000000000000000333732343133380d000000000000000000000000000000ff000000000009000dff000000000000000800000000000000000d"); let tx_bytes = hex!("0000fd000001021921212121212121212121f8b372b0239cc1dff600000000004f4f4f4f4f4f4f4f000000000000000000000000000000333732343133380d000000000000000000000000000000ff000000000009000dff000000000000000800000000000000000d");
let tx: Result<Transaction, _> = deserialize(&tx_bytes); let tx: Result<Transaction, _> = deserialize(&tx_bytes);
assert!(tx.is_err()); assert!(tx.is_err());
assert!(tx assert!(tx.unwrap_err().to_string().contains("witness flag set but no witnesses present"));
.unwrap_err()
.to_string()
.contains("witness flag set but no witnesses present"));
} }
#[test] #[test]
@ -1960,10 +1951,7 @@ mod tests {
format!("{:x}", tx.compute_txid()), format!("{:x}", tx.compute_txid()),
"9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec" "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec"
); );
assert_eq!( assert_eq!(format!("{:.10x}", tx.compute_txid()), "9652aa62b0");
format!("{:.10x}", tx.compute_txid()),
"9652aa62b0"
);
assert_eq!(tx.weight(), Weight::from_wu(2718)); assert_eq!(tx.weight(), Weight::from_wu(2718));
// non-segwit tx from my mempool // non-segwit tx from my mempool

View File

@ -13,8 +13,8 @@ use io::{BufRead, Write};
use crate::consensus::encode::{Error, MAX_VEC_SIZE}; use crate::consensus::encode::{Error, MAX_VEC_SIZE};
use crate::consensus::{Decodable, Encodable, WriteExt}; use crate::consensus::{Decodable, Encodable, WriteExt};
use crate::crypto::ecdsa; use crate::crypto::ecdsa;
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
use crate::prelude::*; use crate::prelude::*;
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
use crate::{Script, VarInt}; use crate::{Script, VarInt};
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade]. /// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
@ -235,11 +235,7 @@ impl Witness {
/// Creates a new empty [`Witness`]. /// Creates a new empty [`Witness`].
#[inline] #[inline]
pub const fn new() -> Self { pub const fn new() -> Self {
Witness { Witness { content: Vec::new(), witness_elements: 0, indices_start: 0 }
content: Vec::new(),
witness_elements: 0,
indices_start: 0,
}
} }
/// Creates a witness required to spend a P2WPKH output. /// Creates a witness required to spend a P2WPKH output.
@ -506,8 +502,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( InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) {
).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",
@ -557,7 +552,7 @@ impl Default for Witness {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use hex::{test_hex_unwrap as hex}; use hex::test_hex_unwrap as hex;
use super::*; use super::*;
use crate::consensus::{deserialize, serialize}; use crate::consensus::{deserialize, serialize};

View File

@ -20,7 +20,7 @@ use core::{fmt, mem, u32};
use hashes::{sha256, sha256d, Hash}; use hashes::{sha256, sha256d, Hash};
use hex::error::{InvalidCharError, OddLengthStringError}; use hex::error::{InvalidCharError, OddLengthStringError};
use internals::write_err; use internals::write_err;
use io::{Cursor, BufRead, Read, Write}; use io::{BufRead, Cursor, Read, Write};
use crate::bip152::{PrefilledTransaction, ShortId}; use crate::bip152::{PrefilledTransaction, ShortId};
use crate::bip158::{FilterHash, FilterHeader}; use crate::bip158::{FilterHash, FilterHeader};
@ -109,7 +109,7 @@ pub enum FromHexError {
/// Purported hex string had odd length. /// Purported hex string had odd length.
OddLengthString(OddLengthStringError), OddLengthString(OddLengthStringError),
/// Decoding error. /// Decoding error.
Decode(DecodeError<InvalidCharError>) Decode(DecodeError<InvalidCharError>),
} }
impl fmt::Display for FromHexError { impl fmt::Display for FromHexError {
@ -406,13 +406,18 @@ macro_rules! impl_int_encodable {
($ty:ident, $meth_dec:ident, $meth_enc:ident) => { ($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
impl Decodable for $ty { impl Decodable for $ty {
#[inline] #[inline]
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> core::result::Result<Self, Error> { fn consensus_decode<R: BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<Self, Error> {
ReadExt::$meth_dec(r) ReadExt::$meth_dec(r)
} }
} }
impl Encodable for $ty { impl Encodable for $ty {
#[inline] #[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> core::result::Result<usize, io::Error> { fn consensus_encode<W: Write + ?Sized>(
&self,
w: &mut W,
) -> core::result::Result<usize, io::Error> {
w.$meth_enc(*self)?; w.$meth_enc(*self)?;
Ok(mem::size_of::<$ty>()) Ok(mem::size_of::<$ty>())
} }
@ -588,7 +593,9 @@ macro_rules! impl_array {
impl Decodable for [u8; $size] { impl Decodable for [u8; $size] {
#[inline] #[inline]
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> core::result::Result<Self, Error> { fn consensus_decode<R: BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<Self, Error> {
let mut ret = [0; $size]; let mut ret = [0; $size];
r.read_slice(&mut ret)?; r.read_slice(&mut ret)?;
Ok(ret) Ok(ret)
@ -632,7 +639,10 @@ macro_rules! impl_vec {
($type: ty) => { ($type: ty) => {
impl Encodable for Vec<$type> { impl Encodable for Vec<$type> {
#[inline] #[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> core::result::Result<usize, io::Error> { fn consensus_encode<W: Write + ?Sized>(
&self,
w: &mut W,
) -> core::result::Result<usize, io::Error> {
let mut len = 0; let mut len = 0;
len += VarInt(self.len() as u64).consensus_encode(w)?; len += VarInt(self.len() as u64).consensus_encode(w)?;
for c in self.iter() { for c in self.iter() {
@ -1293,6 +1303,9 @@ mod tests {
let mut hex = include_str!("../../tests/data/previous_tx_0_hex").to_string(); // An arbitrary transaction. let mut hex = include_str!("../../tests/data/previous_tx_0_hex").to_string(); // An arbitrary transaction.
hex.push_str("abcdef"); hex.push_str("abcdef");
assert!(matches!(deserialize_hex::<Transaction>(&hex).unwrap_err(), FromHexError::Decode(DecodeError::TooManyBytes))); assert!(matches!(
deserialize_hex::<Transaction>(&hex).unwrap_err(),
FromHexError::Decode(DecodeError::TooManyBytes)
));
} }
} }

View File

@ -15,8 +15,8 @@ pub mod validation;
use core::fmt; use core::fmt;
use io::{Read, BufRead};
use internals::write_err; use internals::write_err;
use io::{BufRead, Read};
use crate::consensus; use crate::consensus;
@ -40,7 +40,9 @@ struct IterReader<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> {
} }
impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> IterReader<E, I> { impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> IterReader<E, I> {
pub(crate) fn new(iterator: I) -> Self { IterReader { iterator: iterator.fuse(), buf: None, error: None } } pub(crate) fn new(iterator: I) -> Self {
IterReader { iterator: iterator.fuse(), buf: None, error: None }
}
fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> { fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
let result = T::consensus_decode(&mut self); let result = T::consensus_decode(&mut self);
@ -94,11 +96,11 @@ impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> BufRead for IterReader<E,
Some(Ok(byte)) => { Some(Ok(byte)) => {
self.buf = Some(byte); self.buf = Some(byte);
Ok(core::slice::from_ref(self.buf.as_ref().expect("we've just filled it"))) Ok(core::slice::from_ref(self.buf.as_ref().expect("we've just filled it")))
}, }
Some(Err(error)) => { Some(Err(error)) => {
self.error = Some(error); self.error = Some(error);
Err(io::ErrorKind::Other.into()) Err(io::ErrorKind::Other.into())
}, }
None => Ok(&[]), None => Ok(&[]),
} }
} }
@ -130,7 +132,8 @@ impl<E: fmt::Debug> fmt::Display for DecodeError<E> {
use DecodeError::*; use DecodeError::*;
match *self { match *self {
TooManyBytes => write!(f, "attempted to decode object from an iterator that yielded too many bytes"), TooManyBytes =>
write!(f, "attempted to decode object from an iterator that yielded too many bytes"),
Consensus(ref e) => write_err!(f, "invalid consensus encoding"; e), Consensus(ref e) => write_err!(f, "invalid consensus encoding"; e),
Other(ref other) => write!(f, "other decoding error: {:?}", other), Other(ref other) => write!(f, "other decoding error: {:?}", other),
} }

View File

@ -136,7 +136,6 @@ impl From<Network> for Params {
fn from(value: Network) -> Self { Self::new(value) } fn from(value: Network) -> Self { Self::new(value) }
} }
impl From<&Network> for Params { impl From<&Network> for Params {
fn from(value: &Network) -> Self { Self::new(*value) } fn from(value: &Network) -> Self { Self::new(*value) }
} }

View File

@ -17,9 +17,9 @@ use serde::de::{SeqAccess, Unexpected, Visitor};
use serde::ser::SerializeSeq; use serde::ser::SerializeSeq;
use serde::{Deserializer, Serializer}; use serde::{Deserializer, Serializer};
use crate::consensus::{DecodeError, IterReader};
use super::encode::Error as ConsensusError; use super::encode::Error as ConsensusError;
use super::{Decodable, Encodable}; use super::{Decodable, Encodable};
use crate::consensus::{DecodeError, IterReader};
/// Hex-encoding strategy /// Hex-encoding strategy
pub struct Hex<Case = hex::Lower>(PhantomData<Case>) pub struct Hex<Case = hex::Lower>(PhantomData<Case>)
@ -75,9 +75,7 @@ pub mod hex {
pub struct Encoder<C: Case>(BufEncoder<{ HEX_BUF_SIZE }>, PhantomData<C>); pub struct Encoder<C: Case>(BufEncoder<{ HEX_BUF_SIZE }>, PhantomData<C>);
impl<C: Case> From<super::Hex<C>> for Encoder<C> { impl<C: Case> From<super::Hex<C>> for Encoder<C> {
fn from(_: super::Hex<C>) -> Self { fn from(_: super::Hex<C>) -> Self { Encoder(BufEncoder::new(), Default::default()) }
Encoder(BufEncoder::new(), Default::default())
}
} }
impl<C: Case> super::EncodeBytes for Encoder<C> { impl<C: Case> super::EncodeBytes for Encoder<C> {

View File

@ -224,9 +224,7 @@ pub struct SortKey(ArrayVec<u8, 65>);
impl fmt::Display for PublicKey { impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.with_serialized(|bytes| { self.with_serialized(|bytes| fmt::Display::fmt(&bytes.as_hex(), f))
fmt::Display::fmt(&bytes.as_hex(), f)
})
} }
} }
@ -239,14 +237,14 @@ impl FromStr for PublicKey {
66 => { 66 => {
let bytes = <[u8; 33]>::from_hex(s).map_err(|e| match e { let bytes = <[u8; 33]>::from_hex(s).map_err(|e| match e {
InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()), InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()),
InvalidLength(_) => unreachable!("length checked already") InvalidLength(_) => unreachable!("length checked already"),
})?; })?;
Ok(PublicKey::from_slice(&bytes)?) Ok(PublicKey::from_slice(&bytes)?)
}, }
130 => { 130 => {
let bytes = <[u8; 65]>::from_hex(s).map_err(|e| match e { let bytes = <[u8; 65]>::from_hex(s).map_err(|e| match e {
InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()), InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()),
InvalidLength(_) => unreachable!("length checked already") InvalidLength(_) => unreachable!("length checked already"),
})?; })?;
Ok(PublicKey::from_slice(&bytes)?) Ok(PublicKey::from_slice(&bytes)?)
} }
@ -441,7 +439,10 @@ impl PrivateKey {
pub fn to_bytes(self) -> Vec<u8> { self.inner[..].to_vec() } pub fn to_bytes(self) -> Vec<u8> { self.inner[..].to_vec() }
/// Deserialize a private key from a slice /// Deserialize a private key from a slice
pub fn from_slice(data: &[u8], network: impl Into<NetworkKind>) -> Result<PrivateKey, secp256k1::Error> { pub fn from_slice(
data: &[u8],
network: impl Into<NetworkKind>,
) -> Result<PrivateKey, secp256k1::Error> {
Ok(PrivateKey::new(secp256k1::SecretKey::from_slice(data)?, network)) Ok(PrivateKey::new(secp256k1::SecretKey::from_slice(data)?, network))
} }
@ -948,8 +949,10 @@ impl fmt::Display for FromWifError {
match *self { match *self {
Base58(ref e) => write_err!(f, "invalid base58"; e), Base58(ref e) => write_err!(f, "invalid base58"; e),
InvalidBase58PayloadLength(ref e) => write_err!(f, "decoded base58 data was an invalid length"; e), InvalidBase58PayloadLength(ref e) =>
InvalidAddressVersion(ref e) => write_err!(f, "decoded base58 data contained an invalid address version btye"; e), write_err!(f, "decoded base58 data was an invalid length"; e),
InvalidAddressVersion(ref e) =>
write_err!(f, "decoded base58 data contained an invalid address version btye"; e),
Secp256k1(ref e) => write_err!(f, "private key validation failed"; e), Secp256k1(ref e) => write_err!(f, "private key validation failed"; e),
} }
} }
@ -978,7 +981,9 @@ impl From<secp256k1::Error> for FromWifError {
} }
impl From<InvalidBase58PayloadLengthError> for FromWifError { impl From<InvalidBase58PayloadLengthError> for FromWifError {
fn from(e: InvalidBase58PayloadLengthError) -> FromWifError { Self::InvalidBase58PayloadLength(e) } fn from(e: InvalidBase58PayloadLengthError) -> FromWifError {
Self::InvalidBase58PayloadLength(e)
}
} }
impl From<InvalidAddressVersionError> for FromWifError { impl From<InvalidAddressVersionError> for FromWifError {
@ -1004,7 +1009,8 @@ impl fmt::Display for ParsePublicKeyError {
match self { match self {
Encoding(e) => write_err!(f, "string error"; e), Encoding(e) => write_err!(f, "string error"; e),
InvalidChar(char) => write!(f, "hex error {}", char), InvalidChar(char) => write!(f, "hex error {}", char),
InvalidHexLength(got) => write!(f, "pubkey string should be 66 or 130 digits long, got: {}", got), InvalidHexLength(got) =>
write!(f, "pubkey string should be 66 or 130 digits long, got: {}", got),
} }
} }
} }
@ -1041,7 +1047,7 @@ impl fmt::Display for ParseCompressedPublicKeyError {
use ParseCompressedPublicKeyError::*; use ParseCompressedPublicKeyError::*;
match self { match self {
Secp256k1(e) => write_err!(f, "secp256k1 error"; e), Secp256k1(e) => write_err!(f, "secp256k1 error"; e),
Hex(e) => write_err!(f, "invalid hex"; e) Hex(e) => write_err!(f, "invalid hex"; e),
} }
} }
} }
@ -1463,19 +1469,32 @@ mod tests {
assert_eq!(s.len(), 130); assert_eq!(s.len(), 130);
let res = PublicKey::from_str(s); let res = PublicKey::from_str(s);
assert!(res.is_err()); assert!(res.is_err());
assert_eq!(res.unwrap_err(), ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(secp256k1::Error::InvalidPublicKey))); assert_eq!(
res.unwrap_err(),
ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(
secp256k1::Error::InvalidPublicKey
))
);
let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd169"; let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd169";
assert_eq!(s.len(), 66); assert_eq!(s.len(), 66);
let res = PublicKey::from_str(s); let res = PublicKey::from_str(s);
assert!(res.is_err()); assert!(res.is_err());
assert_eq!(res.unwrap_err(), ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(secp256k1::Error::InvalidPublicKey))); assert_eq!(
res.unwrap_err(),
ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(
secp256k1::Error::InvalidPublicKey
))
);
let s = "062e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"; let s = "062e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133";
assert_eq!(s.len(), 130); assert_eq!(s.len(), 130);
let res = PublicKey::from_str(s); let res = PublicKey::from_str(s);
assert!(res.is_err()); assert!(res.is_err());
assert_eq!(res.unwrap_err(), ParsePublicKeyError::Encoding(FromSliceError::InvalidKeyPrefix(6))); assert_eq!(
res.unwrap_err(),
ParsePublicKeyError::Encoding(FromSliceError::InvalidKeyPrefix(6))
);
let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b13g"; let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b13g";
assert_eq!(s.len(), 130); assert_eq!(s.len(), 130);

View File

@ -19,8 +19,8 @@ use io::Write;
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::consensus::{encode, Encodable}; use crate::consensus::{encode, Encodable};
use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX};
use crate::prelude::*; use crate::prelude::*;
use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX};
use crate::{transaction, Amount, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut}; use crate::{transaction, Amount, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut};
/// Used for signature hash for invalid use of SIGHASH_SINGLE. /// Used for signature hash for invalid use of SIGHASH_SINGLE.
@ -607,8 +607,12 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
// sha_sequences (32): the SHA256 of the serialization of all input nSequence. // sha_sequences (32): the SHA256 of the serialization of all input nSequence.
if !anyone_can_pay { if !anyone_can_pay {
self.common_cache().prevouts.consensus_encode(writer)?; self.common_cache().prevouts.consensus_encode(writer)?;
self.taproot_cache(prevouts.get_all().map_err(SigningDataError::sighash)?).amounts.consensus_encode(writer)?; self.taproot_cache(prevouts.get_all().map_err(SigningDataError::sighash)?)
self.taproot_cache(prevouts.get_all().map_err(SigningDataError::sighash)?).script_pubkeys.consensus_encode(writer)?; .amounts
.consensus_encode(writer)?;
self.taproot_cache(prevouts.get_all().map_err(SigningDataError::sighash)?)
.script_pubkeys
.consensus_encode(writer)?;
self.common_cache().sequences.consensus_encode(writer)?; self.common_cache().sequences.consensus_encode(writer)?;
} }
@ -668,7 +672,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
.ok_or(TaprootError::SingleMissingOutput(SingleMissingOutputError { .ok_or(TaprootError::SingleMissingOutput(SingleMissingOutputError {
input_index, input_index,
outputs_length: self.tx.borrow().output.len(), outputs_length: self.tx.borrow().output.len(),
})).map_err(SigningDataError::Sighash)? }))
.map_err(SigningDataError::Sighash)?
.consensus_encode(&mut enc)?; .consensus_encode(&mut enc)?;
let hash = sha256::Hash::from_engine(enc); let hash = sha256::Hash::from_engine(enc);
hash.consensus_encode(writer)?; hash.consensus_encode(writer)?;
@ -704,7 +709,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
annex, annex,
leaf_hash_code_separator, leaf_hash_code_separator,
sighash_type, sighash_type,
).map_err(SigningDataError::unwrap_sighash)?; )
.map_err(SigningDataError::unwrap_sighash)?;
Ok(TapSighash::from_engine(enc)) Ok(TapSighash::from_engine(enc))
} }
@ -723,7 +729,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
None, None,
None, None,
sighash_type, sighash_type,
).map_err(SigningDataError::unwrap_sighash)?; )
.map_err(SigningDataError::unwrap_sighash)?;
Ok(TapSighash::from_engine(enc)) Ok(TapSighash::from_engine(enc))
} }
@ -746,7 +753,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
None, None,
Some((leaf_hash.into(), 0xFFFFFFFF)), Some((leaf_hash.into(), 0xFFFFFFFF)),
sighash_type, sighash_type,
).map_err(SigningDataError::unwrap_sighash)?; )
.map_err(SigningDataError::unwrap_sighash)?;
Ok(TapSighash::from_engine(enc)) Ok(TapSighash::from_engine(enc))
} }
@ -830,7 +838,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
&script_code, &script_code,
value, value,
sighash_type, sighash_type,
).map_err(SigningDataError::unwrap_sighash)?; )
.map_err(SigningDataError::unwrap_sighash)?;
Ok(SegwitV0Sighash::from_engine(enc)) Ok(SegwitV0Sighash::from_engine(enc))
} }
@ -849,7 +858,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
witness_script, witness_script,
value, value,
sighash_type, sighash_type,
).map_err(SigningDataError::unwrap_sighash)?; )
.map_err(SigningDataError::unwrap_sighash)?;
Ok(SegwitV0Sighash::from_engine(enc)) Ok(SegwitV0Sighash::from_engine(enc))
} }
@ -1215,9 +1225,7 @@ pub enum P2wpkhError {
internals::impl_from_infallible!(P2wpkhError); internals::impl_from_infallible!(P2wpkhError);
impl From<transaction::InputsIndexError> for P2wpkhError { impl From<transaction::InputsIndexError> for P2wpkhError {
fn from(value: transaction::InputsIndexError) -> Self { fn from(value: transaction::InputsIndexError) -> Self { P2wpkhError::Sighash(value) }
P2wpkhError::Sighash(value)
}
} }
impl fmt::Display for P2wpkhError { impl fmt::Display for P2wpkhError {
@ -1400,17 +1408,13 @@ impl<E> SigningDataError<E> {
} }
} }
fn sighash<E2: Into<E>>(error: E2) -> Self { fn sighash<E2: Into<E>>(error: E2) -> Self { Self::Sighash(error.into()) }
Self::Sighash(error.into())
}
} }
// We cannot simultaneously impl `From<E>`. it was determined that this alternative requires less // We cannot simultaneously impl `From<E>`. it was determined that this alternative requires less
// manual `map_err` calls. // manual `map_err` calls.
impl<E> From<io::Error> for SigningDataError<E> { impl<E> From<io::Error> for SigningDataError<E> {
fn from(value: io::Error) -> Self { fn from(value: io::Error) -> Self { Self::Io(value) }
Self::Io(value)
}
} }
impl<E: fmt::Display> fmt::Display for SigningDataError<E> { impl<E: fmt::Display> fmt::Display for SigningDataError<E> {

View File

@ -10,9 +10,9 @@ use core::fmt;
use internals::write_err; use internals::write_err;
use io::Write; use io::Write;
use crate::prelude::*;
use crate::sighash::{InvalidSighashTypeError, TapSighashType}; use crate::sighash::{InvalidSighashTypeError, TapSighashType};
use crate::taproot::serialized_signature::{self, SerializedSignature}; use crate::taproot::serialized_signature::{self, SerializedSignature};
use crate::prelude::*;
/// A BIP340-341 serialized taproot signature with the corresponding hash type. /// A BIP340-341 serialized taproot signature with the corresponding hash type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]

View File

@ -12,10 +12,12 @@ pub use crate::{
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::hashes::Hash;
use super::*; use super::*;
use crate::hashes::Hash;
use crate::{LegacySighash, SegwitV0Sighash, TapSighash, PubkeyHash, WPubkeyHash, WScriptHash, ScriptHash, XKeyIdentifier}; use crate::{
LegacySighash, PubkeyHash, ScriptHash, SegwitV0Sighash, TapSighash, WPubkeyHash,
WScriptHash, XKeyIdentifier,
};
#[test] #[test]
fn hash_display() { fn hash_display() {
@ -45,18 +47,9 @@ mod tests {
"dabc11914abcd8072900042a2681e52f8dba99ce82e224f97b5fdb7cd4b9c803", "dabc11914abcd8072900042a2681e52f8dba99ce82e224f97b5fdb7cd4b9c803",
); );
assert_eq!( assert_eq!(PubkeyHash::hash(&[]).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",);
PubkeyHash::hash(&[]).to_string(), assert_eq!(ScriptHash::hash(&[]).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",);
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", assert_eq!(WPubkeyHash::hash(&[]).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",);
);
assert_eq!(
ScriptHash::hash(&[]).to_string(),
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",
);
assert_eq!(
WPubkeyHash::hash(&[]).to_string(),
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",
);
assert_eq!( assert_eq!(
WScriptHash::hash(&[]).to_string(), WScriptHash::hash(&[]).to_string(),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",

View File

@ -107,7 +107,10 @@ macro_rules! impl_bytes_newtype {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl $crate::serde::Serialize for $t { impl $crate::serde::Serialize for $t {
fn serialize<S: $crate::serde::Serializer>(&self, s: S) -> core::result::Result<S::Ok, S::Error> { fn serialize<S: $crate::serde::Serializer>(
&self,
s: S,
) -> core::result::Result<S::Ok, S::Error> {
if s.is_human_readable() { if s.is_human_readable() {
s.collect_str(self) s.collect_str(self)
} else { } else {
@ -118,7 +121,9 @@ macro_rules! impl_bytes_newtype {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl<'de> $crate::serde::Deserialize<'de> for $t { impl<'de> $crate::serde::Deserialize<'de> for $t {
fn deserialize<D: $crate::serde::Deserializer<'de>>(d: D) -> core::result::Result<$t, D::Error> { fn deserialize<D: $crate::serde::Deserializer<'de>>(
d: D,
) -> core::result::Result<$t, D::Error> {
if d.is_human_readable() { if d.is_human_readable() {
struct HexVisitor; struct HexVisitor;

View File

@ -29,17 +29,13 @@
//! happen the implementations diverge one day. //! happen the implementations diverge one day.
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] #![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
// Experimental features we need. // Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(bench, feature(test))] #![cfg_attr(bench, feature(test))]
// Coding conventions. // Coding conventions.
#![warn(missing_docs)] #![warn(missing_docs)]
// Instead of littering the codebase for non-fuzzing code just globally allow. // Instead of littering the codebase for non-fuzzing code just globally allow.
#![cfg_attr(fuzzing, allow(dead_code, unused_imports))] #![cfg_attr(fuzzing, allow(dead_code, unused_imports))]
// Exclude lints we don't think are valuable. // Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![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. #![allow(clippy::manual_range_contains)] // More readable than clippy's format.

View File

@ -540,9 +540,10 @@ impl std::error::Error for MerkleBlockError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hex::{test_hex_unwrap as hex}; use hex::test_hex_unwrap as hex;
#[cfg(feature = "rand-std")] #[cfg(feature = "rand-std")]
use secp256k1::rand::prelude::*; use secp256k1::rand::prelude::*;
use super::*; use super::*;
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
@ -832,7 +833,8 @@ mod tests {
000000000300000000000003000000000200000000ff00000000c7f1ccb10407\ 000000000300000000000003000000000200000000ff00000000c7f1ccb10407\
00000000000000ccb100c76538b100000004bfa9c251681b1b00040000000025\ 00000000000000ccb100c76538b100000004bfa9c251681b1b00040000000025\
00000004bfaac251681b1b25\ 00000004bfaac251681b1b25\
"); "
);
let deser = crate::consensus::deserialize::<MerkleBlock>(&bytes); let deser = crate::consensus::deserialize::<MerkleBlock>(&bytes);
assert!(deser.is_err()); assert!(deser.is_err());
} }

View File

@ -742,8 +742,7 @@ mod test {
0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2
]); ]);
let preimage = let preimage = RawNetworkMessage::new(Magic::BITCOIN, NetworkMessage::GetAddr);
RawNetworkMessage::new(Magic::BITCOIN, NetworkMessage::GetAddr);
assert!(msg.is_ok()); assert!(msg.is_ok());
let msg: RawNetworkMessage = msg.unwrap(); let msg: RawNetworkMessage = msg.unwrap();
assert_eq!(preimage.magic, msg.magic); assert_eq!(preimage.magic, msg.magic);

View File

@ -11,10 +11,10 @@ use io::{BufRead, Write};
use crate::consensus::{encode, Decodable, Encodable, ReadExt}; use crate::consensus::{encode, Decodable, Encodable, ReadExt};
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::p2p;
use crate::p2p::address::Address; use crate::p2p::address::Address;
use crate::p2p::ServiceFlags; use crate::p2p::ServiceFlags;
use crate::prelude::*; use crate::prelude::*;
use crate::p2p;
/// Some simple messages /// Some simple messages

View File

@ -18,7 +18,7 @@ use crate::blockdata::block::BlockHash;
use crate::consensus::encode::{self, Decodable, Encodable}; use crate::consensus::encode::{self, Decodable, Encodable};
#[cfg(doc)] #[cfg(doc)]
use crate::consensus::Params; use crate::consensus::Params;
use crate::error::{PrefixedHexError, UnprefixedHexError, ContainsPrefixError, MissingPrefixError}; use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError};
use crate::Network; use crate::Network;
/// Implement traits and methods shared by `Target` and `Work`. /// Implement traits and methods shared by `Target` and `Work`.
@ -44,17 +44,23 @@ macro_rules! do_impl {
impl fmt::Display for $ty { impl fmt::Display for $ty {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
fmt::Display::fmt(&self.0, f)
}
} }
impl fmt::LowerHex for $ty { impl fmt::LowerHex for $ty {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result { fmt::LowerHex::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
} }
impl fmt::UpperHex for $ty { impl fmt::UpperHex for $ty {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result { fmt::UpperHex::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
fmt::UpperHex::fmt(&self.0, f)
}
} }
}; };
} }

View File

@ -24,13 +24,12 @@ use secp256k1::{Keypair, Message, Secp256k1, Signing, Verification};
use crate::bip32::{self, KeySource, Xpriv, Xpub}; use crate::bip32::{self, KeySource, Xpriv, Xpub};
use crate::blockdata::transaction::{self, Transaction, TxOut}; use crate::blockdata::transaction::{self, Transaction, TxOut};
use crate::crypto::{ecdsa, taproot};
use crate::crypto::key::{PrivateKey, PublicKey}; use crate::crypto::key::{PrivateKey, PublicKey};
use crate::crypto::{ecdsa, taproot};
use crate::key::TapTweak;
use crate::prelude::*; use crate::prelude::*;
use crate::sighash::{self, EcdsaSighashType, Prevouts, SighashCache}; use crate::sighash::{self, EcdsaSighashType, Prevouts, SighashCache};
use crate::{Amount, FeeRate, TapSighashType}; use crate::{Amount, FeeRate, TapLeafHash, TapSighashType};
use crate::key::TapTweak;
use crate::TapLeafHash;
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
@ -316,7 +315,7 @@ impl Psbt {
for i in 0..self.inputs.len() { for i in 0..self.inputs.len() {
match self.signing_algorithm(i) { match self.signing_algorithm(i) {
Ok(SigningAlgorithm::Ecdsa) => { Ok(SigningAlgorithm::Ecdsa) =>
match self.bip32_sign_ecdsa(k, i, &mut cache, secp) { match self.bip32_sign_ecdsa(k, i, &mut cache, secp) {
Ok(v) => { Ok(v) => {
used.insert(i, v); used.insert(i, v);
@ -324,8 +323,7 @@ impl Psbt {
Err(e) => { Err(e) => {
errors.insert(i, e); errors.insert(i, e);
} }
} },
}
Ok(SigningAlgorithm::Schnorr) => { Ok(SigningAlgorithm::Schnorr) => {
match self.bip32_sign_schnorr(k, i, &mut cache, secp) { match self.bip32_sign_schnorr(k, i, &mut cache, secp) {
Ok(v) => { Ok(v) => {
@ -422,13 +420,14 @@ impl Psbt {
T: Borrow<Transaction>, T: Borrow<Transaction>,
K: GetKey, K: GetKey,
{ {
let mut input = self.inputs[input_index].clone(); let mut input = self.inputs[input_index].clone();
let mut used = vec![]; // List of pubkeys used to sign the input. let mut used = vec![]; // List of pubkeys used to sign the input.
for (&xonly, (leaf_hashes, key_source)) in input.tap_key_origins.iter() { for (&xonly, (leaf_hashes, key_source)) in input.tap_key_origins.iter() {
let sk = if let Ok(Some(secret_key)) = k.get_key(KeyRequest::Bip32(key_source.clone()), secp) { let sk = if let Ok(Some(secret_key)) =
k.get_key(KeyRequest::Bip32(key_source.clone()), secp)
{
secret_key secret_key
} else { } else {
continue; continue;
@ -441,7 +440,6 @@ impl Psbt {
// key path spend // key path spend
if let Some(internal_key) = input.tap_internal_key { if let Some(internal_key) = input.tap_internal_key {
// BIP 371: The internal key does not have leaf hashes, so can be indicated with a hashes len of 0. // BIP 371: The internal key does not have leaf hashes, so can be indicated with a hashes len of 0.
// Based on input.tap_internal_key.is_some() alone, it is not sufficient to determine whether it is a key path spend. // Based on input.tap_internal_key.is_some() alone, it is not sufficient to determine whether it is a key path spend.
@ -473,11 +471,11 @@ impl Psbt {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !leaf_hashes.is_empty() { if !leaf_hashes.is_empty() {
let key_pair = Keypair::from_secret_key(secp, &sk.inner); let key_pair = Keypair::from_secret_key(secp, &sk.inner);
for lh in leaf_hashes { for lh in leaf_hashes {
let (msg, sighash_type) = self.sighash_taproot(input_index, cache, Some(lh))?; let (msg, sighash_type) =
self.sighash_taproot(input_index, cache, Some(lh))?;
#[cfg(feature = "rand-std")] #[cfg(feature = "rand-std")]
let signature = secp.sign_schnorr(&msg, &key_pair); let signature = secp.sign_schnorr(&msg, &key_pair);
@ -548,8 +546,9 @@ impl Psbt {
Wsh | ShWsh => { Wsh | ShWsh => {
let witness_script = let witness_script =
input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?; input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?;
let sighash = let sighash = cache
cache.p2wsh_signature_hash(input_index, witness_script, utxo.value, hash_ty).map_err(SignError::SegwitV0Sighash)?; .p2wsh_signature_hash(input_index, witness_script, utxo.value, hash_ty)
.map_err(SignError::SegwitV0Sighash)?;
Ok((Message::from_digest(sighash.to_byte_array()), hash_ty)) Ok((Message::from_digest(sighash.to_byte_array()), hash_ty))
} }
Tr => { Tr => {
@ -567,7 +566,7 @@ impl Psbt {
&self, &self,
input_index: usize, input_index: usize,
cache: &mut SighashCache<T>, cache: &mut SighashCache<T>,
leaf_hash: Option<TapLeafHash> leaf_hash: Option<TapLeafHash>,
) -> Result<(Message, TapSighashType), SignError> { ) -> Result<(Message, TapSighashType), SignError> {
use OutputType::*; use OutputType::*;
@ -585,9 +584,8 @@ impl Psbt {
.taproot_hash_ty() .taproot_hash_ty()
.map_err(|_| SignError::InvalidSighashType)?; .map_err(|_| SignError::InvalidSighashType)?;
let spend_utxos = (0..self.inputs.len()) let spend_utxos =
.map(|i| self.spend_utxo(i).ok()) (0..self.inputs.len()).map(|i| self.spend_utxo(i).ok()).collect::<Vec<_>>();
.collect::<Vec<_>>();
let all_spend_utxos; let all_spend_utxos;
let is_anyone_can_pay = PsbtSighashType::from(hash_ty).to_u32() & 0x80 != 0; let is_anyone_can_pay = PsbtSighashType::from(hash_ty).to_u32() & 0x80 != 0;
@ -605,15 +603,18 @@ impl Psbt {
}; };
let sighash = if let Some(leaf_hash) = leaf_hash { let sighash = if let Some(leaf_hash) = leaf_hash {
cache.taproot_script_spend_signature_hash(input_index, &prev_outs, leaf_hash, hash_ty)? cache.taproot_script_spend_signature_hash(
input_index,
&prev_outs,
leaf_hash,
hash_ty,
)?
} else { } else {
cache.taproot_key_spend_signature_hash(input_index, &prev_outs, hash_ty)? cache.taproot_key_spend_signature_hash(input_index, &prev_outs, hash_ty)?
}; };
Ok((Message::from(sighash), hash_ty)) Ok((Message::from(sighash), hash_ty))
} }
_ => { _ => Err(SignError::Unsupported),
Err(SignError::Unsupported)
}
} }
} }

View File

@ -1443,8 +1443,8 @@ impl std::error::Error for TaprootError {
mod test { mod test {
use core::str::FromStr; use core::str::FromStr;
use hashes::sha256t::Tag;
use hashes::sha256; use hashes::sha256;
use hashes::sha256t::Tag;
use hex::FromHex; use hex::FromHex;
use secp256k1::VerifyOnly; use secp256k1::VerifyOnly;

View File

@ -2,20 +2,22 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use secp256k1::{Keypair, Signing, Secp256k1, XOnlyPublicKey};
use bitcoin::{absolute, Address, Network, OutPoint, PrivateKey, Psbt, script, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Witness};
use bitcoin::bip32::{DerivationPath, Fingerprint}; use bitcoin::bip32::{DerivationPath, Fingerprint};
use bitcoin::consensus::encode::serialize_hex; use bitcoin::consensus::encode::serialize_hex;
use bitcoin::opcodes::all::OP_CHECKSIG; use bitcoin::opcodes::all::OP_CHECKSIG;
use bitcoin::psbt::{GetKey, Input, KeyRequest, PsbtSighashType, SignError}; use bitcoin::psbt::{GetKey, Input, KeyRequest, PsbtSighashType, SignError};
use bitcoin::taproot::{LeafVersion, TaprootBuilder, TaprootSpendInfo}; use bitcoin::taproot::{LeafVersion, TaprootBuilder, TaprootSpendInfo};
use bitcoin::transaction::Version; use bitcoin::transaction::Version;
use bitcoin::{
absolute, script, Address, Network, OutPoint, PrivateKey, Psbt, ScriptBuf, Sequence,
Transaction, TxIn, TxOut, Witness,
};
use secp256k1::{Keypair, Secp256k1, Signing, XOnlyPublicKey};
use units::Amount; use units::Amount;
#[test] #[test]
fn psbt_sign_taproot() { fn psbt_sign_taproot() {
struct Keystore { struct Keystore {
sk: PrivateKey, sk: PrivateKey,
mfp: Fingerprint, mfp: Fingerprint,
@ -23,16 +25,19 @@ fn psbt_sign_taproot() {
impl GetKey for Keystore { impl GetKey for Keystore {
type Error = SignError; type Error = SignError;
fn get_key<C: Signing>(&self, key_request: KeyRequest, _secp: &Secp256k1<C>) -> Result<Option<PrivateKey>, Self::Error> { fn get_key<C: Signing>(
&self,
key_request: KeyRequest,
_secp: &Secp256k1<C>,
) -> Result<Option<PrivateKey>, Self::Error> {
match key_request { match key_request {
KeyRequest::Bip32((mfp, _)) => { KeyRequest::Bip32((mfp, _)) =>
if mfp == self.mfp { if mfp == self.mfp {
Ok(Some(self.sk)) Ok(Some(self.sk))
} else { } else {
Err(SignError::KeyNotFound) Err(SignError::KeyNotFound)
} },
} _ => Err(SignError::KeyNotFound),
_ => Err(SignError::KeyNotFound)
} }
} }
} }
@ -60,10 +65,14 @@ fn psbt_sign_taproot() {
let internal_key = kp.x_only_public_key().0; // Ignore the parity. let internal_key = kp.x_only_public_key().0; // Ignore the parity.
let tree = create_taproot_tree(secp, script1.clone(), script2.clone(), script3.clone(), internal_key); let tree =
create_taproot_tree(secp, script1.clone(), script2.clone(), script3.clone(), internal_key);
let address = create_p2tr_address(tree.clone()); let address = create_p2tr_address(tree.clone());
assert_eq!("tb1pytee2mxz0f4fkrsqqws2lsgnkp8nrw2atjkjy2n9gahggsphr0gszaxxmv", address.to_string()); assert_eq!(
"tb1pytee2mxz0f4fkrsqqws2lsgnkp8nrw2atjkjy2n9gahggsphr0gszaxxmv",
address.to_string()
);
// m/86'/1'/0'/0/7 // m/86'/1'/0'/0/7
let to_address = "tb1pyfv094rr0vk28lf8v9yx3veaacdzg26ztqk4ga84zucqqhafnn5q9my9rz"; let to_address = "tb1pyfv094rr0vk28lf8v9yx3veaacdzg26ztqk4ga84zucqqhafnn5q9my9rz";
@ -74,7 +83,11 @@ fn psbt_sign_taproot() {
// //
// Step 1: create psbt for key path spend. // Step 1: create psbt for key path spend.
// //
let mut psbt_key_path_spend = create_psbt_for_taproot_key_path_spend(address.clone(), to_address.clone(), tree.clone()); let mut psbt_key_path_spend = create_psbt_for_taproot_key_path_spend(
address.clone(),
to_address.clone(),
tree.clone(),
);
// //
// Step 2: sign psbt. // Step 2: sign psbt.
@ -117,7 +130,14 @@ fn psbt_sign_taproot() {
// //
// Step 1: create psbt for script path spend. // Step 1: create psbt for script path spend.
// //
let mut psbt_script_path_spend = create_psbt_for_taproot_script_path_spend(address.clone(), to_address.clone(), tree.clone(), x_only_pubkey, signing_key_path, script2.clone()); let mut psbt_script_path_spend = create_psbt_for_taproot_script_path_spend(
address.clone(),
to_address.clone(),
tree.clone(),
x_only_pubkey,
signing_key_path,
script2.clone(),
);
// //
// Step 2: sign psbt. // Step 2: sign psbt.
@ -125,7 +145,15 @@ fn psbt_sign_taproot() {
let _ = psbt_script_path_spend.sign(&keystore, secp); let _ = psbt_script_path_spend.sign(&keystore, secp);
let sig = "9c1466e1631a58c55fcb8642ce5f7896314f4b565d92c5c80b17aa9abf56d22e0b5e5dcbcfe836bbd7d409491f58aa9e1f68a491ef8f05eef62fb50ffac85727"; let sig = "9c1466e1631a58c55fcb8642ce5f7896314f4b565d92c5c80b17aa9abf56d22e0b5e5dcbcfe836bbd7d409491f58aa9e1f68a491ef8f05eef62fb50ffac85727";
assert_eq!(sig, psbt_script_path_spend.inputs[0].tap_script_sigs.get(&(x_only_pubkey, script2.clone().tapscript_leaf_hash())).unwrap().signature.to_string()); assert_eq!(
sig,
psbt_script_path_spend.inputs[0]
.tap_script_sigs
.get(&(x_only_pubkey, script2.clone().tapscript_leaf_hash()))
.unwrap()
.signature
.to_string()
);
// //
// Step 3: finalize psbt. // Step 3: finalize psbt.
@ -142,7 +170,7 @@ fn psbt_sign_taproot() {
} }
} }
fn create_basic_single_sig_script(secp: &Secp256k1::<secp256k1::All>, sk: &str) -> ScriptBuf { fn create_basic_single_sig_script(secp: &Secp256k1<secp256k1::All>, sk: &str) -> ScriptBuf {
let kp = Keypair::from_seckey_str(secp, sk).expect("failed to create keypair"); let kp = Keypair::from_seckey_str(secp, sk).expect("failed to create keypair");
let x_only_pubkey = kp.x_only_public_key().0; let x_only_pubkey = kp.x_only_public_key().0;
script::Builder::new() script::Builder::new()
@ -151,7 +179,13 @@ fn create_basic_single_sig_script(secp: &Secp256k1::<secp256k1::All>, sk: &str)
.into_script() .into_script()
} }
fn create_taproot_tree(secp: &Secp256k1::<secp256k1::All>, script1: ScriptBuf, script2: ScriptBuf, script3: ScriptBuf, internal_key: XOnlyPublicKey) -> TaprootSpendInfo { fn create_taproot_tree(
secp: &Secp256k1<secp256k1::All>,
script1: ScriptBuf,
script2: ScriptBuf,
script3: ScriptBuf,
internal_key: XOnlyPublicKey,
) -> TaprootSpendInfo {
let builder = TaprootBuilder::new(); let builder = TaprootBuilder::new();
let builder = builder.add_leaf(2, script1).unwrap(); let builder = builder.add_leaf(2, script1).unwrap();
let builder = builder.add_leaf(2, script2).unwrap(); let builder = builder.add_leaf(2, script2).unwrap();
@ -164,12 +198,16 @@ fn create_p2tr_address(tree: TaprootSpendInfo) -> Address {
Address::p2tr_tweaked(output_key, Network::Testnet) Address::p2tr_tweaked(output_key, Network::Testnet)
} }
fn create_psbt_for_taproot_key_path_spend(from_address: Address, to_address: Address, tree: TaprootSpendInfo) -> Psbt { fn create_psbt_for_taproot_key_path_spend(
from_address: Address,
to_address: Address,
tree: TaprootSpendInfo,
) -> Psbt {
let send_value = 6400; let send_value = 6400;
let out_puts = vec![ let out_puts = vec![TxOut {
TxOut { value: Amount::from_sat(send_value), script_pubkey: to_address.script_pubkey() }, value: Amount::from_sat(send_value),
]; script_pubkey: to_address.script_pubkey(),
}];
let prev_tx_id = "06980ca116f74c7845a897461dd0e1d15b114130176de5004957da516b4dee3a"; let prev_tx_id = "06980ca116f74c7845a897461dd0e1d15b114130176de5004957da516b4dee3a";
let transaction = Transaction { let transaction = Transaction {
@ -186,7 +224,6 @@ fn create_psbt_for_taproot_key_path_spend(from_address: Address, to_address: Add
let mut psbt = Psbt::from_unsigned_tx(transaction).unwrap(); let mut psbt = Psbt::from_unsigned_tx(transaction).unwrap();
let mfp = "73c5da0a"; let mfp = "73c5da0a";
let internal_key_path = "86'/1'/0'/0/2"; let internal_key_path = "86'/1'/0'/0/2";
@ -233,14 +270,22 @@ fn finalize_psbt_for_key_path_spend(mut psbt: Psbt) -> Psbt {
psbt psbt
} }
fn create_psbt_for_taproot_script_path_spend(from_address: Address, to_address: Address, tree: TaprootSpendInfo, x_only_pubkey_of_signing_key: XOnlyPublicKey, signing_key_path: &str, use_script: ScriptBuf) -> Psbt { fn create_psbt_for_taproot_script_path_spend(
from_address: Address,
to_address: Address,
tree: TaprootSpendInfo,
x_only_pubkey_of_signing_key: XOnlyPublicKey,
signing_key_path: &str,
use_script: ScriptBuf,
) -> Psbt {
let utxo_value = 6280; let utxo_value = 6280;
let send_value = 6000; let send_value = 6000;
let mfp = "73c5da0a"; let mfp = "73c5da0a";
let out_puts = vec![ let out_puts = vec![TxOut {
TxOut { value: Amount::from_sat(send_value), script_pubkey: to_address.script_pubkey() }, value: Amount::from_sat(send_value),
]; script_pubkey: to_address.script_pubkey(),
}];
let prev_tx_id = "9d7c6770fca57285babab60c51834cfcfd10ad302119cae842d7216b4ac9a376"; let prev_tx_id = "9d7c6770fca57285babab60c51834cfcfd10ad302119cae842d7216b4ac9a376";
let transaction = Transaction { let transaction = Transaction {
version: Version(2), version: Version(2),
@ -291,7 +336,6 @@ fn create_psbt_for_taproot_script_path_spend(from_address: Address, to_address:
psbt psbt
} }
fn finalize_psbt_for_script_path_spend(mut psbt: Psbt) -> Psbt { fn finalize_psbt_for_script_path_spend(mut psbt: Psbt) -> Psbt {
psbt.inputs.iter_mut().for_each(|input| { psbt.inputs.iter_mut().for_each(|input| {
let mut script_witness: Witness = Witness::new(); let mut script_witness: Witness = Witness::new();

View File

@ -66,17 +66,13 @@
//! ``` //! ```
#![cfg_attr(all(not(test), not(feature = "std")), no_std)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)]
// Experimental features we need. // Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(bench, feature(test))] #![cfg_attr(bench, feature(test))]
// Coding conventions. // Coding conventions.
#![warn(missing_docs)] #![warn(missing_docs)]
// Instead of littering the codebase for non-fuzzing code just globally allow. // Instead of littering the codebase for non-fuzzing code just globally allow.
#![cfg_attr(hashes_fuzz, allow(dead_code, unused_imports))] #![cfg_attr(hashes_fuzz, allow(dead_code, unused_imports))]
// Exclude lints we don't think are valuable. // Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![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. #![allow(clippy::manual_range_contains)] // More readable than clippy's format.

View File

@ -815,8 +815,8 @@ impl HashEngine {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{sha256, Hash as _, HashEngine};
use super::*; use super::*;
use crate::{sha256, Hash as _, HashEngine};
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]

View File

@ -7,13 +7,10 @@
//! //!
#![no_std] #![no_std]
// Experimental features we need. // Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
// Coding conventions. // Coding conventions.
#![warn(missing_docs)] #![warn(missing_docs)]
// Exclude lints we don't think are valuable. // Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![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. #![allow(clippy::manual_range_contains)] // More readable than clippy's format.

View File

@ -99,7 +99,10 @@ macro_rules! impl_array_newtype {
macro_rules! debug_from_display { macro_rules! debug_from_display {
($thing:ident) => { ($thing:ident) => {
impl core::fmt::Debug for $thing { impl core::fmt::Debug for $thing {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> { fn fmt(
&self,
f: &mut core::fmt::Formatter,
) -> core::result::Result<(), core::fmt::Error> {
core::fmt::Display::fmt(self, f) core::fmt::Display::fmt(self, f)
} }
} }

View File

@ -9,13 +9,10 @@
//! `std::io`'s traits without unnecessary complexity. //! `std::io`'s traits without unnecessary complexity.
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
// Experimental features we need. // Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
// Coding conventions. // Coding conventions.
#![warn(missing_docs)] #![warn(missing_docs)]
// Exclude lints we don't think are valuable. // Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![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. #![allow(clippy::manual_range_contains)] // More readable than clippy's format.
@ -325,11 +322,11 @@ pub fn sink() -> Sink { Sink }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
#[cfg(all(not(feature = "std"), feature = "alloc"))] #[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{string::ToString, vec}; use alloc::{string::ToString, vec};
use super::*;
#[test] #[test]
fn buf_read_fill_and_consume_slice() { fn buf_read_fill_and_consume_slice() {
let data = [0_u8, 1, 2]; let data = [0_u8, 1, 2];

View File

@ -5,21 +5,19 @@
//! This module mainly introduces the [Amount] and [SignedAmount] types. //! This module mainly introduces the [Amount] and [SignedAmount] types.
//! We refer to the documentation on the types for more information. //! We refer to the documentation on the types for more information.
#[cfg(feature = "alloc")]
use alloc::string::{String, ToString};
use core::cmp::Ordering; use core::cmp::Ordering;
use core::fmt;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use core::fmt::Write as _; use core::fmt::Write as _;
use core::str::FromStr; use core::str::FromStr;
use core::{default, ops}; use core::{default, fmt, ops};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
use internals::error::InputString; use internals::error::InputString;
use internals::write_err; use internals::write_err;
#[cfg(feature = "alloc")]
use alloc::string::{String, ToString};
/// A set of denominations in which amounts can be expressed. /// A set of denominations in which amounts can be expressed.
/// ///
/// # Examples /// # Examples
@ -198,7 +196,8 @@ impl fmt::Display for ParseError {
ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error), ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error),
// We consider this to not be a source because it currently doesn't contain useful // We consider this to not be a source because it currently doesn't contain useful
// information // information
ParseError::MissingDenomination(_) => f.write_str("the input doesn't contain a denomination"), ParseError::MissingDenomination(_) =>
f.write_str("the input doesn't contain a denomination"),
} }
} }
} }
@ -233,28 +232,19 @@ pub enum ParseAmountError {
} }
impl From<TooPreciseError> for ParseAmountError { impl From<TooPreciseError> for ParseAmountError {
fn from(value: TooPreciseError) -> Self { fn from(value: TooPreciseError) -> Self { Self::TooPrecise(value) }
Self::TooPrecise(value)
}
} }
impl From<MissingDigitsError> for ParseAmountError { impl From<MissingDigitsError> for ParseAmountError {
fn from(value: MissingDigitsError) -> Self { fn from(value: MissingDigitsError) -> Self { Self::MissingDigits(value) }
Self::MissingDigits(value)
}
} }
impl From<InputTooLargeError> for ParseAmountError { impl From<InputTooLargeError> for ParseAmountError {
fn from(value: InputTooLargeError) -> Self { fn from(value: InputTooLargeError) -> Self { Self::InputTooLarge(value) }
Self::InputTooLarge(value)
} }
}
impl From<InvalidCharacterError> for ParseAmountError { impl From<InvalidCharacterError> for ParseAmountError {
fn from(value: InvalidCharacterError) -> Self { fn from(value: InvalidCharacterError) -> Self { Self::InvalidCharacter(value) }
Self::InvalidCharacter(value)
}
} }
internals::impl_from_infallible!(ParseAmountError); internals::impl_from_infallible!(ParseAmountError);
@ -307,21 +297,12 @@ impl OutOfRangeError {
} }
/// Returns true if the input value was large than the maximum allowed value. /// Returns true if the input value was large than the maximum allowed value.
pub fn is_above_max(&self) -> bool { pub fn is_above_max(&self) -> bool { self.is_greater_than_max }
self.is_greater_than_max
}
/// Returns true if the input value was smaller than the minimum allowed value. /// Returns true if the input value was smaller than the minimum allowed value.
pub fn is_below_min(&self) -> bool { pub fn is_below_min(&self) -> bool { !self.is_greater_than_max }
!self.is_greater_than_max
}
pub(crate) fn too_big(is_signed: bool) -> Self { pub(crate) fn too_big(is_signed: bool) -> Self { Self { is_signed, is_greater_than_max: true } }
Self {
is_signed,
is_greater_than_max: true,
}
}
pub(crate) fn too_small() -> Self { pub(crate) fn too_small() -> Self {
Self { Self {
@ -354,9 +335,7 @@ impl fmt::Display for OutOfRangeError {
impl std::error::Error for OutOfRangeError {} impl std::error::Error for OutOfRangeError {}
impl From<OutOfRangeError> for ParseAmountError { impl From<OutOfRangeError> for ParseAmountError {
fn from(value: OutOfRangeError) -> Self { fn from(value: OutOfRangeError) -> Self { ParseAmountError::OutOfRange(value) }
ParseAmountError::OutOfRange(value)
}
} }
/// Error returned when the input string has higher precision than satoshis. /// Error returned when the input string has higher precision than satoshis.
@ -369,7 +348,11 @@ impl fmt::Display for TooPreciseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.position { match self.position {
0 => f.write_str("the amount is less than 1 satoshi but it's not zero"), 0 => f.write_str("the amount is less than 1 satoshi but it's not zero"),
pos => write!(f, "the digits starting from position {} represent a sub-satoshi amount", pos), pos => write!(
f,
"the digits starting from position {} represent a sub-satoshi amount",
pos
),
} }
} }
} }
@ -386,8 +369,16 @@ pub struct InputTooLargeError {
impl fmt::Display for InputTooLargeError { impl fmt::Display for InputTooLargeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.len - INPUT_STRING_LEN_LIMIT { match self.len - INPUT_STRING_LEN_LIMIT {
1 => write!(f, "the input is one character longer than the maximum allowed length ({})", INPUT_STRING_LEN_LIMIT), 1 => write!(
n => write!(f, "the input is {} characters longer than the maximum allowed length ({})", n, INPUT_STRING_LEN_LIMIT), f,
"the input is one character longer than the maximum allowed length ({})",
INPUT_STRING_LEN_LIMIT
),
n => write!(
f,
"the input is {} characters longer than the maximum allowed length ({})",
n, INPUT_STRING_LEN_LIMIT
),
} }
} }
} }
@ -407,7 +398,8 @@ impl fmt::Display for MissingDigitsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind { match self.kind {
MissingDigitsKind::Empty => f.write_str("the input is empty"), MissingDigitsKind::Empty => f.write_str("the input is empty"),
MissingDigitsKind::OnlyMinusSign => f.write_str("there are no digits following the minus (-) sign"), MissingDigitsKind::OnlyMinusSign =>
f.write_str("there are no digits following the minus (-) sign"),
} }
} }
} }
@ -433,7 +425,11 @@ impl fmt::Display for InvalidCharacterError {
match self.invalid_char { match self.invalid_char {
'.' => f.write_str("there is more than one decimal separator (dot) in the input"), '.' => f.write_str("there is more than one decimal separator (dot) in the input"),
'-' => f.write_str("there is more than one minus sign (-) in the input"), '-' => f.write_str("there is more than one minus sign (-) in the input"),
c => write!(f, "the character '{}' at position {} is not a valid digit", c, self.position), c => write!(
f,
"the character '{}' at position {} is not a valid digit",
c, self.position
),
} }
} }
} }
@ -517,17 +513,18 @@ impl std::error::Error for PossiblyConfusingDenominationError {
/// The position indicates the first digit that is too precise. /// The position indicates the first digit that is too precise.
fn is_too_precise(s: &str, precision: usize) -> Option<usize> { fn is_too_precise(s: &str, precision: usize) -> Option<usize> {
match s.find('.') { match s.find('.') {
Some(pos) if precision >= pos => { Some(0) }, Some(pos) if precision >= pos => Some(0),
Some(pos) => { Some(pos) => s[..pos]
s[..pos].char_indices().rev().take(precision).find(|(_, d)| *d != '0').map(|(i, _)| i) .char_indices()
.rev()
.take(precision)
.find(|(_, d)| *d != '0')
.map(|(i, _)| i)
.or_else(|| { .or_else(|| {
s[(pos + 1)..].char_indices().find(|(_, d)| *d != '0').map(|(i, _)| i + pos + 1) s[(pos + 1)..].char_indices().find(|(_, d)| *d != '0').map(|(i, _)| i + pos + 1)
}) }),
}, None if precision >= s.len() => Some(0),
None if precision >= s.len() => { Some(0) }, None => s.char_indices().rev().take(precision).find(|(_, d)| *d != '0').map(|(i, _)| i),
None => {
s.char_indices().rev().take(precision).find(|(_, d)| *d != '0').map(|(i, _)| i)
},
} }
} }
@ -540,7 +537,9 @@ fn parse_signed_to_satoshi(
denom: Denomination, denom: Denomination,
) -> Result<(bool, u64), InnerParseError> { ) -> Result<(bool, u64), InnerParseError> {
if s.is_empty() { if s.is_empty() {
return Err(InnerParseError::MissingDigits(MissingDigitsError { kind: MissingDigitsKind::Empty })); return Err(InnerParseError::MissingDigits(MissingDigitsError {
kind: MissingDigitsKind::Empty,
}));
} }
if s.len() > INPUT_STRING_LEN_LIMIT { if s.len() > INPUT_STRING_LEN_LIMIT {
return Err(InnerParseError::InputTooLarge(s.len())); return Err(InnerParseError::InputTooLarge(s.len()));
@ -549,7 +548,9 @@ fn parse_signed_to_satoshi(
let is_negative = s.starts_with('-'); let is_negative = s.starts_with('-');
if is_negative { if is_negative {
if s.len() == 1 { if s.len() == 1 {
return Err(InnerParseError::MissingDigits(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign })); return Err(InnerParseError::MissingDigits(MissingDigitsError {
kind: MissingDigitsKind::OnlyMinusSign,
}));
} }
s = &s[1..]; s = &s[1..];
} }
@ -567,7 +568,10 @@ fn parse_signed_to_satoshi(
if let Some(position) = is_too_precise(s, last_n) { if let Some(position) = is_too_precise(s, last_n) {
match s.parse::<i64>() { match s.parse::<i64>() {
Ok(0) => return Ok((is_negative, 0)), Ok(0) => return Ok((is_negative, 0)),
_ => return Err(InnerParseError::TooPrecise(TooPreciseError { position: position + is_negative as usize })), _ =>
return Err(InnerParseError::TooPrecise(TooPreciseError {
position: position + is_negative as usize,
})),
} }
} }
s = &s[0..s.find('.').unwrap_or(s.len()) - last_n]; s = &s[0..s.find('.').unwrap_or(s.len()) - last_n];
@ -594,16 +598,27 @@ fn parse_signed_to_satoshi(
decimals = match decimals { decimals = match decimals {
None => None, None => None,
Some(d) if d < max_decimals => Some(d + 1), Some(d) if d < max_decimals => Some(d + 1),
_ => return Err(InnerParseError::TooPrecise(TooPreciseError { position: i + is_negative as usize, })), _ =>
return Err(InnerParseError::TooPrecise(TooPreciseError {
position: i + is_negative as usize,
})),
}; };
} }
'.' => match decimals { '.' => match decimals {
None if max_decimals <= 0 => break, None if max_decimals <= 0 => break,
None => decimals = Some(0), None => decimals = Some(0),
// Double decimal dot. // Double decimal dot.
_ => return Err(InnerParseError::InvalidCharacter(InvalidCharacterError { invalid_char: '.', position: i + is_negative as usize })), _ =>
return Err(InnerParseError::InvalidCharacter(InvalidCharacterError {
invalid_char: '.',
position: i + is_negative as usize,
})),
}, },
c => return Err(InnerParseError::InvalidCharacter(InvalidCharacterError { invalid_char: c, position: i + is_negative as usize })), c =>
return Err(InnerParseError::InvalidCharacter(InvalidCharacterError {
invalid_char: c,
position: i + is_negative as usize,
})),
} }
} }
@ -632,7 +647,8 @@ internals::impl_from_infallible!(InnerParseError);
impl InnerParseError { impl InnerParseError {
fn convert(self, is_signed: bool) -> ParseAmountError { fn convert(self, is_signed: bool) -> ParseAmountError {
match self { match self {
Self::Overflow { is_negative } => OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(), Self::Overflow { is_negative } =>
OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
Self::TooPrecise(error) => ParseAmountError::TooPrecise(error), Self::TooPrecise(error) => ParseAmountError::TooPrecise(error),
Self::MissingDigits(error) => ParseAmountError::MissingDigits(error), Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }), Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
@ -645,7 +661,9 @@ fn split_amount_and_denomination(s: &str) -> Result<(&str, Denomination), ParseE
let (i, j) = if let Some(i) = s.find(' ') { let (i, j) = if let Some(i) = s.find(' ') {
(i, i + 1) (i, i + 1)
} else { } else {
let i = s.find(|c: char| c.is_alphabetic()).ok_or(ParseError::MissingDenomination(MissingDenominationError))?; let i = s
.find(|c: char| c.is_alphabetic())
.ok_or(ParseError::MissingDenomination(MissingDenominationError))?;
(i, i) (i, i)
}; };
Ok((&s[..i], s[j..].parse()?)) Ok((&s[..i], s[j..].parse()?))
@ -891,8 +909,8 @@ impl Amount {
/// Note: This only parses the value string. If you want to parse a value /// Note: This only parses the value string. If you want to parse a value
/// with denomination, use [FromStr]. /// with denomination, use [FromStr].
pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> { pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
let (negative, satoshi) = parse_signed_to_satoshi(s, denom) let (negative, satoshi) =
.map_err(|error| error.convert(false))?; parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
if negative { if negative {
return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative())); return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative()));
} }
@ -1029,16 +1047,12 @@ impl Amount {
/// Unchecked addition. /// Unchecked addition.
/// ///
/// Computes `self + rhs`. Panics in debug mode, wraps in release mode. /// Computes `self + rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_add(self, rhs: Amount) -> Amount { pub fn unchecked_add(self, rhs: Amount) -> Amount { Self(self.0 + rhs.0) }
Self(self.0 + rhs.0)
}
/// Unchecked subtraction. /// Unchecked subtraction.
/// ///
/// Computes `self - rhs`. Panics in debug mode, wraps in release mode. /// Computes `self - rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_sub(self, rhs: Amount) -> Amount { pub fn unchecked_sub(self, rhs: Amount) -> Amount { Self(self.0 - rhs.0) }
Self(self.0 - rhs.0)
}
/// Convert to a signed amount. /// Convert to a signed amount.
pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> { pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> {
@ -1055,9 +1069,7 @@ impl default::Default for Amount {
} }
impl fmt::Debug for Amount { impl fmt::Debug for Amount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} SAT", self.to_sat()) }
write!(f, "{} SAT", self.to_sat())
}
} }
// No one should depend on a binding contract for Display for this type. // No one should depend on a binding contract for Display for this type.
@ -1143,9 +1155,7 @@ impl FromStr for Amount {
impl TryFrom<SignedAmount> for Amount { impl TryFrom<SignedAmount> for Amount {
type Error = OutOfRangeError; type Error = OutOfRangeError;
fn try_from(value: SignedAmount) -> Result<Self, Self::Error> { fn try_from(value: SignedAmount) -> Result<Self, Self::Error> { value.to_unsigned() }
value.to_unsigned()
}
} }
impl core::iter::Sum for Amount { impl core::iter::Sum for Amount {
@ -1264,10 +1274,12 @@ impl SignedAmount {
pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> { pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? { match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? {
// (negative, amount) // (negative, amount)
(false, sat) if sat > i64::MAX as u64 => Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))), (false, sat) if sat > i64::MAX as u64 =>
Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))),
(false, sat) => Ok(SignedAmount(sat as i64)), (false, sat) => Ok(SignedAmount(sat as i64)),
(true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)), (true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)),
(true, sat) if sat > i64::MIN.unsigned_abs() => Err(ParseAmountError::OutOfRange(OutOfRangeError::too_small())), (true, sat) if sat > i64::MIN.unsigned_abs() =>
Err(ParseAmountError::OutOfRange(OutOfRangeError::too_small())),
(true, sat) => Ok(SignedAmount(-(sat as i64))), (true, sat) => Ok(SignedAmount(-(sat as i64))),
} }
} }
@ -1423,16 +1435,12 @@ impl SignedAmount {
/// Unchecked addition. /// Unchecked addition.
/// ///
/// Computes `self + rhs`. Panics in debug mode, wraps in release mode. /// Computes `self + rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount { pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount { Self(self.0 + rhs.0) }
Self(self.0 + rhs.0)
}
/// Unchecked subtraction. /// Unchecked subtraction.
/// ///
/// Computes `self - rhs`. Panics in debug mode, wraps in release mode. /// Computes `self - rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount { pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount { Self(self.0 - rhs.0) }
Self(self.0 - rhs.0)
}
/// Subtraction that doesn't allow negative [SignedAmount]s. /// Subtraction that doesn't allow negative [SignedAmount]s.
/// Returns [None] if either [self], `rhs` or the result is strictly negative. /// Returns [None] if either [self], `rhs` or the result is strictly negative.
@ -1548,9 +1556,7 @@ impl FromStr for SignedAmount {
impl TryFrom<Amount> for SignedAmount { impl TryFrom<Amount> for SignedAmount {
type Error = OutOfRangeError; type Error = OutOfRangeError;
fn try_from(value: Amount) -> Result<Self, Self::Error> { fn try_from(value: Amount) -> Result<Self, Self::Error> { value.to_signed() }
value.to_signed()
}
} }
impl core::iter::Sum for SignedAmount { impl core::iter::Sum for SignedAmount {
@ -1621,11 +1627,12 @@ pub mod serde {
//! ``` //! ```
use core::fmt; use core::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{Amount, SignedAmount, ParseAmountError};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use super::Denomination; use super::Denomination;
use super::{Amount, ParseAmountError, SignedAmount};
/// This trait is used only to avoid code duplication and naming collisions /// This trait is used only to avoid code duplication and naming collisions
/// of the different serde serialization crates. /// of the different serde serialization crates.
@ -1670,9 +1677,7 @@ pub mod serde {
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
impl fmt::Display for DisplayFullError { impl fmt::Display for DisplayFullError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
fmt::Display::fmt(&self.0, f)
}
} }
impl SerdeAmount for Amount { impl SerdeAmount for Amount {
@ -1741,10 +1746,9 @@ pub mod serde {
//! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi. //! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi.
//! Use with `#[serde(with = "amount::serde::as_sat")]`. //! Use with `#[serde(with = "amount::serde::as_sat")]`.
//! //!
use super::private;
use serde::{Deserializer, Serializer}; use serde::{Deserializer, Serializer};
use super::private;
use crate::amount::serde::SerdeAmount; use crate::amount::serde::SerdeAmount;
pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> { pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
@ -1759,12 +1763,12 @@ pub mod serde {
//! Serialize and deserialize [`Option<Amount>`](crate::Amount) as real numbers denominated in satoshi. //! Serialize and deserialize [`Option<Amount>`](crate::Amount) as real numbers denominated in satoshi.
//! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`. //! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`.
use super::private;
use core::fmt; use core::fmt;
use core::marker::PhantomData; use core::marker::PhantomData;
use serde::{de, Deserializer, Serializer}; use serde::{de, Deserializer, Serializer};
use super::private;
use crate::amount::serde::SerdeAmountForOpt; use crate::amount::serde::SerdeAmountForOpt;
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>( pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
@ -1812,10 +1816,9 @@ pub mod serde {
//! Serialize and deserialize [`Amount`](crate::Amount) as JSON numbers denominated in BTC. //! Serialize and deserialize [`Amount`](crate::Amount) as JSON numbers denominated in BTC.
//! Use with `#[serde(with = "amount::serde::as_btc")]`. //! Use with `#[serde(with = "amount::serde::as_btc")]`.
use super::private;
use serde::{Deserializer, Serializer}; use serde::{Deserializer, Serializer};
use super::private;
use crate::amount::serde::SerdeAmount; use crate::amount::serde::SerdeAmount;
pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> { pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
@ -1830,12 +1833,12 @@ pub mod serde {
//! Serialize and deserialize `Option<Amount>` as JSON numbers denominated in BTC. //! Serialize and deserialize `Option<Amount>` as JSON numbers denominated in BTC.
//! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`. //! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`.
use super::private;
use core::fmt; use core::fmt;
use core::marker::PhantomData; use core::marker::PhantomData;
use serde::{de, Deserializer, Serializer}; use serde::{de, Deserializer, Serializer};
use super::private;
use crate::amount::serde::SerdeAmountForOpt; use crate::amount::serde::SerdeAmountForOpt;
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>( pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
@ -1971,10 +1974,7 @@ mod verification {
if n1 >= 0 { if n1 >= 0 {
Ok(Amount::from_sat(n1.try_into().unwrap())) Ok(Amount::from_sat(n1.try_into().unwrap()))
} else { } else {
Err(OutOfRangeError { Err(OutOfRangeError { is_signed: true, is_greater_than_max: false })
is_signed: true,
is_greater_than_max: false
})
}, },
); );
} }
@ -2008,7 +2008,6 @@ mod verification {
mod tests { mod tests {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::format; use alloc::format;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::panic; use std::panic;
@ -2029,7 +2028,10 @@ mod tests {
let s = format!("-0 {}", denom); let s = format!("-0 {}", denom);
match Amount::from_str(&s) { match Amount::from_str(&s) {
Err(e) => assert_eq!(e, ParseError::Amount(ParseAmountError::OutOfRange(OutOfRangeError::negative()))), Err(e) => assert_eq!(
e,
ParseError::Amount(ParseAmountError::OutOfRange(OutOfRangeError::negative()))
),
Ok(_) => panic!("Unsigned amount from {}", s), Ok(_) => panic!("Unsigned amount from {}", s),
} }
match SignedAmount::from_str(&s) { match SignedAmount::from_str(&s) {
@ -2057,13 +2059,7 @@ mod tests {
let ua_max = Amount::MAX; let ua_max = Amount::MAX;
let result = SignedAmount::try_from(ua_max); let result = SignedAmount::try_from(ua_max);
assert_eq!( assert_eq!(result, Err(OutOfRangeError { is_signed: true, is_greater_than_max: true }));
result,
Err(OutOfRangeError {
is_signed: true,
is_greater_than_max: true
})
);
} }
#[test] #[test]
@ -2074,13 +2070,7 @@ mod tests {
let sa_negative = SignedAmount(-123); let sa_negative = SignedAmount(-123);
let result = Amount::try_from(sa_negative); let result = Amount::try_from(sa_negative);
assert_eq!( assert_eq!(result, Err(OutOfRangeError { is_signed: false, is_greater_than_max: false }));
result,
Err(OutOfRangeError {
is_signed: false,
is_greater_than_max: false
})
);
} }
#[test] #[test]
@ -2175,7 +2165,10 @@ mod tests {
assert_eq!(sf(-100.0, D::MilliSatoshi), Err(TooPreciseError { position: 1 }.into())); assert_eq!(sf(-100.0, D::MilliSatoshi), Err(TooPreciseError { position: 1 }.into()));
assert_eq!(f(42.123456781, D::Bitcoin), Err(TooPreciseError { position: 11 }.into())); assert_eq!(f(42.123456781, D::Bitcoin), Err(TooPreciseError { position: 11 }.into()));
assert_eq!(sf(-184467440738.0, D::Bitcoin), Err(OutOfRangeError::too_small().into())); assert_eq!(sf(-184467440738.0, D::Bitcoin), Err(OutOfRangeError::too_small().into()));
assert_eq!(f(18446744073709551617.0, D::Satoshi), Err(OutOfRangeError::too_big(false).into())); assert_eq!(
f(18446744073709551617.0, D::Satoshi),
Err(OutOfRangeError::too_big(false).into())
);
// Amount can be grater than the max SignedAmount. // Amount can be grater than the max SignedAmount.
assert!(f(SignedAmount::MAX.to_float_in(D::Satoshi) + 1.0, D::Satoshi).is_ok()); assert!(f(SignedAmount::MAX.to_float_in(D::Satoshi) + 1.0, D::Satoshi).is_ok());
@ -2210,12 +2203,30 @@ mod tests {
let p = Amount::from_str_in; let p = Amount::from_str_in;
let sp = SignedAmount::from_str_in; let sp = SignedAmount::from_str_in;
assert_eq!(p("x", btc), Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 0 }))); assert_eq!(
assert_eq!(p("-", btc), Err(E::from(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }))); p("x", btc),
assert_eq!(sp("-", btc), Err(E::from(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }))); Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 0 }))
assert_eq!(p("-1.0x", btc), Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 4 }))); );
assert_eq!(p("0.0 ", btc), Err(E::from(InvalidCharacterError { invalid_char: ' ', position: 3 }))); assert_eq!(
assert_eq!(p("0.000.000", btc), Err(E::from(InvalidCharacterError { invalid_char: '.', position: 5 }))); p("-", btc),
Err(E::from(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }))
);
assert_eq!(
sp("-", btc),
Err(E::from(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }))
);
assert_eq!(
p("-1.0x", btc),
Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 4 }))
);
assert_eq!(
p("0.0 ", btc),
Err(E::from(InvalidCharacterError { invalid_char: ' ', position: 3 }))
);
assert_eq!(
p("0.000.000", btc),
Err(E::from(InvalidCharacterError { invalid_char: '.', position: 5 }))
);
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
let more_than_max = format!("1{}", Amount::MAX); let more_than_max = format!("1{}", Amount::MAX);
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -2253,11 +2264,16 @@ mod tests {
{ {
let amount = Amount::from_sat(i64::MAX as u64); let amount = Amount::from_sat(i64::MAX as u64);
assert_eq!(Amount::from_str_in(&amount.to_string_in(sat), sat), Ok(amount)); assert_eq!(Amount::from_str_in(&amount.to_string_in(sat), sat), Ok(amount));
assert!(SignedAmount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat).is_err()); assert!(
SignedAmount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat).is_err()
);
assert!(Amount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat).is_ok()); assert!(Amount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat).is_ok());
} }
assert_eq!(p("12.000", Denomination::MilliSatoshi), Err(TooPreciseError { position: 0 }.into())); assert_eq!(
p("12.000", Denomination::MilliSatoshi),
Err(TooPreciseError { position: 0 }.into())
);
// exactly 50 chars. // exactly 50 chars.
assert_eq!( assert_eq!(
p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin), p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin),
@ -2500,7 +2516,10 @@ mod tests {
use super::ParseAmountError as E; use super::ParseAmountError as E;
assert_eq!(Amount::from_str("x BTC"), Err(InvalidCharacterError { invalid_char: 'x', position: 0 }.into())); assert_eq!(
Amount::from_str("x BTC"),
Err(InvalidCharacterError { invalid_char: 'x', position: 0 }.into())
);
assert_eq!( assert_eq!(
Amount::from_str("xBTC"), Amount::from_str("xBTC"),
Err(Unknown(UnknownDenominationError("xBTC".into())).into()), Err(Unknown(UnknownDenominationError("xBTC".into())).into()),
@ -2509,7 +2528,10 @@ mod tests {
Amount::from_str("5 BTC BTC"), Amount::from_str("5 BTC BTC"),
Err(Unknown(UnknownDenominationError("BTC BTC".into())).into()), Err(Unknown(UnknownDenominationError("BTC BTC".into())).into()),
); );
assert_eq!(Amount::from_str("5BTC BTC"), Err(E::from(InvalidCharacterError { invalid_char: 'B', position: 1 }).into())); assert_eq!(
Amount::from_str("5BTC BTC"),
Err(E::from(InvalidCharacterError { invalid_char: 'B', position: 1 }).into())
);
assert_eq!( assert_eq!(
Amount::from_str("5 5 BTC"), Amount::from_str("5 5 BTC"),
Err(Unknown(UnknownDenominationError("5 BTC".into())).into()), Err(Unknown(UnknownDenominationError("5 BTC".into())).into()),
@ -2732,7 +2754,10 @@ mod tests {
// errors // errors
let t: Result<T, serde_json::Error> = let t: Result<T, serde_json::Error> =
serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}"); serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}");
assert!(t.unwrap_err().to_string().contains(&ParseAmountError::TooPrecise(TooPreciseError { position: 16 }).to_string())); assert!(t
.unwrap_err()
.to_string()
.contains(&ParseAmountError::TooPrecise(TooPreciseError { position: 16 }).to_string()));
let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}"); let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}");
assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string())); assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string()));
} }
@ -2830,11 +2855,8 @@ mod tests {
let sum = amounts.into_iter().sum::<Amount>(); let sum = amounts.into_iter().sum::<Amount>();
assert_eq!(Amount::from_sat(1400), sum); assert_eq!(Amount::from_sat(1400), sum);
let amounts = [ let amounts =
SignedAmount::from_sat(-42), [SignedAmount::from_sat(-42), SignedAmount::from_sat(1337), SignedAmount::from_sat(21)];
SignedAmount::from_sat(1337),
SignedAmount::from_sat(21),
];
let sum = amounts.into_iter().sum::<SignedAmount>(); let sum = amounts.into_iter().sum::<SignedAmount>();
assert_eq!(SignedAmount::from_sat(1316), sum); assert_eq!(SignedAmount::from_sat(1316), sum);
} }
@ -2848,8 +2870,7 @@ mod tests {
let sum = amounts.into_iter().checked_sum(); let sum = amounts.into_iter().checked_sum();
assert_eq!(Some(Amount::from_sat(1400)), sum); assert_eq!(Some(Amount::from_sat(1400)), sum);
let amounts = let amounts = [Amount::from_sat(u64::MAX), Amount::from_sat(1337), Amount::from_sat(21)];
[Amount::from_sat(u64::MAX), Amount::from_sat(1337), Amount::from_sat(21)];
let sum = amounts.into_iter().checked_sum(); let sum = amounts.into_iter().checked_sum();
assert_eq!(None, sum); assert_eq!(None, sum);
@ -2869,11 +2890,8 @@ mod tests {
let sum = amounts.into_iter().checked_sum(); let sum = amounts.into_iter().checked_sum();
assert_eq!(None, sum); assert_eq!(None, sum);
let amounts = [ let amounts =
SignedAmount::from_sat(42), [SignedAmount::from_sat(42), SignedAmount::from_sat(3301), SignedAmount::from_sat(21)];
SignedAmount::from_sat(3301),
SignedAmount::from_sat(21),
];
let sum = amounts.into_iter().checked_sum(); let sum = amounts.into_iter().checked_sum();
assert_eq!(Some(SignedAmount::from_sat(3364)), sum); assert_eq!(Some(SignedAmount::from_sat(3364)), sum);
} }
@ -2932,17 +2950,8 @@ mod tests {
assert_eq!(format!("{:.2}", Amount::from_sat(100_000_000)), "1.00 BTC"); assert_eq!(format!("{:.2}", Amount::from_sat(100_000_000)), "1.00 BTC");
assert_eq!(format!("{}", Amount::from_sat(100_000_000)), "1 BTC"); assert_eq!(format!("{}", Amount::from_sat(100_000_000)), "1 BTC");
assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC"); assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC");
assert_eq!( assert_eq!(format!("{:.10}", Amount::from_sat(100_000_000)), "1.0000000000 BTC");
format!("{:.10}", Amount::from_sat(100_000_000)), assert_eq!(format!("{}", Amount::from_sat(400_000_000_000_010)), "4000000.00000010 BTC");
"1.0000000000 BTC" assert_eq!(format!("{}", Amount::from_sat(400_000_000_000_000)), "4000000 BTC");
);
assert_eq!(
format!("{}", Amount::from_sat(400_000_000_000_010)),
"4000000.00000010 BTC"
);
assert_eq!(
format!("{}", Amount::from_sat(400_000_000_000_000)),
"4000000 BTC"
);
} }
} }

View File

@ -6,14 +6,11 @@
// Experimental features we need. // Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
// Coding conventions. // Coding conventions.
#![warn(missing_docs)] #![warn(missing_docs)]
// Exclude lints we don't think are valuable. // Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![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. #![allow(clippy::manual_range_contains)] // More readable than clippy's format.
#![no_std] #![no_std]
// Disable 16-bit support at least for now as we can't guarantee it yet. // Disable 16-bit support at least for now as we can't guarantee it yet.
@ -39,18 +36,16 @@ mod test_macros;
pub mod amount; pub mod amount;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod locktime;
#[cfg(feature = "alloc")]
pub mod fee_rate; pub mod fee_rate;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod locktime;
#[cfg(feature = "alloc")]
pub mod parse; pub mod parse;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod weight; pub mod weight;
#[doc(inline)] #[doc(inline)]
pub use self::{ pub use self::amount::{Amount, ParseAmountError, SignedAmount};
amount::{Amount, ParseAmountError, SignedAmount},
};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub use self::parse::ParseIntError; pub use self::parse::ParseIntError;

View File

@ -2,9 +2,9 @@
//! Parsing utilities. //! Parsing utilities.
use alloc::string::String;
use core::fmt; use core::fmt;
use core::str::FromStr; use core::str::FromStr;
use alloc::string::String;
use internals::write_err; use internals::write_err;
@ -58,10 +58,7 @@ impl AsRef<core::num::ParseIntError> for ParseIntError {
/// Not strictly necessary but serves as a lint - avoids weird behavior if someone accidentally /// Not strictly necessary but serves as a lint - avoids weird behavior if someone accidentally
/// passes non-integer to the `parse()` function. /// passes non-integer to the `parse()` function.
pub trait Integer: pub trait Integer: FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized {}
FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized
{
}
macro_rules! impl_integer { macro_rules! impl_integer {
($($type:ty),* $(,)?) => { ($($type:ty),* $(,)?) => {