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: ACKa565db9fdd
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:
commit
c20cb4da53
|
@ -101,7 +101,11 @@ impl TooShortError {
|
|||
|
||||
impl fmt::Display for TooShortError {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -268,10 +268,7 @@ mod tests {
|
|||
Some(hex!("00f8917303bfa8ef24f292e8fa1419b20460ba064d"))
|
||||
);
|
||||
// Non Base58 char.
|
||||
assert_eq!(
|
||||
decode("¢").unwrap_err(),
|
||||
InvalidCharacterError { invalid: 194 }
|
||||
);
|
||||
assert_eq!(decode("¢").unwrap_err(), InvalidCharacterError { invalid: 194 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -133,7 +133,11 @@ impl ColdStorage {
|
|||
fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() }
|
||||
|
||||
/// 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) {
|
||||
Ok(keys) => assert_eq!(keys.len(), 1),
|
||||
Err((_, e)) => {
|
||||
|
|
|
@ -379,10 +379,7 @@ mod test {
|
|||
use crate::blockdata::locktime::absolute;
|
||||
use crate::blockdata::transaction;
|
||||
use crate::consensus::encode::{deserialize, serialize};
|
||||
use crate::{
|
||||
Amount, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid,
|
||||
Witness,
|
||||
};
|
||||
use crate::{Amount, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid, Witness};
|
||||
|
||||
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
||||
Transaction {
|
||||
|
|
|
@ -17,8 +17,8 @@ use secp256k1::{Secp256k1, XOnlyPublicKey};
|
|||
|
||||
use crate::crypto::key::{CompressedPublicKey, Keypair, PrivateKey};
|
||||
use crate::internal_macros::impl_bytes_newtype;
|
||||
use crate::prelude::*;
|
||||
use crate::network::NetworkKind;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Version bytes for extended public keys on the Bitcoin network.
|
||||
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.
|
||||
InvalidPublicKeyHexLength(usize),
|
||||
/// Base58 decoded data was an invalid length.
|
||||
InvalidBase58PayloadLength(InvalidBase58PayloadLengthError)
|
||||
InvalidBase58PayloadLength(InvalidBase58PayloadLengthError),
|
||||
}
|
||||
|
||||
internals::impl_from_infallible!(Error);
|
||||
|
@ -885,7 +885,11 @@ impl InvalidBase58PayloadLengthError {
|
|||
|
||||
impl fmt::Display for InvalidBase58PayloadLengthError {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -357,9 +357,7 @@ pub struct DisabledLockTimeError(u32);
|
|||
impl DisabledLockTimeError {
|
||||
/// Accessor for the `u32` whose "disable" flag was set, preventing
|
||||
/// it from being parsed as a relative locktime.
|
||||
pub fn disabled_locktime_value(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
pub fn disabled_locktime_value(&self) -> u32 { self.0 }
|
||||
}
|
||||
|
||||
impl fmt::Display for DisabledLockTimeError {
|
||||
|
|
|
@ -184,8 +184,7 @@ impl Script {
|
|||
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
|
||||
|
||||
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;
|
||||
}
|
||||
// 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")]
|
||||
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
|
||||
/// broadcastable on today's Bitcoin network.
|
||||
///
|
||||
|
|
|
@ -16,8 +16,8 @@ use crate::blockdata::script::{
|
|||
use crate::key::{
|
||||
PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey, WPubkeyHash,
|
||||
};
|
||||
use crate::taproot::TapNodeHash;
|
||||
use crate::prelude::*;
|
||||
use crate::taproot::TapNodeHash;
|
||||
|
||||
/// An owned, growable script.
|
||||
///
|
||||
|
|
|
@ -25,11 +25,11 @@ use crate::blockdata::script::{Script, ScriptBuf};
|
|||
use crate::blockdata::witness::Witness;
|
||||
use crate::blockdata::FeeRate;
|
||||
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::prelude::*;
|
||||
#[cfg(doc)]
|
||||
use crate::sighash::{EcdsaSighashType, TapSighashType};
|
||||
use crate::prelude::*;
|
||||
use crate::{Amount, SignedAmount, VarInt};
|
||||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
|
@ -835,13 +835,7 @@ impl Transaction {
|
|||
size += self
|
||||
.input
|
||||
.iter()
|
||||
.map(|input| {
|
||||
if uses_segwit {
|
||||
input.total_size()
|
||||
} else {
|
||||
input.base_size()
|
||||
}
|
||||
})
|
||||
.map(|input| if uses_segwit { input.total_size() } else { input.base_size() })
|
||||
.sum::<usize>();
|
||||
|
||||
size += VarInt::from(self.output.len()).size();
|
||||
|
@ -1796,10 +1790,7 @@ mod tests {
|
|||
let tx_bytes = hex!("0000fd000001021921212121212121212121f8b372b0239cc1dff600000000004f4f4f4f4f4f4f4f000000000000000000000000000000333732343133380d000000000000000000000000000000ff000000000009000dff000000000000000800000000000000000d");
|
||||
let tx: Result<Transaction, _> = deserialize(&tx_bytes);
|
||||
assert!(tx.is_err());
|
||||
assert!(tx
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("witness flag set but no witnesses present"));
|
||||
assert!(tx.unwrap_err().to_string().contains("witness flag set but no witnesses present"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1960,10 +1951,7 @@ mod tests {
|
|||
format!("{:x}", tx.compute_txid()),
|
||||
"9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:.10x}", tx.compute_txid()),
|
||||
"9652aa62b0"
|
||||
);
|
||||
assert_eq!(format!("{:.10x}", tx.compute_txid()), "9652aa62b0");
|
||||
assert_eq!(tx.weight(), Weight::from_wu(2718));
|
||||
|
||||
// non-segwit tx from my mempool
|
||||
|
|
|
@ -13,8 +13,8 @@ use io::{BufRead, Write};
|
|||
use crate::consensus::encode::{Error, MAX_VEC_SIZE};
|
||||
use crate::consensus::{Decodable, Encodable, WriteExt};
|
||||
use crate::crypto::ecdsa;
|
||||
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
|
||||
use crate::prelude::*;
|
||||
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
|
||||
use crate::{Script, VarInt};
|
||||
|
||||
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
|
||||
|
@ -235,11 +235,7 @@ impl Witness {
|
|||
/// Creates a new empty [`Witness`].
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Witness {
|
||||
content: Vec::new(),
|
||||
witness_elements: 0,
|
||||
indices_start: 0,
|
||||
}
|
||||
Witness { content: Vec::new(), witness_elements: 0, indices_start: 0 }
|
||||
}
|
||||
|
||||
/// 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>()? {
|
||||
let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e {
|
||||
InvalidChar(ref e) => match core::char::from_u32(e.invalid_char(
|
||||
).into()) {
|
||||
InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) {
|
||||
Some(c) => de::Error::invalid_value(
|
||||
Unexpected::Char(c),
|
||||
&"a valid hex character",
|
||||
|
@ -557,7 +552,7 @@ impl Default for Witness {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use hex::{test_hex_unwrap as hex};
|
||||
use hex::test_hex_unwrap as hex;
|
||||
|
||||
use super::*;
|
||||
use crate::consensus::{deserialize, serialize};
|
||||
|
|
|
@ -20,7 +20,7 @@ use core::{fmt, mem, u32};
|
|||
use hashes::{sha256, sha256d, Hash};
|
||||
use hex::error::{InvalidCharError, OddLengthStringError};
|
||||
use internals::write_err;
|
||||
use io::{Cursor, BufRead, Read, Write};
|
||||
use io::{BufRead, Cursor, Read, Write};
|
||||
|
||||
use crate::bip152::{PrefilledTransaction, ShortId};
|
||||
use crate::bip158::{FilterHash, FilterHeader};
|
||||
|
@ -109,7 +109,7 @@ pub enum FromHexError {
|
|||
/// Purported hex string had odd length.
|
||||
OddLengthString(OddLengthStringError),
|
||||
/// Decoding error.
|
||||
Decode(DecodeError<InvalidCharError>)
|
||||
Decode(DecodeError<InvalidCharError>),
|
||||
}
|
||||
|
||||
impl fmt::Display for FromHexError {
|
||||
|
@ -406,13 +406,18 @@ macro_rules! impl_int_encodable {
|
|||
($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
|
||||
impl Decodable for $ty {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
impl Encodable for $ty {
|
||||
#[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)?;
|
||||
Ok(mem::size_of::<$ty>())
|
||||
}
|
||||
|
@ -588,7 +593,9 @@ macro_rules! impl_array {
|
|||
|
||||
impl Decodable for [u8; $size] {
|
||||
#[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];
|
||||
r.read_slice(&mut ret)?;
|
||||
Ok(ret)
|
||||
|
@ -632,7 +639,10 @@ macro_rules! impl_vec {
|
|||
($type: ty) => {
|
||||
impl Encodable for Vec<$type> {
|
||||
#[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;
|
||||
len += VarInt(self.len() as u64).consensus_encode(w)?;
|
||||
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.
|
||||
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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ pub mod validation;
|
|||
|
||||
use core::fmt;
|
||||
|
||||
use io::{Read, BufRead};
|
||||
use internals::write_err;
|
||||
use io::{BufRead, Read};
|
||||
|
||||
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> {
|
||||
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>> {
|
||||
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)) => {
|
||||
self.buf = Some(byte);
|
||||
Ok(core::slice::from_ref(self.buf.as_ref().expect("we've just filled it")))
|
||||
},
|
||||
}
|
||||
Some(Err(error)) => {
|
||||
self.error = Some(error);
|
||||
Err(io::ErrorKind::Other.into())
|
||||
},
|
||||
}
|
||||
None => Ok(&[]),
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +132,8 @@ impl<E: fmt::Debug> fmt::Display for DecodeError<E> {
|
|||
use DecodeError::*;
|
||||
|
||||
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),
|
||||
Other(ref other) => write!(f, "other decoding error: {:?}", other),
|
||||
}
|
||||
|
|
|
@ -136,7 +136,6 @@ impl From<Network> for Params {
|
|||
fn from(value: Network) -> Self { Self::new(value) }
|
||||
}
|
||||
|
||||
|
||||
impl From<&Network> for Params {
|
||||
fn from(value: &Network) -> Self { Self::new(*value) }
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ use serde::de::{SeqAccess, Unexpected, Visitor};
|
|||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
use crate::consensus::{DecodeError, IterReader};
|
||||
use super::encode::Error as ConsensusError;
|
||||
use super::{Decodable, Encodable};
|
||||
use crate::consensus::{DecodeError, IterReader};
|
||||
|
||||
/// Hex-encoding strategy
|
||||
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>);
|
||||
|
||||
impl<C: Case> From<super::Hex<C>> for Encoder<C> {
|
||||
fn from(_: super::Hex<C>) -> Self {
|
||||
Encoder(BufEncoder::new(), Default::default())
|
||||
}
|
||||
fn from(_: super::Hex<C>) -> Self { Encoder(BufEncoder::new(), Default::default()) }
|
||||
}
|
||||
|
||||
impl<C: Case> super::EncodeBytes for Encoder<C> {
|
||||
|
|
|
@ -224,9 +224,7 @@ pub struct SortKey(ArrayVec<u8, 65>);
|
|||
|
||||
impl fmt::Display for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.with_serialized(|bytes| {
|
||||
fmt::Display::fmt(&bytes.as_hex(), f)
|
||||
})
|
||||
self.with_serialized(|bytes| fmt::Display::fmt(&bytes.as_hex(), f))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,14 +237,14 @@ impl FromStr for PublicKey {
|
|||
66 => {
|
||||
let bytes = <[u8; 33]>::from_hex(s).map_err(|e| match e {
|
||||
InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()),
|
||||
InvalidLength(_) => unreachable!("length checked already")
|
||||
InvalidLength(_) => unreachable!("length checked already"),
|
||||
})?;
|
||||
Ok(PublicKey::from_slice(&bytes)?)
|
||||
},
|
||||
}
|
||||
130 => {
|
||||
let bytes = <[u8; 65]>::from_hex(s).map_err(|e| match e {
|
||||
InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()),
|
||||
InvalidLength(_) => unreachable!("length checked already")
|
||||
InvalidLength(_) => unreachable!("length checked already"),
|
||||
})?;
|
||||
Ok(PublicKey::from_slice(&bytes)?)
|
||||
}
|
||||
|
@ -441,7 +439,10 @@ impl PrivateKey {
|
|||
pub fn to_bytes(self) -> Vec<u8> { self.inner[..].to_vec() }
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
||||
|
@ -948,8 +949,10 @@ impl fmt::Display for FromWifError {
|
|||
|
||||
match *self {
|
||||
Base58(ref e) => write_err!(f, "invalid base58"; e),
|
||||
InvalidBase58PayloadLength(ref 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),
|
||||
InvalidBase58PayloadLength(ref 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),
|
||||
}
|
||||
}
|
||||
|
@ -978,7 +981,9 @@ impl From<secp256k1::Error> 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 {
|
||||
|
@ -1004,7 +1009,8 @@ impl fmt::Display for ParsePublicKeyError {
|
|||
match self {
|
||||
Encoding(e) => write_err!(f, "string error"; e),
|
||||
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::*;
|
||||
match self {
|
||||
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);
|
||||
let res = PublicKey::from_str(s);
|
||||
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";
|
||||
assert_eq!(s.len(), 66);
|
||||
let res = PublicKey::from_str(s);
|
||||
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";
|
||||
assert_eq!(s.len(), 130);
|
||||
let res = PublicKey::from_str(s);
|
||||
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";
|
||||
assert_eq!(s.len(), 130);
|
||||
|
|
|
@ -19,8 +19,8 @@ use io::Write;
|
|||
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::consensus::{encode, Encodable};
|
||||
use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX};
|
||||
use crate::prelude::*;
|
||||
use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX};
|
||||
use crate::{transaction, Amount, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut};
|
||||
|
||||
/// 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.
|
||||
if !anyone_can_pay {
|
||||
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)?).script_pubkeys.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)?)
|
||||
.script_pubkeys
|
||||
.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 {
|
||||
input_index,
|
||||
outputs_length: self.tx.borrow().output.len(),
|
||||
})).map_err(SigningDataError::Sighash)?
|
||||
}))
|
||||
.map_err(SigningDataError::Sighash)?
|
||||
.consensus_encode(&mut enc)?;
|
||||
let hash = sha256::Hash::from_engine(enc);
|
||||
hash.consensus_encode(writer)?;
|
||||
|
@ -704,7 +709,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
annex,
|
||||
leaf_hash_code_separator,
|
||||
sighash_type,
|
||||
).map_err(SigningDataError::unwrap_sighash)?;
|
||||
)
|
||||
.map_err(SigningDataError::unwrap_sighash)?;
|
||||
Ok(TapSighash::from_engine(enc))
|
||||
}
|
||||
|
||||
|
@ -723,7 +729,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
None,
|
||||
None,
|
||||
sighash_type,
|
||||
).map_err(SigningDataError::unwrap_sighash)?;
|
||||
)
|
||||
.map_err(SigningDataError::unwrap_sighash)?;
|
||||
Ok(TapSighash::from_engine(enc))
|
||||
}
|
||||
|
||||
|
@ -746,7 +753,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
None,
|
||||
Some((leaf_hash.into(), 0xFFFFFFFF)),
|
||||
sighash_type,
|
||||
).map_err(SigningDataError::unwrap_sighash)?;
|
||||
)
|
||||
.map_err(SigningDataError::unwrap_sighash)?;
|
||||
Ok(TapSighash::from_engine(enc))
|
||||
}
|
||||
|
||||
|
@ -830,7 +838,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
&script_code,
|
||||
value,
|
||||
sighash_type,
|
||||
).map_err(SigningDataError::unwrap_sighash)?;
|
||||
)
|
||||
.map_err(SigningDataError::unwrap_sighash)?;
|
||||
Ok(SegwitV0Sighash::from_engine(enc))
|
||||
}
|
||||
|
||||
|
@ -849,7 +858,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
witness_script,
|
||||
value,
|
||||
sighash_type,
|
||||
).map_err(SigningDataError::unwrap_sighash)?;
|
||||
)
|
||||
.map_err(SigningDataError::unwrap_sighash)?;
|
||||
Ok(SegwitV0Sighash::from_engine(enc))
|
||||
}
|
||||
|
||||
|
@ -1215,9 +1225,7 @@ pub enum P2wpkhError {
|
|||
internals::impl_from_infallible!(P2wpkhError);
|
||||
|
||||
impl From<transaction::InputsIndexError> for P2wpkhError {
|
||||
fn from(value: transaction::InputsIndexError) -> Self {
|
||||
P2wpkhError::Sighash(value)
|
||||
}
|
||||
fn from(value: transaction::InputsIndexError) -> Self { P2wpkhError::Sighash(value) }
|
||||
}
|
||||
|
||||
impl fmt::Display for P2wpkhError {
|
||||
|
@ -1400,17 +1408,13 @@ impl<E> SigningDataError<E> {
|
|||
}
|
||||
}
|
||||
|
||||
fn sighash<E2: Into<E>>(error: E2) -> Self {
|
||||
Self::Sighash(error.into())
|
||||
}
|
||||
fn sighash<E2: Into<E>>(error: E2) -> Self { Self::Sighash(error.into()) }
|
||||
}
|
||||
|
||||
// We cannot simultaneously impl `From<E>`. it was determined that this alternative requires less
|
||||
// manual `map_err` calls.
|
||||
impl<E> From<io::Error> for SigningDataError<E> {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::Io(value)
|
||||
}
|
||||
fn from(value: io::Error) -> Self { Self::Io(value) }
|
||||
}
|
||||
|
||||
impl<E: fmt::Display> fmt::Display for SigningDataError<E> {
|
||||
|
|
|
@ -10,9 +10,9 @@ use core::fmt;
|
|||
use internals::write_err;
|
||||
use io::Write;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::sighash::{InvalidSighashTypeError, TapSighashType};
|
||||
use crate::taproot::serialized_signature::{self, SerializedSignature};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A BIP340-341 serialized taproot signature with the corresponding hash type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
|
|
@ -12,10 +12,12 @@ pub use crate::{
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::hashes::Hash;
|
||||
use super::*;
|
||||
|
||||
use crate::{LegacySighash, SegwitV0Sighash, TapSighash, PubkeyHash, WPubkeyHash, WScriptHash, ScriptHash, XKeyIdentifier};
|
||||
use crate::hashes::Hash;
|
||||
use crate::{
|
||||
LegacySighash, PubkeyHash, ScriptHash, SegwitV0Sighash, TapSighash, WPubkeyHash,
|
||||
WScriptHash, XKeyIdentifier,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn hash_display() {
|
||||
|
@ -45,18 +47,9 @@ mod tests {
|
|||
"dabc11914abcd8072900042a2681e52f8dba99ce82e224f97b5fdb7cd4b9c803",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PubkeyHash::hash(&[]).to_string(),
|
||||
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",
|
||||
);
|
||||
assert_eq!(
|
||||
ScriptHash::hash(&[]).to_string(),
|
||||
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",
|
||||
);
|
||||
assert_eq!(
|
||||
WPubkeyHash::hash(&[]).to_string(),
|
||||
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",
|
||||
);
|
||||
assert_eq!(PubkeyHash::hash(&[]).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",);
|
||||
assert_eq!(ScriptHash::hash(&[]).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",);
|
||||
assert_eq!(WPubkeyHash::hash(&[]).to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",);
|
||||
assert_eq!(
|
||||
WScriptHash::hash(&[]).to_string(),
|
||||
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
|
|
|
@ -107,7 +107,10 @@ macro_rules! impl_bytes_newtype {
|
|||
|
||||
#[cfg(feature = "serde")]
|
||||
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() {
|
||||
s.collect_str(self)
|
||||
} else {
|
||||
|
@ -118,7 +121,9 @@ macro_rules! impl_bytes_newtype {
|
|||
|
||||
#[cfg(feature = "serde")]
|
||||
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() {
|
||||
struct HexVisitor;
|
||||
|
||||
|
|
|
@ -29,17 +29,13 @@
|
|||
//! happen the implementations diverge one day.
|
||||
|
||||
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
|
||||
|
||||
// Experimental features we need.
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(bench, feature(test))]
|
||||
|
||||
// Coding conventions.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// Instead of littering the codebase for non-fuzzing code just globally allow.
|
||||
#![cfg_attr(fuzzing, allow(dead_code, unused_imports))]
|
||||
|
||||
// Exclude lints we don't think are valuable.
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
|
||||
|
|
|
@ -540,9 +540,10 @@ impl std::error::Error for MerkleBlockError {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex::{test_hex_unwrap as hex};
|
||||
use hex::test_hex_unwrap as hex;
|
||||
#[cfg(feature = "rand-std")]
|
||||
use secp256k1::rand::prelude::*;
|
||||
|
||||
use super::*;
|
||||
use crate::consensus::encode::{deserialize, serialize};
|
||||
|
||||
|
@ -832,7 +833,8 @@ mod tests {
|
|||
000000000300000000000003000000000200000000ff00000000c7f1ccb10407\
|
||||
00000000000000ccb100c76538b100000004bfa9c251681b1b00040000000025\
|
||||
00000004bfaac251681b1b25\
|
||||
");
|
||||
"
|
||||
);
|
||||
let deser = crate::consensus::deserialize::<MerkleBlock>(&bytes);
|
||||
assert!(deser.is_err());
|
||||
}
|
||||
|
|
|
@ -742,8 +742,7 @@ mod test {
|
|||
0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2
|
||||
]);
|
||||
let preimage =
|
||||
RawNetworkMessage::new(Magic::BITCOIN, NetworkMessage::GetAddr);
|
||||
let preimage = RawNetworkMessage::new(Magic::BITCOIN, NetworkMessage::GetAddr);
|
||||
assert!(msg.is_ok());
|
||||
let msg: RawNetworkMessage = msg.unwrap();
|
||||
assert_eq!(preimage.magic, msg.magic);
|
||||
|
|
|
@ -11,10 +11,10 @@ use io::{BufRead, Write};
|
|||
|
||||
use crate::consensus::{encode, Decodable, Encodable, ReadExt};
|
||||
use crate::internal_macros::impl_consensus_encoding;
|
||||
use crate::p2p;
|
||||
use crate::p2p::address::Address;
|
||||
use crate::p2p::ServiceFlags;
|
||||
use crate::prelude::*;
|
||||
use crate::p2p;
|
||||
|
||||
/// Some simple messages
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::blockdata::block::BlockHash;
|
|||
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||
#[cfg(doc)]
|
||||
use crate::consensus::Params;
|
||||
use crate::error::{PrefixedHexError, UnprefixedHexError, ContainsPrefixError, MissingPrefixError};
|
||||
use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError};
|
||||
use crate::Network;
|
||||
|
||||
/// Implement traits and methods shared by `Target` and `Work`.
|
||||
|
@ -44,17 +44,23 @@ macro_rules! do_impl {
|
|||
|
||||
impl fmt::Display for $ty {
|
||||
#[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 {
|
||||
#[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 {
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,13 +24,12 @@ use secp256k1::{Keypair, Message, Secp256k1, Signing, Verification};
|
|||
|
||||
use crate::bip32::{self, KeySource, Xpriv, Xpub};
|
||||
use crate::blockdata::transaction::{self, Transaction, TxOut};
|
||||
use crate::crypto::{ecdsa, taproot};
|
||||
use crate::crypto::key::{PrivateKey, PublicKey};
|
||||
use crate::crypto::{ecdsa, taproot};
|
||||
use crate::key::TapTweak;
|
||||
use crate::prelude::*;
|
||||
use crate::sighash::{self, EcdsaSighashType, Prevouts, SighashCache};
|
||||
use crate::{Amount, FeeRate, TapSighashType};
|
||||
use crate::key::TapTweak;
|
||||
use crate::TapLeafHash;
|
||||
use crate::{Amount, FeeRate, TapLeafHash, TapSighashType};
|
||||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[doc(inline)]
|
||||
|
@ -316,7 +315,7 @@ impl Psbt {
|
|||
|
||||
for i in 0..self.inputs.len() {
|
||||
match self.signing_algorithm(i) {
|
||||
Ok(SigningAlgorithm::Ecdsa) => {
|
||||
Ok(SigningAlgorithm::Ecdsa) =>
|
||||
match self.bip32_sign_ecdsa(k, i, &mut cache, secp) {
|
||||
Ok(v) => {
|
||||
used.insert(i, v);
|
||||
|
@ -324,8 +323,7 @@ impl Psbt {
|
|||
Err(e) => {
|
||||
errors.insert(i, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(SigningAlgorithm::Schnorr) => {
|
||||
match self.bip32_sign_schnorr(k, i, &mut cache, secp) {
|
||||
Ok(v) => {
|
||||
|
@ -422,13 +420,14 @@ impl Psbt {
|
|||
T: Borrow<Transaction>,
|
||||
K: GetKey,
|
||||
{
|
||||
|
||||
let mut input = self.inputs[input_index].clone();
|
||||
|
||||
let mut used = vec![]; // List of pubkeys used to sign the input.
|
||||
|
||||
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
|
||||
} else {
|
||||
continue;
|
||||
|
@ -441,7 +440,6 @@ impl Psbt {
|
|||
|
||||
// key path spend
|
||||
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.
|
||||
|
||||
// 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<_>>();
|
||||
|
||||
if !leaf_hashes.is_empty() {
|
||||
|
||||
let key_pair = Keypair::from_secret_key(secp, &sk.inner);
|
||||
|
||||
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")]
|
||||
let signature = secp.sign_schnorr(&msg, &key_pair);
|
||||
|
@ -548,8 +546,9 @@ impl Psbt {
|
|||
Wsh | ShWsh => {
|
||||
let witness_script =
|
||||
input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?;
|
||||
let sighash =
|
||||
cache.p2wsh_signature_hash(input_index, witness_script, utxo.value, hash_ty).map_err(SignError::SegwitV0Sighash)?;
|
||||
let sighash = cache
|
||||
.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))
|
||||
}
|
||||
Tr => {
|
||||
|
@ -567,7 +566,7 @@ impl Psbt {
|
|||
&self,
|
||||
input_index: usize,
|
||||
cache: &mut SighashCache<T>,
|
||||
leaf_hash: Option<TapLeafHash>
|
||||
leaf_hash: Option<TapLeafHash>,
|
||||
) -> Result<(Message, TapSighashType), SignError> {
|
||||
use OutputType::*;
|
||||
|
||||
|
@ -585,9 +584,8 @@ impl Psbt {
|
|||
.taproot_hash_ty()
|
||||
.map_err(|_| SignError::InvalidSighashType)?;
|
||||
|
||||
let spend_utxos = (0..self.inputs.len())
|
||||
.map(|i| self.spend_utxo(i).ok())
|
||||
.collect::<Vec<_>>();
|
||||
let spend_utxos =
|
||||
(0..self.inputs.len()).map(|i| self.spend_utxo(i).ok()).collect::<Vec<_>>();
|
||||
let all_spend_utxos;
|
||||
|
||||
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 {
|
||||
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 {
|
||||
cache.taproot_key_spend_signature_hash(input_index, &prev_outs, hash_ty)?
|
||||
};
|
||||
Ok((Message::from(sighash), hash_ty))
|
||||
}
|
||||
_ => {
|
||||
Err(SignError::Unsupported)
|
||||
}
|
||||
_ => Err(SignError::Unsupported),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1443,8 +1443,8 @@ impl std::error::Error for TaprootError {
|
|||
mod test {
|
||||
use core::str::FromStr;
|
||||
|
||||
use hashes::sha256t::Tag;
|
||||
use hashes::sha256;
|
||||
use hashes::sha256t::Tag;
|
||||
use hex::FromHex;
|
||||
use secp256k1::VerifyOnly;
|
||||
|
||||
|
|
|
@ -2,20 +2,22 @@
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
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::consensus::encode::serialize_hex;
|
||||
use bitcoin::opcodes::all::OP_CHECKSIG;
|
||||
use bitcoin::psbt::{GetKey, Input, KeyRequest, PsbtSighashType, SignError};
|
||||
use bitcoin::taproot::{LeafVersion, TaprootBuilder, TaprootSpendInfo};
|
||||
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;
|
||||
|
||||
|
||||
#[test]
|
||||
fn psbt_sign_taproot() {
|
||||
|
||||
struct Keystore {
|
||||
sk: PrivateKey,
|
||||
mfp: Fingerprint,
|
||||
|
@ -23,16 +25,19 @@ fn psbt_sign_taproot() {
|
|||
|
||||
impl GetKey for Keystore {
|
||||
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 {
|
||||
KeyRequest::Bip32((mfp, _)) => {
|
||||
KeyRequest::Bip32((mfp, _)) =>
|
||||
if mfp == self.mfp {
|
||||
Ok(Some(self.sk))
|
||||
} else {
|
||||
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 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());
|
||||
assert_eq!("tb1pytee2mxz0f4fkrsqqws2lsgnkp8nrw2atjkjy2n9gahggsphr0gszaxxmv", address.to_string());
|
||||
assert_eq!(
|
||||
"tb1pytee2mxz0f4fkrsqqws2lsgnkp8nrw2atjkjy2n9gahggsphr0gszaxxmv",
|
||||
address.to_string()
|
||||
);
|
||||
|
||||
// m/86'/1'/0'/0/7
|
||||
let to_address = "tb1pyfv094rr0vk28lf8v9yx3veaacdzg26ztqk4ga84zucqqhafnn5q9my9rz";
|
||||
|
@ -74,7 +83,11 @@ fn psbt_sign_taproot() {
|
|||
//
|
||||
// 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.
|
||||
|
@ -117,7 +130,14 @@ fn psbt_sign_taproot() {
|
|||
//
|
||||
// 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.
|
||||
|
@ -125,7 +145,15 @@ fn psbt_sign_taproot() {
|
|||
let _ = psbt_script_path_spend.sign(&keystore, secp);
|
||||
|
||||
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.
|
||||
|
@ -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 x_only_pubkey = kp.x_only_public_key().0;
|
||||
script::Builder::new()
|
||||
|
@ -151,7 +179,13 @@ fn create_basic_single_sig_script(secp: &Secp256k1::<secp256k1::All>, sk: &str)
|
|||
.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 = builder.add_leaf(2, script1).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)
|
||||
}
|
||||
|
||||
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 out_puts = vec![
|
||||
TxOut { value: Amount::from_sat(send_value), script_pubkey: to_address.script_pubkey() },
|
||||
];
|
||||
let out_puts = vec![TxOut {
|
||||
value: Amount::from_sat(send_value),
|
||||
script_pubkey: to_address.script_pubkey(),
|
||||
}];
|
||||
let prev_tx_id = "06980ca116f74c7845a897461dd0e1d15b114130176de5004957da516b4dee3a";
|
||||
|
||||
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 mfp = "73c5da0a";
|
||||
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
|
||||
}
|
||||
|
||||
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 send_value = 6000;
|
||||
let mfp = "73c5da0a";
|
||||
|
||||
let out_puts = vec![
|
||||
TxOut { value: Amount::from_sat(send_value), script_pubkey: to_address.script_pubkey() },
|
||||
];
|
||||
let out_puts = vec![TxOut {
|
||||
value: Amount::from_sat(send_value),
|
||||
script_pubkey: to_address.script_pubkey(),
|
||||
}];
|
||||
let prev_tx_id = "9d7c6770fca57285babab60c51834cfcfd10ad302119cae842d7216b4ac9a376";
|
||||
let transaction = Transaction {
|
||||
version: Version(2),
|
||||
|
@ -291,7 +336,6 @@ fn create_psbt_for_taproot_script_path_spend(from_address: Address, to_address:
|
|||
psbt
|
||||
}
|
||||
|
||||
|
||||
fn finalize_psbt_for_script_path_spend(mut psbt: Psbt) -> Psbt {
|
||||
psbt.inputs.iter_mut().for_each(|input| {
|
||||
let mut script_witness: Witness = Witness::new();
|
||||
|
|
|
@ -66,17 +66,13 @@
|
|||
//! ```
|
||||
|
||||
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
|
||||
|
||||
// Experimental features we need.
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(bench, feature(test))]
|
||||
|
||||
// Coding conventions.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// Instead of littering the codebase for non-fuzzing code just globally allow.
|
||||
#![cfg_attr(hashes_fuzz, allow(dead_code, unused_imports))]
|
||||
|
||||
// Exclude lints we don't think are valuable.
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
|
||||
|
|
|
@ -815,8 +815,8 @@ impl HashEngine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{sha256, Hash as _, HashEngine};
|
||||
use super::*;
|
||||
use crate::{sha256, Hash as _, HashEngine};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
|
|
|
@ -7,13 +7,10 @@
|
|||
//!
|
||||
|
||||
#![no_std]
|
||||
|
||||
// Experimental features we need.
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
// Coding conventions.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// Exclude lints we don't think are valuable.
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
|
||||
|
|
|
@ -99,7 +99,10 @@ macro_rules! impl_array_newtype {
|
|||
macro_rules! debug_from_display {
|
||||
($thing:ident) => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,10 @@
|
|||
//! `std::io`'s traits without unnecessary complexity.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
// Experimental features we need.
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
// Coding conventions.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// Exclude lints we don't think are valuable.
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
|
||||
|
@ -325,11 +322,11 @@ pub fn sink() -> Sink { Sink }
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||
use alloc::{string::ToString, vec};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn buf_read_fill_and_consume_slice() {
|
||||
let data = [0_u8, 1, 2];
|
||||
|
|
|
@ -5,21 +5,19 @@
|
|||
//! This module mainly introduces the [Amount] and [SignedAmount] types.
|
||||
//! 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::fmt;
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::fmt::Write as _;
|
||||
use core::str::FromStr;
|
||||
use core::{default, ops};
|
||||
use core::{default, fmt, ops};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use ::serde::{Deserialize, Serialize};
|
||||
use internals::error::InputString;
|
||||
use internals::write_err;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
/// A set of denominations in which amounts can be expressed.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -198,7 +196,8 @@ impl fmt::Display for ParseError {
|
|||
ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error),
|
||||
// We consider this to not be a source because it currently doesn't contain useful
|
||||
// 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 {
|
||||
fn from(value: TooPreciseError) -> Self {
|
||||
Self::TooPrecise(value)
|
||||
}
|
||||
fn from(value: TooPreciseError) -> Self { Self::TooPrecise(value) }
|
||||
}
|
||||
|
||||
impl From<MissingDigitsError> for ParseAmountError {
|
||||
fn from(value: MissingDigitsError) -> Self {
|
||||
Self::MissingDigits(value)
|
||||
}
|
||||
fn from(value: MissingDigitsError) -> Self { Self::MissingDigits(value) }
|
||||
}
|
||||
|
||||
impl From<InputTooLargeError> for ParseAmountError {
|
||||
fn from(value: InputTooLargeError) -> Self {
|
||||
Self::InputTooLarge(value)
|
||||
fn from(value: InputTooLargeError) -> Self { Self::InputTooLarge(value) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<InvalidCharacterError> for ParseAmountError {
|
||||
fn from(value: InvalidCharacterError) -> Self {
|
||||
Self::InvalidCharacter(value)
|
||||
}
|
||||
fn from(value: InvalidCharacterError) -> Self { Self::InvalidCharacter(value) }
|
||||
}
|
||||
|
||||
internals::impl_from_infallible!(ParseAmountError);
|
||||
|
@ -307,21 +297,12 @@ impl OutOfRangeError {
|
|||
}
|
||||
|
||||
/// Returns true if the input value was large than the maximum allowed value.
|
||||
pub fn is_above_max(&self) -> bool {
|
||||
self.is_greater_than_max
|
||||
}
|
||||
pub fn is_above_max(&self) -> bool { self.is_greater_than_max }
|
||||
|
||||
/// Returns true if the input value was smaller than the minimum allowed value.
|
||||
pub fn is_below_min(&self) -> bool {
|
||||
!self.is_greater_than_max
|
||||
}
|
||||
pub fn is_below_min(&self) -> bool { !self.is_greater_than_max }
|
||||
|
||||
pub(crate) fn too_big(is_signed: bool) -> Self {
|
||||
Self {
|
||||
is_signed,
|
||||
is_greater_than_max: true,
|
||||
}
|
||||
}
|
||||
pub(crate) fn too_big(is_signed: bool) -> Self { Self { is_signed, is_greater_than_max: true } }
|
||||
|
||||
pub(crate) fn too_small() -> Self {
|
||||
Self {
|
||||
|
@ -354,9 +335,7 @@ impl fmt::Display for OutOfRangeError {
|
|||
impl std::error::Error for OutOfRangeError {}
|
||||
|
||||
impl From<OutOfRangeError> for ParseAmountError {
|
||||
fn from(value: OutOfRangeError) -> Self {
|
||||
ParseAmountError::OutOfRange(value)
|
||||
}
|
||||
fn from(value: OutOfRangeError) -> Self { ParseAmountError::OutOfRange(value) }
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
match self.position {
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
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),
|
||||
n => write!(f, "the input is {} characters longer than the maximum allowed length ({})", n, INPUT_STRING_LEN_LIMIT),
|
||||
1 => write!(
|
||||
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 {
|
||||
match self.kind {
|
||||
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 {
|
||||
'.' => 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"),
|
||||
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.
|
||||
fn is_too_precise(s: &str, precision: usize) -> Option<usize> {
|
||||
match s.find('.') {
|
||||
Some(pos) if precision >= pos => { Some(0) },
|
||||
Some(pos) => {
|
||||
s[..pos].char_indices().rev().take(precision).find(|(_, d)| *d != '0').map(|(i, _)| i)
|
||||
Some(pos) if precision >= pos => Some(0),
|
||||
Some(pos) => s[..pos]
|
||||
.char_indices()
|
||||
.rev()
|
||||
.take(precision)
|
||||
.find(|(_, d)| *d != '0')
|
||||
.map(|(i, _)| i)
|
||||
.or_else(|| {
|
||||
s[(pos + 1)..].char_indices().find(|(_, d)| *d != '0').map(|(i, _)| i + pos + 1)
|
||||
})
|
||||
},
|
||||
None if precision >= s.len() => { Some(0) },
|
||||
None => {
|
||||
s.char_indices().rev().take(precision).find(|(_, d)| *d != '0').map(|(i, _)| i)
|
||||
},
|
||||
}),
|
||||
None if precision >= s.len() => Some(0),
|
||||
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,
|
||||
) -> Result<(bool, u64), InnerParseError> {
|
||||
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 {
|
||||
return Err(InnerParseError::InputTooLarge(s.len()));
|
||||
|
@ -549,7 +548,9 @@ fn parse_signed_to_satoshi(
|
|||
let is_negative = s.starts_with('-');
|
||||
if is_negative {
|
||||
if s.len() == 1 {
|
||||
return Err(InnerParseError::MissingDigits(MissingDigitsError { kind: MissingDigitsKind::OnlyMinusSign }));
|
||||
return Err(InnerParseError::MissingDigits(MissingDigitsError {
|
||||
kind: MissingDigitsKind::OnlyMinusSign,
|
||||
}));
|
||||
}
|
||||
s = &s[1..];
|
||||
}
|
||||
|
@ -567,7 +568,10 @@ fn parse_signed_to_satoshi(
|
|||
if let Some(position) = is_too_precise(s, last_n) {
|
||||
match s.parse::<i64>() {
|
||||
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];
|
||||
|
@ -594,16 +598,27 @@ fn parse_signed_to_satoshi(
|
|||
decimals = match decimals {
|
||||
None => None,
|
||||
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 {
|
||||
None if max_decimals <= 0 => break,
|
||||
None => decimals = Some(0),
|
||||
// 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 {
|
||||
fn convert(self, is_signed: bool) -> ParseAmountError {
|
||||
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::MissingDigits(error) => ParseAmountError::MissingDigits(error),
|
||||
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(' ') {
|
||||
(i, i + 1)
|
||||
} 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)
|
||||
};
|
||||
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
|
||||
/// with denomination, use [FromStr].
|
||||
pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
|
||||
let (negative, satoshi) = parse_signed_to_satoshi(s, denom)
|
||||
.map_err(|error| error.convert(false))?;
|
||||
let (negative, satoshi) =
|
||||
parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
|
||||
if negative {
|
||||
return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative()));
|
||||
}
|
||||
|
@ -1029,16 +1047,12 @@ impl Amount {
|
|||
/// Unchecked addition.
|
||||
///
|
||||
/// Computes `self + rhs`. Panics in debug mode, wraps in release mode.
|
||||
pub fn unchecked_add(self, rhs: Amount) -> Amount {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
pub fn unchecked_add(self, rhs: Amount) -> Amount { Self(self.0 + rhs.0) }
|
||||
|
||||
/// Unchecked subtraction.
|
||||
///
|
||||
/// Computes `self - rhs`. Panics in debug mode, wraps in release mode.
|
||||
pub fn unchecked_sub(self, rhs: Amount) -> Amount {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
pub fn unchecked_sub(self, rhs: Amount) -> Amount { Self(self.0 - rhs.0) }
|
||||
|
||||
/// Convert to a signed amount.
|
||||
pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> {
|
||||
|
@ -1055,9 +1069,7 @@ impl default::Default for Amount {
|
|||
}
|
||||
|
||||
impl fmt::Debug for Amount {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} SAT", self.to_sat())
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} SAT", self.to_sat()) }
|
||||
}
|
||||
|
||||
// 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 {
|
||||
type Error = OutOfRangeError;
|
||||
|
||||
fn try_from(value: SignedAmount) -> Result<Self, Self::Error> {
|
||||
value.to_unsigned()
|
||||
}
|
||||
fn try_from(value: SignedAmount) -> Result<Self, Self::Error> { value.to_unsigned() }
|
||||
}
|
||||
|
||||
impl core::iter::Sum for Amount {
|
||||
|
@ -1264,10 +1274,12 @@ impl SignedAmount {
|
|||
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))? {
|
||||
// (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)),
|
||||
(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))),
|
||||
}
|
||||
}
|
||||
|
@ -1423,16 +1435,12 @@ impl SignedAmount {
|
|||
/// Unchecked addition.
|
||||
///
|
||||
/// Computes `self + rhs`. Panics in debug mode, wraps in release mode.
|
||||
pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount { Self(self.0 + rhs.0) }
|
||||
|
||||
/// Unchecked subtraction.
|
||||
///
|
||||
/// Computes `self - rhs`. Panics in debug mode, wraps in release mode.
|
||||
pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount { Self(self.0 - rhs.0) }
|
||||
|
||||
/// Subtraction that doesn't allow negative [SignedAmount]s.
|
||||
/// 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 {
|
||||
type Error = OutOfRangeError;
|
||||
|
||||
fn try_from(value: Amount) -> Result<Self, Self::Error> {
|
||||
value.to_signed()
|
||||
}
|
||||
fn try_from(value: Amount) -> Result<Self, Self::Error> { value.to_signed() }
|
||||
}
|
||||
|
||||
impl core::iter::Sum for SignedAmount {
|
||||
|
@ -1621,11 +1627,12 @@ pub mod serde {
|
|||
//! ```
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use super::{Amount, SignedAmount, ParseAmountError};
|
||||
#[cfg(feature = "alloc")]
|
||||
use super::Denomination;
|
||||
use super::{Amount, ParseAmountError, SignedAmount};
|
||||
|
||||
/// This trait is used only to avoid code duplication and naming collisions
|
||||
/// of the different serde serialization crates.
|
||||
|
@ -1670,9 +1677,7 @@ pub mod serde {
|
|||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl fmt::Display for DisplayFullError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
||||
}
|
||||
|
||||
impl SerdeAmount for Amount {
|
||||
|
@ -1741,10 +1746,9 @@ pub mod serde {
|
|||
//! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi.
|
||||
//! Use with `#[serde(with = "amount::serde::as_sat")]`.
|
||||
//!
|
||||
use super::private;
|
||||
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
use super::private;
|
||||
use crate::amount::serde::SerdeAmount;
|
||||
|
||||
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.
|
||||
//! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`.
|
||||
|
||||
use super::private;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use serde::{de, Deserializer, Serializer};
|
||||
|
||||
use super::private;
|
||||
use crate::amount::serde::SerdeAmountForOpt;
|
||||
|
||||
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.
|
||||
//! Use with `#[serde(with = "amount::serde::as_btc")]`.
|
||||
|
||||
use super::private;
|
||||
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
use super::private;
|
||||
use crate::amount::serde::SerdeAmount;
|
||||
|
||||
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.
|
||||
//! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`.
|
||||
|
||||
use super::private;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use serde::{de, Deserializer, Serializer};
|
||||
|
||||
use super::private;
|
||||
use crate::amount::serde::SerdeAmountForOpt;
|
||||
|
||||
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
|
||||
|
@ -1971,10 +1974,7 @@ mod verification {
|
|||
if n1 >= 0 {
|
||||
Ok(Amount::from_sat(n1.try_into().unwrap()))
|
||||
} else {
|
||||
Err(OutOfRangeError {
|
||||
is_signed: true,
|
||||
is_greater_than_max: false
|
||||
})
|
||||
Err(OutOfRangeError { is_signed: true, is_greater_than_max: false })
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -2008,7 +2008,6 @@ mod verification {
|
|||
mod tests {
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::format;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::panic;
|
||||
|
||||
|
@ -2029,7 +2028,10 @@ mod tests {
|
|||
|
||||
let s = format!("-0 {}", denom);
|
||||
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),
|
||||
}
|
||||
match SignedAmount::from_str(&s) {
|
||||
|
@ -2057,13 +2059,7 @@ mod tests {
|
|||
|
||||
let ua_max = Amount::MAX;
|
||||
let result = SignedAmount::try_from(ua_max);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(OutOfRangeError {
|
||||
is_signed: true,
|
||||
is_greater_than_max: true
|
||||
})
|
||||
);
|
||||
assert_eq!(result, Err(OutOfRangeError { is_signed: true, is_greater_than_max: true }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2074,13 +2070,7 @@ mod tests {
|
|||
|
||||
let sa_negative = SignedAmount(-123);
|
||||
let result = Amount::try_from(sa_negative);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(OutOfRangeError {
|
||||
is_signed: false,
|
||||
is_greater_than_max: false
|
||||
})
|
||||
);
|
||||
assert_eq!(result, Err(OutOfRangeError { is_signed: false, is_greater_than_max: false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2175,7 +2165,10 @@ mod tests {
|
|||
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!(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.
|
||||
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 sp = SignedAmount::from_str_in;
|
||||
|
||||
assert_eq!(p("x", btc), Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 0 })));
|
||||
assert_eq!(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 })));
|
||||
assert_eq!(
|
||||
p("x", btc),
|
||||
Err(E::from(InvalidCharacterError { invalid_char: 'x', position: 0 }))
|
||||
);
|
||||
assert_eq!(
|
||||
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")]
|
||||
let more_than_max = format!("1{}", Amount::MAX);
|
||||
#[cfg(feature = "alloc")]
|
||||
|
@ -2253,11 +2264,16 @@ mod tests {
|
|||
{
|
||||
let amount = Amount::from_sat(i64::MAX as u64);
|
||||
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_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.
|
||||
assert_eq!(
|
||||
p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin),
|
||||
|
@ -2500,7 +2516,10 @@ mod tests {
|
|||
|
||||
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!(
|
||||
Amount::from_str("xBTC"),
|
||||
Err(Unknown(UnknownDenominationError("xBTC".into())).into()),
|
||||
|
@ -2509,7 +2528,10 @@ mod tests {
|
|||
Amount::from_str("5 BTC BTC"),
|
||||
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!(
|
||||
Amount::from_str("5 5 BTC"),
|
||||
Err(Unknown(UnknownDenominationError("5 BTC".into())).into()),
|
||||
|
@ -2732,7 +2754,10 @@ mod tests {
|
|||
// errors
|
||||
let t: Result<T, serde_json::Error> =
|
||||
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}");
|
||||
assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string()));
|
||||
}
|
||||
|
@ -2830,11 +2855,8 @@ mod tests {
|
|||
let sum = amounts.into_iter().sum::<Amount>();
|
||||
assert_eq!(Amount::from_sat(1400), sum);
|
||||
|
||||
let amounts = [
|
||||
SignedAmount::from_sat(-42),
|
||||
SignedAmount::from_sat(1337),
|
||||
SignedAmount::from_sat(21),
|
||||
];
|
||||
let amounts =
|
||||
[SignedAmount::from_sat(-42), SignedAmount::from_sat(1337), SignedAmount::from_sat(21)];
|
||||
let sum = amounts.into_iter().sum::<SignedAmount>();
|
||||
assert_eq!(SignedAmount::from_sat(1316), sum);
|
||||
}
|
||||
|
@ -2848,8 +2870,7 @@ mod tests {
|
|||
let sum = amounts.into_iter().checked_sum();
|
||||
assert_eq!(Some(Amount::from_sat(1400)), sum);
|
||||
|
||||
let amounts =
|
||||
[Amount::from_sat(u64::MAX), Amount::from_sat(1337), Amount::from_sat(21)];
|
||||
let amounts = [Amount::from_sat(u64::MAX), Amount::from_sat(1337), Amount::from_sat(21)];
|
||||
let sum = amounts.into_iter().checked_sum();
|
||||
assert_eq!(None, sum);
|
||||
|
||||
|
@ -2869,11 +2890,8 @@ mod tests {
|
|||
let sum = amounts.into_iter().checked_sum();
|
||||
assert_eq!(None, sum);
|
||||
|
||||
let amounts = [
|
||||
SignedAmount::from_sat(42),
|
||||
SignedAmount::from_sat(3301),
|
||||
SignedAmount::from_sat(21),
|
||||
];
|
||||
let amounts =
|
||||
[SignedAmount::from_sat(42), SignedAmount::from_sat(3301), SignedAmount::from_sat(21)];
|
||||
let sum = amounts.into_iter().checked_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!("{}", Amount::from_sat(100_000_000)), "1 BTC");
|
||||
assert_eq!(format!("{}", Amount::from_sat(40_000_000_000)), "400 BTC");
|
||||
assert_eq!(
|
||||
format!("{:.10}", Amount::from_sat(100_000_000)),
|
||||
"1.0000000000 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"
|
||||
);
|
||||
assert_eq!(format!("{:.10}", Amount::from_sat(100_000_000)), "1.0000000000 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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,11 @@
|
|||
|
||||
// Experimental features we need.
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
// Coding conventions.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// Exclude lints we don't think are valuable.
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
|
||||
|
||||
#![no_std]
|
||||
|
||||
// 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;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod locktime;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod fee_rate;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod locktime;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod parse;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod weight;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::{
|
||||
amount::{Amount, ParseAmountError, SignedAmount},
|
||||
};
|
||||
pub use self::amount::{Amount, ParseAmountError, SignedAmount};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::parse::ParseIntError;
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
//! Parsing utilities.
|
||||
|
||||
use alloc::string::String;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
use alloc::string::String;
|
||||
|
||||
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
|
||||
/// passes non-integer to the `parse()` function.
|
||||
pub trait Integer:
|
||||
FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized
|
||||
{
|
||||
}
|
||||
pub trait Integer: FromStr<Err = core::num::ParseIntError> + TryFrom<i8> + Sized {}
|
||||
|
||||
macro_rules! impl_integer {
|
||||
($($type:ty),* $(,)?) => {
|
||||
|
|
Loading…
Reference in New Issue