2024-03-31 automated rustfmt nightly
This commit is contained in:
parent
4163641f44
commit
a565db9fdd
|
@ -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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)) => {
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl std::error::Error for FromScriptError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<witness_program::Error> for FromScriptError {
|
impl From<witness_program::Error> for FromScriptError {
|
||||||
fn from(e : witness_program::Error) -> Self { Self::WitnessProgram(e) }
|
fn from(e: witness_program::Error) -> Self { Self::WitnessProgram(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<witness_version::TryFromError> for FromScriptError {
|
impl From<witness_version::TryFromError> for FromScriptError {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -964,7 +967,7 @@ impl std::error::Error for FromWifError {
|
||||||
Base58(ref e) => Some(e),
|
Base58(ref e) => Some(e),
|
||||||
InvalidBase58PayloadLength(ref e) => Some(e),
|
InvalidBase58PayloadLength(ref e) => Some(e),
|
||||||
InvalidAddressVersion(ref e) => Some(e),
|
InvalidAddressVersion(ref e) => Some(e),
|
||||||
Secp256k1(ref e)=> Some(e),
|
Secp256k1(ref e) => Some(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);
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,7 +226,7 @@ impl Decodable for AddrV2 {
|
||||||
}
|
}
|
||||||
let addr: [u16; 8] = read_be_address(r)?;
|
let addr: [u16; 8] = read_be_address(r)?;
|
||||||
// check the first byte for the CJDNS marker
|
// check the first byte for the CJDNS marker
|
||||||
if addr[0] >> 8 != 0xFC{
|
if addr[0] >> 8 != 0xFC {
|
||||||
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
||||||
}
|
}
|
||||||
AddrV2::Cjdns(Ipv6Addr::new(
|
AddrV2::Cjdns(Ipv6Addr::new(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
@ -276,7 +321,7 @@ fn create_psbt_for_taproot_script_path_spend(from_address: Address, to_address:
|
||||||
|
|
||||||
let mut input = Input {
|
let mut input = Input {
|
||||||
witness_utxo: {
|
witness_utxo: {
|
||||||
let script_pubkey= from_address.script_pubkey();
|
let script_pubkey = from_address.script_pubkey();
|
||||||
Some(TxOut { value: Amount::from_sat(utxo_value), script_pubkey })
|
Some(TxOut { value: Amount::from_sat(utxo_value), script_pubkey })
|
||||||
},
|
},
|
||||||
tap_key_origins: origins,
|
tap_key_origins: origins,
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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),* $(,)?) => {
|
||||||
|
|
Loading…
Reference in New Issue