Run cargo fmt
Run the command `cargo +nightly fmt` to fix formatting issues. No other changes other than those introduced by `rustfmt`.
This commit is contained in:
parent
1ecf09359b
commit
408d7737fb
|
@ -4,10 +4,10 @@ use std::str::FromStr;
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
|
|
||||||
use bitcoin::address::Address;
|
use bitcoin::address::Address;
|
||||||
|
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey};
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
use bitcoin::secp256k1::ffi::types::AlignedType;
|
use bitcoin::secp256k1::ffi::types::AlignedType;
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey};
|
|
||||||
use bitcoin::PublicKey;
|
use bitcoin::PublicKey;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -33,14 +33,14 @@ use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use bitcoin::consensus::encode;
|
|
||||||
use bitcoin::hashes::hex::FromHex;
|
|
||||||
use bitcoin::locktime::absolute;
|
|
||||||
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
|
|
||||||
use bitcoin::bip32::{
|
use bitcoin::bip32::{
|
||||||
ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivationPath,
|
ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivationPath,
|
||||||
};
|
};
|
||||||
|
use bitcoin::consensus::encode;
|
||||||
|
use bitcoin::hashes::hex::FromHex;
|
||||||
|
use bitcoin::locktime::absolute;
|
||||||
use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType};
|
use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType};
|
||||||
|
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
Address, Amount, Network, OutPoint, PublicKey, Script, Sequence, Transaction, TxIn, TxOut,
|
Address, Amount, Network, OutPoint, PublicKey, Script, Sequence, Transaction, TxIn, TxOut,
|
||||||
Txid, Witness,
|
Txid, Witness,
|
||||||
|
|
|
@ -78,6 +78,7 @@ const UTXO_3: P2trUtxo = P2trUtxo {
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
|
||||||
use bitcoin::consensus::encode;
|
use bitcoin::consensus::encode;
|
||||||
use bitcoin::constants::COIN_VALUE;
|
use bitcoin::constants::COIN_VALUE;
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
|
@ -87,16 +88,13 @@ use bitcoin::psbt::serialize::Serialize;
|
||||||
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
|
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
|
||||||
use bitcoin::schnorr::TapTweak;
|
use bitcoin::schnorr::TapTweak;
|
||||||
use bitcoin::secp256k1::{Message, Secp256k1};
|
use bitcoin::secp256k1::{Message, Secp256k1};
|
||||||
use bitcoin::bip32::{
|
use bitcoin::sighash::{self, SchnorrSighashType, SighashCache};
|
||||||
ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint,
|
|
||||||
};
|
|
||||||
use bitcoin::sighash::{self, SighashCache, SchnorrSighashType};
|
|
||||||
use bitcoin::util::taproot::{
|
use bitcoin::util::taproot::{
|
||||||
LeafVersion, TapLeafHash, TapSighashHash, TaprootBuilder, TaprootSpendInfo,
|
LeafVersion, TapLeafHash, TapSighashHash, TaprootBuilder, TaprootSpendInfo,
|
||||||
};
|
};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
absolute, script, Address, Amount, OutPoint, SchnorrSig, Script,
|
absolute, script, Address, Amount, OutPoint, SchnorrSig, Script, Transaction, TxIn, TxOut,
|
||||||
Transaction, TxIn, TxOut, Txid, Witness, XOnlyPublicKey,
|
Txid, Witness, XOnlyPublicKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
|
@ -34,9 +34,9 @@ use crate::blockdata::constants::{
|
||||||
MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST,
|
MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST,
|
||||||
SCRIPT_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_TEST,
|
SCRIPT_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_TEST,
|
||||||
};
|
};
|
||||||
|
use crate::blockdata::opcodes::all::*;
|
||||||
use crate::blockdata::script::Instruction;
|
use crate::blockdata::script::Instruction;
|
||||||
use crate::blockdata::{opcodes, script};
|
use crate::blockdata::{opcodes, script};
|
||||||
use crate::blockdata::opcodes::all::*;
|
|
||||||
use crate::error::ParseIntError;
|
use crate::error::ParseIntError;
|
||||||
use crate::hash_types::{PubkeyHash, ScriptHash};
|
use crate::hash_types::{PubkeyHash, ScriptHash};
|
||||||
use crate::hashes::{sha256, Hash, HashEngine};
|
use crate::hashes::{sha256, Hash, HashEngine};
|
||||||
|
@ -324,9 +324,7 @@ impl TryFrom<opcodes::All> for WitnessVersion {
|
||||||
fn try_from(opcode: opcodes::All) -> Result<Self, Self::Error> {
|
fn try_from(opcode: opcodes::All) -> Result<Self, Self::Error> {
|
||||||
match opcode.to_u8() {
|
match opcode.to_u8() {
|
||||||
0 => Ok(WitnessVersion::V0),
|
0 => Ok(WitnessVersion::V0),
|
||||||
version
|
version if version >= OP_PUSHNUM_1.to_u8() && version <= OP_PUSHNUM_16.to_u8() =>
|
||||||
if version >= OP_PUSHNUM_1.to_u8()
|
|
||||||
&& version <= OP_PUSHNUM_16.to_u8() =>
|
|
||||||
WitnessVersion::try_from(version - OP_PUSHNUM_1.to_u8() + 1),
|
WitnessVersion::try_from(version - OP_PUSHNUM_1.to_u8() + 1),
|
||||||
_ => Err(Error::MalformedWitnessVersion),
|
_ => Err(Error::MalformedWitnessVersion),
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
//! We refer to the documentation on the types for more information.
|
//! We refer to the documentation on the types for more information.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::prelude::*;
|
use core::cmp::Ordering;
|
||||||
|
|
||||||
use core::{ops, default, str::FromStr, cmp::Ordering};
|
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
|
use core::str::FromStr;
|
||||||
|
use core::{default, ops};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// A set of denominations in which amounts can be expressed.
|
/// A set of denominations in which amounts can be expressed.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
@ -63,9 +65,7 @@ impl Denomination {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Denomination {
|
impl fmt::Display for Denomination {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.as_str()) }
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Denomination {
|
impl FromStr for Denomination {
|
||||||
|
@ -79,18 +79,17 @@ impl FromStr for Denomination {
|
||||||
///
|
///
|
||||||
/// Due to ambiguity between mega and milli, pico and peta we prohibit usage of leading capital 'M', 'P'.
|
/// Due to ambiguity between mega and milli, pico and peta we prohibit usage of leading capital 'M', 'P'.
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
use self::ParseAmountError::*;
|
|
||||||
use self::Denomination as D;
|
use self::Denomination as D;
|
||||||
|
use self::ParseAmountError::*;
|
||||||
|
|
||||||
let starts_with_uppercase = || s.starts_with(char::is_uppercase);
|
let starts_with_uppercase = || s.starts_with(char::is_uppercase);
|
||||||
match denomination_from_str(s) {
|
match denomination_from_str(s) {
|
||||||
None => Err(UnknownDenomination(s.to_owned())),
|
None => Err(UnknownDenomination(s.to_owned())),
|
||||||
Some(D::MilliBitcoin) | Some(D::PicoBitcoin) | Some(D::MilliSatoshi) if starts_with_uppercase() => {
|
Some(D::MilliBitcoin) | Some(D::PicoBitcoin) | Some(D::MilliSatoshi)
|
||||||
Err(PossiblyConfusingDenomination(s.to_owned()))
|
if starts_with_uppercase() =>
|
||||||
}
|
Err(PossiblyConfusingDenomination(s.to_owned())),
|
||||||
Some(D::NanoBitcoin) | Some(D::MicroBitcoin) if starts_with_uppercase() => {
|
Some(D::NanoBitcoin) | Some(D::MicroBitcoin) if starts_with_uppercase() =>
|
||||||
Err(UnknownDenomination(s.to_owned()))
|
Err(UnknownDenomination(s.to_owned())),
|
||||||
}
|
|
||||||
Some(d) => Ok(d),
|
Some(d) => Ok(d),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +156,7 @@ pub enum ParseAmountError {
|
||||||
/// The denomination was unknown.
|
/// The denomination was unknown.
|
||||||
UnknownDenomination(String),
|
UnknownDenomination(String),
|
||||||
/// The denomination has multiple possible interpretations.
|
/// The denomination has multiple possible interpretations.
|
||||||
PossiblyConfusingDenomination(String)
|
PossiblyConfusingDenomination(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParseAmountError {
|
impl fmt::Display for ParseAmountError {
|
||||||
|
@ -169,7 +168,8 @@ impl fmt::Display for ParseAmountError {
|
||||||
ParseAmountError::InvalidFormat => f.write_str("invalid number format"),
|
ParseAmountError::InvalidFormat => f.write_str("invalid number format"),
|
||||||
ParseAmountError::InputTooLarge => f.write_str("input string was too large"),
|
ParseAmountError::InputTooLarge => f.write_str("input string was too large"),
|
||||||
ParseAmountError::InvalidCharacter(c) => write!(f, "invalid character in input: {}", c),
|
ParseAmountError::InvalidCharacter(c) => write!(f, "invalid character in input: {}", c),
|
||||||
ParseAmountError::UnknownDenomination(ref d) => write!(f, "unknown denomination: {}", d),
|
ParseAmountError::UnknownDenomination(ref d) =>
|
||||||
|
write!(f, "unknown denomination: {}", d),
|
||||||
ParseAmountError::PossiblyConfusingDenomination(ref d) => {
|
ParseAmountError::PossiblyConfusingDenomination(ref d) => {
|
||||||
let (letter, upper, lower) = match d.chars().next() {
|
let (letter, upper, lower) = match d.chars().next() {
|
||||||
Some('M') => ('M', "Mega", "milli"),
|
Some('M') => ('M', "Mega", "milli"),
|
||||||
|
@ -197,7 +197,7 @@ impl std::error::Error for ParseAmountError {
|
||||||
| InputTooLarge
|
| InputTooLarge
|
||||||
| InvalidCharacter(_)
|
| InvalidCharacter(_)
|
||||||
| UnknownDenomination(_)
|
| UnknownDenomination(_)
|
||||||
| PossiblyConfusingDenomination(_) => None
|
| PossiblyConfusingDenomination(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,9 +337,7 @@ fn dec_width(mut num: u64) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NIH due to MSRV, impl copied from `core::i8::unsigned_abs` (introduced in Rust 1.51.1).
|
// NIH due to MSRV, impl copied from `core::i8::unsigned_abs` (introduced in Rust 1.51.1).
|
||||||
fn unsigned_abs(x: i8) -> u8 {
|
fn unsigned_abs(x: i8) -> u8 { x.wrapping_abs() as u8 }
|
||||||
x.wrapping_abs() as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
|
fn repeat_char(f: &mut dyn fmt::Write, c: char, count: usize) -> fmt::Result {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
|
@ -372,7 +370,7 @@ fn fmt_satoshi_in(
|
||||||
exp = precision as usize;
|
exp = precision as usize;
|
||||||
}
|
}
|
||||||
trailing_decimal_zeros = options.precision.unwrap_or(0);
|
trailing_decimal_zeros = options.precision.unwrap_or(0);
|
||||||
},
|
}
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let precision = unsigned_abs(precision);
|
let precision = unsigned_abs(precision);
|
||||||
let divisor = 10u64.pow(precision.into());
|
let divisor = 10u64.pow(precision.into());
|
||||||
|
@ -391,7 +389,7 @@ fn fmt_satoshi_in(
|
||||||
// compute requested precision
|
// compute requested precision
|
||||||
let opt_precision = options.precision.unwrap_or(0);
|
let opt_precision = options.precision.unwrap_or(0);
|
||||||
trailing_decimal_zeros = opt_precision.saturating_sub(norm_nb_decimals);
|
trailing_decimal_zeros = opt_precision.saturating_sub(norm_nb_decimals);
|
||||||
},
|
}
|
||||||
Ordering::Equal => trailing_decimal_zeros = options.precision.unwrap_or(0),
|
Ordering::Equal => trailing_decimal_zeros = options.precision.unwrap_or(0),
|
||||||
}
|
}
|
||||||
let total_decimals = norm_nb_decimals + trailing_decimal_zeros;
|
let total_decimals = norm_nb_decimals + trailing_decimal_zeros;
|
||||||
|
@ -420,7 +418,8 @@ fn fmt_satoshi_in(
|
||||||
(true, true, _) | (true, false, fmt::Alignment::Right) => (width - num_width, 0),
|
(true, true, _) | (true, false, fmt::Alignment::Right) => (width - num_width, 0),
|
||||||
(true, false, fmt::Alignment::Left) => (0, width - num_width),
|
(true, false, fmt::Alignment::Left) => (0, width - num_width),
|
||||||
// If the required padding is odd it needs to be skewed to the left
|
// If the required padding is odd it needs to be skewed to the left
|
||||||
(true, false, fmt::Alignment::Center) => ((width - num_width) / 2, (width - num_width + 1) / 2),
|
(true, false, fmt::Alignment::Center) =>
|
||||||
|
((width - num_width) / 2, (width - num_width + 1) / 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !options.sign_aware_zero_pad {
|
if !options.sign_aware_zero_pad {
|
||||||
|
@ -488,24 +487,16 @@ impl Amount {
|
||||||
pub const MAX_MONEY: Amount = Amount(21_000_000 * 100_000_000);
|
pub const MAX_MONEY: Amount = Amount(21_000_000 * 100_000_000);
|
||||||
|
|
||||||
/// Create an [Amount] with satoshi precision and the given number of satoshis.
|
/// Create an [Amount] with satoshi precision and the given number of satoshis.
|
||||||
pub const fn from_sat(satoshi: u64) -> Amount {
|
pub const fn from_sat(satoshi: u64) -> Amount { Amount(satoshi) }
|
||||||
Amount(satoshi)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the number of satoshis in this [`Amount`].
|
/// Gets the number of satoshis in this [`Amount`].
|
||||||
pub fn to_sat(self) -> u64 {
|
pub fn to_sat(self) -> u64 { self.0 }
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The maximum value of an [Amount].
|
/// The maximum value of an [Amount].
|
||||||
pub fn max_value() -> Amount {
|
pub fn max_value() -> Amount { Amount(u64::max_value()) }
|
||||||
Amount(u64::max_value())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The minimum value of an [Amount].
|
/// The minimum value of an [Amount].
|
||||||
pub fn min_value() -> Amount {
|
pub fn min_value() -> Amount { Amount(u64::min_value()) }
|
||||||
Amount(u64::min_value())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert from a value expressing bitcoins to an [Amount].
|
/// Convert from a value expressing bitcoins to an [Amount].
|
||||||
pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
|
pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
|
||||||
|
@ -559,9 +550,7 @@ impl Amount {
|
||||||
/// let amount = Amount::from_sat(100_000);
|
/// let amount = Amount::from_sat(100_000);
|
||||||
/// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin))
|
/// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin))
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to_btc(self) -> f64 {
|
pub fn to_btc(self) -> f64 { self.to_float_in(Denomination::Bitcoin) }
|
||||||
self.to_float_in(Denomination::Bitcoin)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert this [Amount] in floating-point notation with a given
|
/// Convert this [Amount] in floating-point notation with a given
|
||||||
/// denomination.
|
/// denomination.
|
||||||
|
@ -582,7 +571,7 @@ impl Amount {
|
||||||
Display {
|
Display {
|
||||||
sats_abs: self.to_sat(),
|
sats_abs: self.to_sat(),
|
||||||
is_negative: false,
|
is_negative: false,
|
||||||
style: DisplayStyle::FixedDenomination { denomination, show_denomination: false, },
|
style: DisplayStyle::FixedDenomination { denomination, show_denomination: false },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,23 +629,17 @@ impl Amount {
|
||||||
|
|
||||||
/// Checked multiplication.
|
/// Checked multiplication.
|
||||||
/// Returns [None] if overflow occurred.
|
/// Returns [None] if overflow occurred.
|
||||||
pub fn checked_mul(self, rhs: u64) -> Option<Amount> {
|
pub fn checked_mul(self, rhs: u64) -> Option<Amount> { self.0.checked_mul(rhs).map(Amount) }
|
||||||
self.0.checked_mul(rhs).map(Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checked integer division.
|
/// Checked integer division.
|
||||||
/// Be aware that integer division loses the remainder if no exact division
|
/// Be aware that integer division loses the remainder if no exact division
|
||||||
/// can be made.
|
/// can be made.
|
||||||
/// Returns [None] if overflow occurred.
|
/// Returns [None] if overflow occurred.
|
||||||
pub fn checked_div(self, rhs: u64) -> Option<Amount> {
|
pub fn checked_div(self, rhs: u64) -> Option<Amount> { self.0.checked_div(rhs).map(Amount) }
|
||||||
self.0.checked_div(rhs).map(Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checked remainder.
|
/// Checked remainder.
|
||||||
/// Returns [None] if overflow occurred.
|
/// Returns [None] if overflow occurred.
|
||||||
pub fn checked_rem(self, rhs: u64) -> Option<Amount> {
|
pub fn checked_rem(self, rhs: u64) -> Option<Amount> { self.0.checked_rem(rhs).map(Amount) }
|
||||||
self.0.checked_rem(rhs).map(Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to a signed amount.
|
/// Convert to a signed amount.
|
||||||
pub fn to_signed(self) -> Result<SignedAmount, ParseAmountError> {
|
pub fn to_signed(self) -> Result<SignedAmount, ParseAmountError> {
|
||||||
|
@ -669,9 +652,7 @@ impl Amount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl default::Default for Amount {
|
impl default::Default for Amount {
|
||||||
fn default() -> Self {
|
fn default() -> Self { Amount::ZERO }
|
||||||
Amount::ZERO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Amount {
|
impl fmt::Debug for Amount {
|
||||||
|
@ -698,9 +679,7 @@ impl ops::Add for Amount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::AddAssign for Amount {
|
impl ops::AddAssign for Amount {
|
||||||
fn add_assign(&mut self, other: Amount) {
|
fn add_assign(&mut self, other: Amount) { *self = *self + other }
|
||||||
*self = *self + other
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Sub for Amount {
|
impl ops::Sub for Amount {
|
||||||
|
@ -712,9 +691,7 @@ impl ops::Sub for Amount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::SubAssign for Amount {
|
impl ops::SubAssign for Amount {
|
||||||
fn sub_assign(&mut self, other: Amount) {
|
fn sub_assign(&mut self, other: Amount) { *self = *self - other }
|
||||||
*self = *self - other
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Rem<u64> for Amount {
|
impl ops::Rem<u64> for Amount {
|
||||||
|
@ -726,9 +703,7 @@ impl ops::Rem<u64> for Amount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::RemAssign<u64> for Amount {
|
impl ops::RemAssign<u64> for Amount {
|
||||||
fn rem_assign(&mut self, modulus: u64) {
|
fn rem_assign(&mut self, modulus: u64) { *self = *self % modulus }
|
||||||
*self = *self % modulus
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Mul<u64> for Amount {
|
impl ops::Mul<u64> for Amount {
|
||||||
|
@ -740,31 +715,23 @@ impl ops::Mul<u64> for Amount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::MulAssign<u64> for Amount {
|
impl ops::MulAssign<u64> for Amount {
|
||||||
fn mul_assign(&mut self, rhs: u64) {
|
fn mul_assign(&mut self, rhs: u64) { *self = *self * rhs }
|
||||||
*self = *self * rhs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Div<u64> for Amount {
|
impl ops::Div<u64> for Amount {
|
||||||
type Output = Amount;
|
type Output = Amount;
|
||||||
|
|
||||||
fn div(self, rhs: u64) -> Self::Output {
|
fn div(self, rhs: u64) -> Self::Output { self.checked_div(rhs).expect("Amount division error") }
|
||||||
self.checked_div(rhs).expect("Amount division error")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::DivAssign<u64> for Amount {
|
impl ops::DivAssign<u64> for Amount {
|
||||||
fn div_assign(&mut self, rhs: u64) {
|
fn div_assign(&mut self, rhs: u64) { *self = *self / rhs }
|
||||||
*self = *self / rhs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Amount {
|
impl FromStr for Amount {
|
||||||
type Err = ParseAmountError;
|
type Err = ParseAmountError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> { Amount::from_str_with_denomination(s) }
|
||||||
Amount::from_str_with_denomination(s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::iter::Sum for Amount {
|
impl core::iter::Sum for Amount {
|
||||||
|
@ -829,7 +796,7 @@ impl fmt::Display for Display {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum DisplayStyle {
|
enum DisplayStyle {
|
||||||
FixedDenomination { denomination: Denomination, show_denomination: bool, },
|
FixedDenomination { denomination: Denomination, show_denomination: bool },
|
||||||
DynamicDenomination,
|
DynamicDenomination,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -861,24 +828,16 @@ impl SignedAmount {
|
||||||
pub const MAX_MONEY: SignedAmount = SignedAmount(21_000_000 * 100_000_000);
|
pub const MAX_MONEY: SignedAmount = SignedAmount(21_000_000 * 100_000_000);
|
||||||
|
|
||||||
/// Create an [SignedAmount] with satoshi precision and the given number of satoshis.
|
/// Create an [SignedAmount] with satoshi precision and the given number of satoshis.
|
||||||
pub const fn from_sat(satoshi: i64) -> SignedAmount {
|
pub const fn from_sat(satoshi: i64) -> SignedAmount { SignedAmount(satoshi) }
|
||||||
SignedAmount(satoshi)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the number of satoshis in this [`SignedAmount`].
|
/// Gets the number of satoshis in this [`SignedAmount`].
|
||||||
pub fn to_sat(self) -> i64 {
|
pub fn to_sat(self) -> i64 { self.0 }
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The maximum value of an [SignedAmount].
|
/// The maximum value of an [SignedAmount].
|
||||||
pub fn max_value() -> SignedAmount {
|
pub fn max_value() -> SignedAmount { SignedAmount(i64::max_value()) }
|
||||||
SignedAmount(i64::max_value())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The minimum value of an [SignedAmount].
|
/// The minimum value of an [SignedAmount].
|
||||||
pub fn min_value() -> SignedAmount {
|
pub fn min_value() -> SignedAmount { SignedAmount(i64::min_value()) }
|
||||||
SignedAmount(i64::min_value())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert from a value expressing bitcoins to an [SignedAmount].
|
/// Convert from a value expressing bitcoins to an [SignedAmount].
|
||||||
pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
|
pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
|
||||||
|
@ -927,9 +886,7 @@ impl SignedAmount {
|
||||||
/// Equivalent to `to_float_in(Denomination::Bitcoin)`.
|
/// Equivalent to `to_float_in(Denomination::Bitcoin)`.
|
||||||
///
|
///
|
||||||
/// Please be aware of the risk of using floating-point numbers.
|
/// Please be aware of the risk of using floating-point numbers.
|
||||||
pub fn to_btc(self) -> f64 {
|
pub fn to_btc(self) -> f64 { self.to_float_in(Denomination::Bitcoin) }
|
||||||
self.to_float_in(Denomination::Bitcoin)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert this [SignedAmount] in floating-point notation with a given
|
/// Convert this [SignedAmount] in floating-point notation with a given
|
||||||
/// denomination.
|
/// denomination.
|
||||||
|
@ -948,16 +905,14 @@ impl SignedAmount {
|
||||||
/// Returns the absolute value as satoshis.
|
/// Returns the absolute value as satoshis.
|
||||||
///
|
///
|
||||||
/// This is the implementation of `unsigned_abs()` copied from `core` to support older MSRV.
|
/// This is the implementation of `unsigned_abs()` copied from `core` to support older MSRV.
|
||||||
fn to_sat_abs(self) -> u64 {
|
fn to_sat_abs(self) -> u64 { self.to_sat().wrapping_abs() as u64 }
|
||||||
self.to_sat().wrapping_abs() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an object that implements [`fmt::Display`] using specified denomination.
|
/// Create an object that implements [`fmt::Display`] using specified denomination.
|
||||||
pub fn display_in(self, denomination: Denomination) -> Display {
|
pub fn display_in(self, denomination: Denomination) -> Display {
|
||||||
Display {
|
Display {
|
||||||
sats_abs: self.to_sat_abs(),
|
sats_abs: self.to_sat_abs(),
|
||||||
is_negative: self.is_negative(),
|
is_negative: self.is_negative(),
|
||||||
style: DisplayStyle::FixedDenomination { denomination, show_denomination: false, },
|
style: DisplayStyle::FixedDenomination { denomination, show_denomination: false },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1002,37 +957,26 @@ impl SignedAmount {
|
||||||
// Some arithmetic that doesn't fit in `core::ops` traits.
|
// Some arithmetic that doesn't fit in `core::ops` traits.
|
||||||
|
|
||||||
/// Get the absolute value of this [SignedAmount].
|
/// Get the absolute value of this [SignedAmount].
|
||||||
pub fn abs(self) -> SignedAmount {
|
pub fn abs(self) -> SignedAmount { SignedAmount(self.0.abs()) }
|
||||||
SignedAmount(self.0.abs())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a number representing sign of this [SignedAmount].
|
/// Returns a number representing sign of this [SignedAmount].
|
||||||
///
|
///
|
||||||
/// - `0` if the amount is zero
|
/// - `0` if the amount is zero
|
||||||
/// - `1` if the amount is positive
|
/// - `1` if the amount is positive
|
||||||
/// - `-1` if the amount is negative
|
/// - `-1` if the amount is negative
|
||||||
pub fn signum(self) -> i64 {
|
pub fn signum(self) -> i64 { self.0.signum() }
|
||||||
self.0.signum()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this [SignedAmount] is positive and `false` if
|
/// Returns `true` if this [SignedAmount] is positive and `false` if
|
||||||
/// this [SignedAmount] is zero or negative.
|
/// this [SignedAmount] is zero or negative.
|
||||||
pub fn is_positive(self) -> bool {
|
pub fn is_positive(self) -> bool { self.0.is_positive() }
|
||||||
self.0.is_positive()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this [SignedAmount] is negative and `false` if
|
/// Returns `true` if this [SignedAmount] is negative and `false` if
|
||||||
/// this [SignedAmount] is zero or positive.
|
/// this [SignedAmount] is zero or positive.
|
||||||
pub fn is_negative(self) -> bool {
|
pub fn is_negative(self) -> bool { self.0.is_negative() }
|
||||||
self.0.is_negative()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Get the absolute value of this [SignedAmount].
|
/// Get the absolute value of this [SignedAmount].
|
||||||
/// Returns [None] if overflow occurred. (`self == min_value()`)
|
/// Returns [None] if overflow occurred. (`self == min_value()`)
|
||||||
pub fn checked_abs(self) -> Option<SignedAmount> {
|
pub fn checked_abs(self) -> Option<SignedAmount> { self.0.checked_abs().map(SignedAmount) }
|
||||||
self.0.checked_abs().map(SignedAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checked addition.
|
/// Checked addition.
|
||||||
/// Returns [None] if overflow occurred.
|
/// Returns [None] if overflow occurred.
|
||||||
|
@ -1087,9 +1031,7 @@ impl SignedAmount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl default::Default for SignedAmount {
|
impl default::Default for SignedAmount {
|
||||||
fn default() -> Self {
|
fn default() -> Self { SignedAmount::ZERO }
|
||||||
SignedAmount::ZERO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for SignedAmount {
|
impl fmt::Debug for SignedAmount {
|
||||||
|
@ -1116,9 +1058,7 @@ impl ops::Add for SignedAmount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::AddAssign for SignedAmount {
|
impl ops::AddAssign for SignedAmount {
|
||||||
fn add_assign(&mut self, other: SignedAmount) {
|
fn add_assign(&mut self, other: SignedAmount) { *self = *self + other }
|
||||||
*self = *self + other
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Sub for SignedAmount {
|
impl ops::Sub for SignedAmount {
|
||||||
|
@ -1130,9 +1070,7 @@ impl ops::Sub for SignedAmount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::SubAssign for SignedAmount {
|
impl ops::SubAssign for SignedAmount {
|
||||||
fn sub_assign(&mut self, other: SignedAmount) {
|
fn sub_assign(&mut self, other: SignedAmount) { *self = *self - other }
|
||||||
*self = *self - other
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Rem<i64> for SignedAmount {
|
impl ops::Rem<i64> for SignedAmount {
|
||||||
|
@ -1144,9 +1082,7 @@ impl ops::Rem<i64> for SignedAmount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::RemAssign<i64> for SignedAmount {
|
impl ops::RemAssign<i64> for SignedAmount {
|
||||||
fn rem_assign(&mut self, modulus: i64) {
|
fn rem_assign(&mut self, modulus: i64) { *self = *self % modulus }
|
||||||
*self = *self % modulus
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Mul<i64> for SignedAmount {
|
impl ops::Mul<i64> for SignedAmount {
|
||||||
|
@ -1158,9 +1094,7 @@ impl ops::Mul<i64> for SignedAmount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::MulAssign<i64> for SignedAmount {
|
impl ops::MulAssign<i64> for SignedAmount {
|
||||||
fn mul_assign(&mut self, rhs: i64) {
|
fn mul_assign(&mut self, rhs: i64) { *self = *self * rhs }
|
||||||
*self = *self * rhs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Div<i64> for SignedAmount {
|
impl ops::Div<i64> for SignedAmount {
|
||||||
|
@ -1172,17 +1106,13 @@ impl ops::Div<i64> for SignedAmount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::DivAssign<i64> for SignedAmount {
|
impl ops::DivAssign<i64> for SignedAmount {
|
||||||
fn div_assign(&mut self, rhs: i64) {
|
fn div_assign(&mut self, rhs: i64) { *self = *self / rhs }
|
||||||
*self = *self / rhs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for SignedAmount {
|
impl FromStr for SignedAmount {
|
||||||
type Err = ParseAmountError;
|
type Err = ParseAmountError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> { SignedAmount::from_str_with_denomination(s) }
|
||||||
SignedAmount::from_str_with_denomination(s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::iter::Sum for SignedAmount {
|
impl core::iter::Sum for SignedAmount {
|
||||||
|
@ -1199,18 +1129,21 @@ pub trait CheckedSum<R>: private::SumSeal<R> {
|
||||||
fn checked_sum(self) -> Option<R>;
|
fn checked_sum(self) -> Option<R>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> CheckedSum<Amount> for T where T: Iterator<Item=Amount> {
|
impl<T> CheckedSum<Amount> for T
|
||||||
|
where
|
||||||
|
T: Iterator<Item = Amount>,
|
||||||
|
{
|
||||||
fn checked_sum(mut self) -> Option<Amount> {
|
fn checked_sum(mut self) -> Option<Amount> {
|
||||||
let first = Some(self.next().unwrap_or_default());
|
let first = Some(self.next().unwrap_or_default());
|
||||||
|
|
||||||
self.fold(
|
self.fold(first, |acc, item| acc.and_then(|acc| acc.checked_add(item)))
|
||||||
first,
|
|
||||||
|acc, item| acc.and_then(|acc| acc.checked_add(item))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> CheckedSum<SignedAmount> for T where T: Iterator<Item=SignedAmount> {
|
impl<T> CheckedSum<SignedAmount> for T
|
||||||
|
where
|
||||||
|
T: Iterator<Item = SignedAmount>,
|
||||||
|
{
|
||||||
fn checked_sum(mut self) -> Option<SignedAmount> {
|
fn checked_sum(mut self) -> Option<SignedAmount> {
|
||||||
let first = Some(self.next().unwrap_or_default());
|
let first = Some(self.next().unwrap_or_default());
|
||||||
|
|
||||||
|
@ -1252,6 +1185,7 @@ pub mod serde {
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::amount::{Amount, Denomination, SignedAmount};
|
use crate::amount::{Amount, Denomination, 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
|
||||||
|
@ -1295,9 +1229,7 @@ pub mod serde {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerdeAmountForOpt for Amount {
|
impl SerdeAmountForOpt for Amount {
|
||||||
fn type_prefix() -> &'static str {
|
fn type_prefix() -> &'static str { "u" }
|
||||||
"u"
|
|
||||||
}
|
|
||||||
fn ser_sat_opt<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
|
fn ser_sat_opt<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
s.serialize_some(&self.to_sat())
|
s.serialize_some(&self.to_sat())
|
||||||
}
|
}
|
||||||
|
@ -1323,9 +1255,7 @@ pub mod serde {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerdeAmountForOpt for SignedAmount {
|
impl SerdeAmountForOpt for SignedAmount {
|
||||||
fn type_prefix() -> &'static str {
|
fn type_prefix() -> &'static str { "i" }
|
||||||
"i"
|
|
||||||
}
|
|
||||||
fn ser_sat_opt<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
|
fn ser_sat_opt<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
s.serialize_some(&self.to_sat())
|
s.serialize_some(&self.to_sat())
|
||||||
}
|
}
|
||||||
|
@ -1339,6 +1269,7 @@ pub mod serde {
|
||||||
//! Use with `#[serde(with = "amount::serde::as_sat")]`.
|
//! Use with `#[serde(with = "amount::serde::as_sat")]`.
|
||||||
|
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
|
|
||||||
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> {
|
||||||
|
@ -1353,11 +1284,13 @@ 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 serde::{Deserializer, Serializer, de};
|
|
||||||
use crate::amount::serde::SerdeAmountForOpt;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use serde::{de, Deserializer, Serializer};
|
||||||
|
|
||||||
|
use crate::amount::serde::SerdeAmountForOpt;
|
||||||
|
|
||||||
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
|
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
|
||||||
a: &Option<A>,
|
a: &Option<A>,
|
||||||
s: S,
|
s: S,
|
||||||
|
@ -1403,6 +1336,7 @@ pub mod serde {
|
||||||
//! Use with `#[serde(with = "amount::serde::as_btc")]`.
|
//! Use with `#[serde(with = "amount::serde::as_btc")]`.
|
||||||
|
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
|
|
||||||
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> {
|
||||||
|
@ -1417,11 +1351,13 @@ 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 serde::{Deserializer, Serializer, de};
|
|
||||||
use crate::amount::serde::SerdeAmountForOpt;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use serde::{de, Deserializer, Serializer};
|
||||||
|
|
||||||
|
use crate::amount::serde::SerdeAmountForOpt;
|
||||||
|
|
||||||
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
|
pub fn serialize<A: SerdeAmountForOpt, S: Serializer>(
|
||||||
a: &Option<A>,
|
a: &Option<A>,
|
||||||
s: S,
|
s: S,
|
||||||
|
@ -1465,14 +1401,15 @@ pub mod serde {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use core::str::FromStr;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use core::str::FromStr;
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde_test;
|
use serde_test;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_sub_mul_div() {
|
fn add_sub_mul_div() {
|
||||||
let sat = Amount::from_sat;
|
let sat = Amount::from_sat;
|
||||||
|
@ -1613,13 +1550,22 @@ mod tests {
|
||||||
// make sure satoshi > i64::max_value() is checked.
|
// make sure satoshi > i64::max_value() is checked.
|
||||||
let amount = Amount::from_sat(i64::max_value() as u64);
|
let amount = Amount::from_sat(i64::max_value() 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_eq!(Amount::from_str_in(&(amount+Amount(1)).to_string_in(sat), sat), Err(E::TooBig));
|
assert_eq!(
|
||||||
|
Amount::from_str_in(&(amount + Amount(1)).to_string_in(sat), sat),
|
||||||
|
Err(E::TooBig)
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(p("12.000", Denomination::MilliSatoshi), Err(E::TooPrecise));
|
assert_eq!(p("12.000", Denomination::MilliSatoshi), Err(E::TooPrecise));
|
||||||
// exactly 50 chars.
|
// exactly 50 chars.
|
||||||
assert_eq!(p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin), Err(E::TooBig));
|
assert_eq!(
|
||||||
|
p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin),
|
||||||
|
Err(E::TooBig)
|
||||||
|
);
|
||||||
// more than 50 chars.
|
// more than 50 chars.
|
||||||
assert_eq!(p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin), Err(E::InputTooLarge));
|
assert_eq!(
|
||||||
|
p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin),
|
||||||
|
Err(E::InputTooLarge)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1845,7 +1791,10 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(sa(0).to_unsigned().unwrap().to_signed(), Ok(sa(0)));
|
assert_eq!(sa(0).to_unsigned().unwrap().to_signed(), Ok(sa(0)));
|
||||||
assert_eq!(sa(1).to_unsigned().unwrap().to_signed(), Ok(sa(1)));
|
assert_eq!(sa(1).to_unsigned().unwrap().to_signed(), Ok(sa(1)));
|
||||||
assert_eq!(sa(i64::max_value()).to_unsigned().unwrap().to_signed(), Ok(sa(i64::max_value())));
|
assert_eq!(
|
||||||
|
sa(i64::max_value()).to_unsigned().unwrap().to_signed(),
|
||||||
|
Ok(sa(i64::max_value()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1907,34 +1856,73 @@ mod tests {
|
||||||
assert_eq!("2535830000", Amount::from_sat(253583).to_string_in(D::PicoBitcoin));
|
assert_eq!("2535830000", Amount::from_sat(253583).to_string_in(D::PicoBitcoin));
|
||||||
assert_eq!("-100000000", SignedAmount::from_sat(-10_000).to_string_in(D::PicoBitcoin));
|
assert_eq!("-100000000", SignedAmount::from_sat(-10_000).to_string_in(D::PicoBitcoin));
|
||||||
|
|
||||||
|
|
||||||
assert_eq!("0.50", format!("{:.2}", Amount::from_sat(50).display_in(D::Bit)));
|
assert_eq!("0.50", format!("{:.2}", Amount::from_sat(50).display_in(D::Bit)));
|
||||||
assert_eq!("-0.50", format!("{:.2}", SignedAmount::from_sat(-50).display_in(D::Bit)));
|
assert_eq!("-0.50", format!("{:.2}", SignedAmount::from_sat(-50).display_in(D::Bit)));
|
||||||
assert_eq!("0.10000000", format!("{:.8}", Amount::from_sat(100_000_00).display_in(D::Bitcoin)));
|
assert_eq!(
|
||||||
|
"0.10000000",
|
||||||
|
format!("{:.8}", Amount::from_sat(100_000_00).display_in(D::Bitcoin))
|
||||||
|
);
|
||||||
assert_eq!("-100.00", format!("{:.2}", SignedAmount::from_sat(-10_000).display_in(D::Bit)));
|
assert_eq!("-100.00", format!("{:.2}", SignedAmount::from_sat(-10_000).display_in(D::Bit)));
|
||||||
|
|
||||||
assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
|
assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
|
||||||
assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500)));
|
assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500)));
|
||||||
assert_eq!(ua_str(&ua_sat(21_000_000).to_string_in(D::Bit), D::Bit), Ok(ua_sat(21_000_000)));
|
assert_eq!(
|
||||||
assert_eq!(ua_str(&ua_sat(1).to_string_in(D::MicroBitcoin), D::MicroBitcoin), Ok(ua_sat(1)));
|
ua_str(&ua_sat(21_000_000).to_string_in(D::Bit), D::Bit),
|
||||||
assert_eq!(ua_str(&ua_sat(1_000_000_000_000).to_string_in(D::MilliBitcoin), D::MilliBitcoin), Ok(ua_sat(1_000_000_000_000)));
|
Ok(ua_sat(21_000_000))
|
||||||
assert_eq!(ua_str(&ua_sat(u64::max_value()).to_string_in(D::MilliBitcoin), D::MilliBitcoin), Err(ParseAmountError::TooBig));
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ua_str(&ua_sat(1).to_string_in(D::MicroBitcoin), D::MicroBitcoin),
|
||||||
|
Ok(ua_sat(1))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ua_str(&ua_sat(1_000_000_000_000).to_string_in(D::MilliBitcoin), D::MilliBitcoin),
|
||||||
|
Ok(ua_sat(1_000_000_000_000))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ua_str(&ua_sat(u64::max_value()).to_string_in(D::MilliBitcoin), D::MilliBitcoin),
|
||||||
|
Err(ParseAmountError::TooBig)
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::MicroBitcoin), D::MicroBitcoin), Ok(sa_sat(-1)));
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(-1).to_string_in(D::MicroBitcoin), D::MicroBitcoin),
|
||||||
|
Ok(sa_sat(-1))
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::MicroBitcoin),
|
||||||
|
Err(ParseAmountError::TooBig)
|
||||||
|
);
|
||||||
// Test an overflow bug in `abs()`
|
// Test an overflow bug in `abs()`
|
||||||
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::MicroBitcoin),
|
||||||
assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::NanoBitcoin), D::NanoBitcoin), Ok(sa_sat(-1)));
|
Err(ParseAmountError::TooBig)
|
||||||
assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::NanoBitcoin), Err(ParseAmountError::TooPrecise));
|
);
|
||||||
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::NanoBitcoin), Err(ParseAmountError::TooPrecise));
|
|
||||||
|
|
||||||
assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::PicoBitcoin), D::PicoBitcoin), Ok(sa_sat(-1)));
|
|
||||||
assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::PicoBitcoin), Err(ParseAmountError::TooPrecise));
|
|
||||||
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::PicoBitcoin), Err(ParseAmountError::TooPrecise));
|
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(-1).to_string_in(D::NanoBitcoin), D::NanoBitcoin),
|
||||||
|
Ok(sa_sat(-1))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::NanoBitcoin),
|
||||||
|
Err(ParseAmountError::TooPrecise)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::NanoBitcoin),
|
||||||
|
Err(ParseAmountError::TooPrecise)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(-1).to_string_in(D::PicoBitcoin), D::PicoBitcoin),
|
||||||
|
Ok(sa_sat(-1))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::PicoBitcoin),
|
||||||
|
Err(ParseAmountError::TooPrecise)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::PicoBitcoin),
|
||||||
|
Err(ParseAmountError::TooPrecise)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1958,7 +1946,6 @@ mod tests {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
#[test]
|
#[test]
|
||||||
fn serde_as_sat() {
|
fn serde_as_sat() {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
#[serde(crate = "actual_serde")]
|
#[serde(crate = "actual_serde")]
|
||||||
struct T {
|
struct T {
|
||||||
|
@ -1969,10 +1956,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
serde_test::assert_tokens(
|
serde_test::assert_tokens(
|
||||||
&T {
|
&T { amt: Amount::from_sat(123456789), samt: SignedAmount::from_sat(-123456789) },
|
||||||
amt: Amount::from_sat(123456789),
|
|
||||||
samt: SignedAmount::from_sat(-123456789),
|
|
||||||
},
|
|
||||||
&[
|
&[
|
||||||
serde_test::Token::Struct { name: "T", len: 2 },
|
serde_test::Token::Struct { name: "T", len: 2 },
|
||||||
serde_test::Token::Str("amt"),
|
serde_test::Token::Str("amt"),
|
||||||
|
@ -2039,10 +2023,7 @@ mod tests {
|
||||||
amt: Some(Amount::from_sat(2_500_000_00)),
|
amt: Some(Amount::from_sat(2_500_000_00)),
|
||||||
samt: Some(SignedAmount::from_sat(-2_500_000_00)),
|
samt: Some(SignedAmount::from_sat(-2_500_000_00)),
|
||||||
};
|
};
|
||||||
let without = T {
|
let without = T { amt: None, samt: None };
|
||||||
amt: None,
|
|
||||||
samt: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test Roundtripping
|
// Test Roundtripping
|
||||||
for s in [&with, &without].iter() {
|
for s in [&with, &without].iter() {
|
||||||
|
@ -2084,10 +2065,7 @@ mod tests {
|
||||||
amt: Some(Amount::from_sat(2_500_000_00)),
|
amt: Some(Amount::from_sat(2_500_000_00)),
|
||||||
samt: Some(SignedAmount::from_sat(-2_500_000_00)),
|
samt: Some(SignedAmount::from_sat(-2_500_000_00)),
|
||||||
};
|
};
|
||||||
let without = T {
|
let without = T { amt: None, samt: None };
|
||||||
amt: None,
|
|
||||||
samt: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test Roundtripping
|
// Test Roundtripping
|
||||||
for s in [&with, &without].iter() {
|
for s in [&with, &without].iter() {
|
||||||
|
@ -2115,18 +2093,14 @@ mod tests {
|
||||||
assert_eq!(Amount::from_sat(0), vec![].into_iter().sum::<Amount>());
|
assert_eq!(Amount::from_sat(0), vec![].into_iter().sum::<Amount>());
|
||||||
assert_eq!(SignedAmount::from_sat(0), vec![].into_iter().sum::<SignedAmount>());
|
assert_eq!(SignedAmount::from_sat(0), vec![].into_iter().sum::<SignedAmount>());
|
||||||
|
|
||||||
let amounts = vec![
|
let amounts = vec![Amount::from_sat(42), Amount::from_sat(1337), Amount::from_sat(21)];
|
||||||
Amount::from_sat(42),
|
|
||||||
Amount::from_sat(1337),
|
|
||||||
Amount::from_sat(21)
|
|
||||||
];
|
|
||||||
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 = vec![
|
let amounts = vec![
|
||||||
SignedAmount::from_sat(-42),
|
SignedAmount::from_sat(-42),
|
||||||
SignedAmount::from_sat(1337),
|
SignedAmount::from_sat(1337),
|
||||||
SignedAmount::from_sat(21)
|
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);
|
||||||
|
@ -2137,26 +2111,19 @@ mod tests {
|
||||||
assert_eq!(Some(Amount::from_sat(0)), vec![].into_iter().checked_sum());
|
assert_eq!(Some(Amount::from_sat(0)), vec![].into_iter().checked_sum());
|
||||||
assert_eq!(Some(SignedAmount::from_sat(0)), vec![].into_iter().checked_sum());
|
assert_eq!(Some(SignedAmount::from_sat(0)), vec![].into_iter().checked_sum());
|
||||||
|
|
||||||
let amounts = vec![
|
let amounts = vec![Amount::from_sat(42), Amount::from_sat(1337), Amount::from_sat(21)];
|
||||||
Amount::from_sat(42),
|
|
||||||
Amount::from_sat(1337),
|
|
||||||
Amount::from_sat(21)
|
|
||||||
];
|
|
||||||
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 = vec![
|
let amounts =
|
||||||
Amount::from_sat(u64::max_value()),
|
vec![Amount::from_sat(u64::max_value()), Amount::from_sat(1337), Amount::from_sat(21)];
|
||||||
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);
|
||||||
|
|
||||||
let amounts = vec![
|
let amounts = vec![
|
||||||
SignedAmount::from_sat(i64::min_value()),
|
SignedAmount::from_sat(i64::min_value()),
|
||||||
SignedAmount::from_sat(-1),
|
SignedAmount::from_sat(-1),
|
||||||
SignedAmount::from_sat(21)
|
SignedAmount::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);
|
||||||
|
@ -2164,7 +2131,7 @@ mod tests {
|
||||||
let amounts = vec![
|
let amounts = vec![
|
||||||
SignedAmount::from_sat(i64::max_value()),
|
SignedAmount::from_sat(i64::max_value()),
|
||||||
SignedAmount::from_sat(1),
|
SignedAmount::from_sat(1),
|
||||||
SignedAmount::from_sat(21)
|
SignedAmount::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);
|
||||||
|
@ -2172,7 +2139,7 @@ mod tests {
|
||||||
let amounts = vec![
|
let amounts = vec![
|
||||||
SignedAmount::from_sat(42),
|
SignedAmount::from_sat(42),
|
||||||
SignedAmount::from_sat(3301),
|
SignedAmount::from_sat(3301),
|
||||||
SignedAmount::from_sat(21)
|
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);
|
||||||
|
@ -2181,7 +2148,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn denomination_string_acceptable_forms() {
|
fn denomination_string_acceptable_forms() {
|
||||||
// Non-exhaustive list of valid forms.
|
// Non-exhaustive list of valid forms.
|
||||||
let valid = vec!["BTC", "btc", "mBTC", "mbtc", "uBTC", "ubtc", "SATOSHI","Satoshi", "Satoshis", "satoshis", "SAT", "Sat", "sats", "bit", "bits", "nBTC", "pBTC"];
|
let valid = vec![
|
||||||
|
"BTC", "btc", "mBTC", "mbtc", "uBTC", "ubtc", "SATOSHI", "Satoshi", "Satoshis",
|
||||||
|
"satoshis", "SAT", "Sat", "sats", "bit", "bits", "nBTC", "pBTC",
|
||||||
|
];
|
||||||
for denom in valid.iter() {
|
for denom in valid.iter() {
|
||||||
assert!(Denomination::from_str(denom).is_ok());
|
assert!(Denomination::from_str(denom).is_ok());
|
||||||
}
|
}
|
||||||
|
@ -2190,11 +2160,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn disallow_confusing_forms() {
|
fn disallow_confusing_forms() {
|
||||||
// Non-exhaustive list of confusing forms.
|
// Non-exhaustive list of confusing forms.
|
||||||
let confusing = vec!["Msat", "Msats", "MSAT", "MSATS", "MSat", "MSats", "MBTC", "Mbtc", "PBTC"];
|
let confusing =
|
||||||
|
vec!["Msat", "Msats", "MSAT", "MSATS", "MSat", "MSats", "MBTC", "Mbtc", "PBTC"];
|
||||||
for denom in confusing.iter() {
|
for denom in confusing.iter() {
|
||||||
match Denomination::from_str(denom) {
|
match Denomination::from_str(denom) {
|
||||||
Ok(_) => panic!("from_str should error for {}", denom),
|
Ok(_) => panic!("from_str should error for {}", denom),
|
||||||
Err(ParseAmountError::PossiblyConfusingDenomination(_)) => {},
|
Err(ParseAmountError::PossiblyConfusingDenomination(_)) => {}
|
||||||
Err(e) => panic!("unexpected error: {}", e),
|
Err(e) => panic!("unexpected error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2207,7 +2178,7 @@ mod tests {
|
||||||
for denom in unknown.iter() {
|
for denom in unknown.iter() {
|
||||||
match Denomination::from_str(denom) {
|
match Denomination::from_str(denom) {
|
||||||
Ok(_) => panic!("from_str should error for {}", denom),
|
Ok(_) => panic!("from_str should error for {}", denom),
|
||||||
Err(ParseAmountError::UnknownDenomination(_)) => {},
|
Err(ParseAmountError::UnknownDenomination(_)) => {}
|
||||||
Err(e) => panic!("unexpected error: {}", e),
|
Err(e) => panic!("unexpected error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::consensus::encode::{self, Decodable, Encodable, VarInt};
|
||||||
use crate::hashes::{sha256, siphash24, Hash};
|
use crate::hashes::{sha256, siphash24, Hash};
|
||||||
use crate::internal_macros::{impl_bytes_newtype, impl_consensus_encoding};
|
use crate::internal_macros::{impl_bytes_newtype, impl_consensus_encoding};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{io, block, Block, BlockHash, Transaction};
|
use crate::{block, io, Block, BlockHash, Transaction};
|
||||||
|
|
||||||
/// A BIP-152 error
|
/// A BIP-152 error
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Copy, PartialOrd, Ord, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Copy, PartialOrd, Ord, Hash)]
|
||||||
|
@ -111,7 +111,10 @@ impl ShortId {
|
||||||
|
|
||||||
// 2. Running SipHash-2-4 with the input being the transaction ID and the keys (k0/k1)
|
// 2. Running SipHash-2-4 with the input being the transaction ID and the keys (k0/k1)
|
||||||
// set to the first two little-endian 64-bit integers from the above hash, respectively.
|
// set to the first two little-endian 64-bit integers from the above hash, respectively.
|
||||||
(u64::from_le_bytes(h[0..8].try_into().expect("8 byte slice")), u64::from_le_bytes(h[8..16].try_into().expect("8 byte slice")))
|
(
|
||||||
|
u64::from_le_bytes(h[0..8].try_into().expect("8 byte slice")),
|
||||||
|
u64::from_le_bytes(h[8..16].try_into().expect("8 byte slice")),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the short ID with the given (w)txid and using the provided SipHash keys.
|
/// Calculate the short ID with the given (w)txid and using the provided SipHash keys.
|
||||||
|
@ -374,8 +377,8 @@ mod test {
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::{
|
use crate::{
|
||||||
CompactTarget, OutPoint, Script, Sequence,
|
CompactTarget, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode, TxOut, Txid,
|
||||||
Transaction, TxIn, TxMerkleNode, TxOut, Txid, Witness,
|
Witness,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
||||||
|
|
|
@ -114,6 +114,7 @@ use std::io;
|
||||||
use core2::io;
|
use core2::io;
|
||||||
|
|
||||||
pub use crate::address::{Address, AddressType};
|
pub use crate::address::{Address, AddressType};
|
||||||
|
pub use crate::amount::{Amount, Denomination, SignedAmount};
|
||||||
pub use crate::blockdata::block::{self, Block};
|
pub use crate::blockdata::block::{self, Block};
|
||||||
pub use crate::blockdata::locktime::{self, absolute, relative};
|
pub use crate::blockdata::locktime::{self, absolute, relative};
|
||||||
pub use crate::blockdata::script::{self, Script};
|
pub use crate::blockdata::script::{self, Script};
|
||||||
|
@ -123,12 +124,11 @@ pub use crate::blockdata::{constants, opcodes};
|
||||||
pub use crate::consensus::encode::VarInt;
|
pub use crate::consensus::encode::VarInt;
|
||||||
pub use crate::error::Error;
|
pub use crate::error::Error;
|
||||||
pub use crate::hash_types::*;
|
pub use crate::hash_types::*;
|
||||||
|
pub use crate::merkle_tree::MerkleBlock;
|
||||||
pub use crate::network::constants::Network;
|
pub use crate::network::constants::Network;
|
||||||
pub use crate::pow::{CompactTarget, Target, Work};
|
pub use crate::pow::{CompactTarget, Target, Work};
|
||||||
pub use crate::amount::{Amount, Denomination, SignedAmount};
|
|
||||||
pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
|
pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
|
||||||
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};
|
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};
|
||||||
pub use crate::merkle_tree::MerkleBlock;
|
|
||||||
pub use crate::util::schnorr::{self, SchnorrSig, SchnorrSigError};
|
pub use crate::util::schnorr::{self, SchnorrSig, SchnorrSigError};
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
|
|
|
@ -8,18 +8,18 @@
|
||||||
//! and legacy (before Bip143).
|
//! and legacy (before Bip143).
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use core::{str, fmt};
|
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
|
use core::{fmt, str};
|
||||||
|
|
||||||
use crate::{io, Script, Transaction, TxIn, TxOut, Sequence, Sighash};
|
|
||||||
use crate::blockdata::transaction::EncodeSigningDataResult;
|
use crate::blockdata::transaction::EncodeSigningDataResult;
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
use crate::consensus::{encode, Encodable};
|
use crate::consensus::{encode, Encodable};
|
||||||
use crate::error::impl_std_error;
|
use crate::error::impl_std_error;
|
||||||
use crate::hashes::{sha256, sha256d, Hash};
|
use crate::hashes::{sha256, sha256d, Hash};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::util::taproot::{TapLeafHash, TAPROOT_ANNEX_PREFIX, TapSighashHash, LeafVersion};
|
use crate::util::taproot::{LeafVersion, TapLeafHash, TapSighashHash, TAPROOT_ANNEX_PREFIX};
|
||||||
|
use crate::{io, Script, Sequence, Sighash, Transaction, TxIn, TxOut};
|
||||||
|
|
||||||
/// Used for signature hash for invalid use of SIGHASH_SINGLE.
|
/// Used for signature hash for invalid use of SIGHASH_SINGLE.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -77,7 +77,10 @@ struct TaprootCache {
|
||||||
/// Contains outputs of previous transactions. In the case [`SchnorrSighashType`] variant is
|
/// Contains outputs of previous transactions. In the case [`SchnorrSighashType`] variant is
|
||||||
/// `SIGHASH_ANYONECANPAY`, [`Prevouts::One`] may be used.
|
/// `SIGHASH_ANYONECANPAY`, [`Prevouts::One`] may be used.
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum Prevouts<'u, T> where T: 'u + Borrow<TxOut> {
|
pub enum Prevouts<'u, T>
|
||||||
|
where
|
||||||
|
T: 'u + Borrow<TxOut>,
|
||||||
|
{
|
||||||
/// `One` variant allows provision of the single prevout needed. It's useful, for example, when
|
/// `One` variant allows provision of the single prevout needed. It's useful, for example, when
|
||||||
/// modifier `SIGHASH_ANYONECANPAY` is provided, only prevout of the current input is needed.
|
/// modifier `SIGHASH_ANYONECANPAY` is provided, only prevout of the current input is needed.
|
||||||
/// The first `usize` argument is the input index this [`TxOut`] is referring to.
|
/// The first `usize` argument is the input index this [`TxOut`] is referring to.
|
||||||
|
@ -233,7 +236,10 @@ impl std::error::Error for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'u, T> Prevouts<'u, T> where T: Borrow<TxOut> {
|
impl<'u, T> Prevouts<'u, T>
|
||||||
|
where
|
||||||
|
T: Borrow<TxOut>,
|
||||||
|
{
|
||||||
fn check_all(&self, tx: &Transaction) -> Result<(), Error> {
|
fn check_all(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
if let Prevouts::All(prevouts) = self {
|
if let Prevouts::All(prevouts) = self {
|
||||||
if prevouts.len() != tx.input.len() {
|
if prevouts.len() != tx.input.len() {
|
||||||
|
@ -252,17 +258,14 @@ impl<'u, T> Prevouts<'u, T> where T: Borrow<TxOut> {
|
||||||
|
|
||||||
fn get(&self, input_index: usize) -> Result<&TxOut, Error> {
|
fn get(&self, input_index: usize) -> Result<&TxOut, Error> {
|
||||||
match self {
|
match self {
|
||||||
Prevouts::One(index, prevout) => {
|
Prevouts::One(index, prevout) =>
|
||||||
if input_index == *index {
|
if input_index == *index {
|
||||||
Ok(prevout.borrow())
|
Ok(prevout.borrow())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::PrevoutIndex)
|
Err(Error::PrevoutIndex)
|
||||||
}
|
},
|
||||||
}
|
Prevouts::All(prevouts) =>
|
||||||
Prevouts::All(prevouts) => prevouts
|
prevouts.get(input_index).map(|x| x.borrow()).ok_or(Error::PrevoutIndex),
|
||||||
.get(input_index)
|
|
||||||
.map(|x| x.borrow())
|
|
||||||
.ok_or(Error::PrevoutIndex),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,20 +273,18 @@ impl<'u, T> Prevouts<'u, T> where T: Borrow<TxOut> {
|
||||||
impl<'s> ScriptPath<'s> {
|
impl<'s> ScriptPath<'s> {
|
||||||
/// Creates a new `ScriptPath` structure.
|
/// Creates a new `ScriptPath` structure.
|
||||||
pub fn new(script: &'s Script, leaf_version: LeafVersion) -> Self {
|
pub fn new(script: &'s Script, leaf_version: LeafVersion) -> Self {
|
||||||
ScriptPath {
|
ScriptPath { script, leaf_version }
|
||||||
script,
|
|
||||||
leaf_version,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Creates a new `ScriptPath` structure using default leaf version value.
|
/// Creates a new `ScriptPath` structure using default leaf version value.
|
||||||
pub fn with_defaults(script: &'s Script) -> Self {
|
pub fn with_defaults(script: &'s Script) -> Self { Self::new(script, LeafVersion::TapScript) }
|
||||||
Self::new(script, LeafVersion::TapScript)
|
|
||||||
}
|
|
||||||
/// Computes the leaf hash for this `ScriptPath`.
|
/// Computes the leaf hash for this `ScriptPath`.
|
||||||
pub fn leaf_hash(&self) -> TapLeafHash {
|
pub fn leaf_hash(&self) -> TapLeafHash {
|
||||||
let mut enc = TapLeafHash::engine();
|
let mut enc = TapLeafHash::engine();
|
||||||
|
|
||||||
self.leaf_version.to_consensus().consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
|
self.leaf_version
|
||||||
|
.to_consensus()
|
||||||
|
.consensus_encode(&mut enc)
|
||||||
|
.expect("Writing to hash enging should never fail");
|
||||||
self.script.consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
|
self.script.consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
|
||||||
|
|
||||||
TapLeafHash::from_engine(enc)
|
TapLeafHash::from_engine(enc)
|
||||||
|
@ -291,9 +292,7 @@ impl<'s> ScriptPath<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> From<ScriptPath<'s>> for TapLeafHash {
|
impl<'s> From<ScriptPath<'s>> for TapLeafHash {
|
||||||
fn from(script_path: ScriptPath<'s>) -> TapLeafHash {
|
fn from(script_path: ScriptPath<'s>) -> TapLeafHash { script_path.leaf_hash() }
|
||||||
script_path.leaf_hash()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hashtype of an input's signature, encoded in the last byte of the signature.
|
/// Hashtype of an input's signature, encoded in the last byte of the signature.
|
||||||
|
@ -316,7 +315,7 @@ pub enum EcdsaSighashType {
|
||||||
/// 0x82: Sign no outputs and only this input.
|
/// 0x82: Sign no outputs and only this input.
|
||||||
NonePlusAnyoneCanPay = 0x82,
|
NonePlusAnyoneCanPay = 0x82,
|
||||||
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means).
|
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means).
|
||||||
SinglePlusAnyoneCanPay = 0x83
|
SinglePlusAnyoneCanPay = 0x83,
|
||||||
}
|
}
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
crate::serde_utils::serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data");
|
crate::serde_utils::serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data");
|
||||||
|
@ -360,7 +359,7 @@ impl EcdsaSighashType {
|
||||||
EcdsaSighashType::Single => (EcdsaSighashType::Single, false),
|
EcdsaSighashType::Single => (EcdsaSighashType::Single, false),
|
||||||
EcdsaSighashType::AllPlusAnyoneCanPay => (EcdsaSighashType::All, true),
|
EcdsaSighashType::AllPlusAnyoneCanPay => (EcdsaSighashType::All, true),
|
||||||
EcdsaSighashType::NonePlusAnyoneCanPay => (EcdsaSighashType::None, true),
|
EcdsaSighashType::NonePlusAnyoneCanPay => (EcdsaSighashType::None, true),
|
||||||
EcdsaSighashType::SinglePlusAnyoneCanPay => (EcdsaSighashType::Single, true)
|
EcdsaSighashType::SinglePlusAnyoneCanPay => (EcdsaSighashType::Single, true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +388,7 @@ impl EcdsaSighashType {
|
||||||
0x83 => EcdsaSighashType::SinglePlusAnyoneCanPay,
|
0x83 => EcdsaSighashType::SinglePlusAnyoneCanPay,
|
||||||
// catchalls
|
// catchalls
|
||||||
x if x & 0x80 == 0x80 => EcdsaSighashType::AllPlusAnyoneCanPay,
|
x if x & 0x80 == 0x80 => EcdsaSighashType::AllPlusAnyoneCanPay,
|
||||||
_ => EcdsaSighashType::All
|
_ => EcdsaSighashType::All,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +406,7 @@ impl EcdsaSighashType {
|
||||||
0x81 => Ok(EcdsaSighashType::AllPlusAnyoneCanPay),
|
0x81 => Ok(EcdsaSighashType::AllPlusAnyoneCanPay),
|
||||||
0x82 => Ok(EcdsaSighashType::NonePlusAnyoneCanPay),
|
0x82 => Ok(EcdsaSighashType::NonePlusAnyoneCanPay),
|
||||||
0x83 => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay),
|
0x83 => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay),
|
||||||
non_standard => Err(NonStandardSighashType(non_standard))
|
non_standard => Err(NonStandardSighashType(non_standard)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,12 +497,7 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
/// sighashes to be valid, no fields in the transaction may change except for script_sig and
|
/// sighashes to be valid, no fields in the transaction may change except for script_sig and
|
||||||
/// witness.
|
/// witness.
|
||||||
pub fn new(tx: R) -> Self {
|
pub fn new(tx: R) -> Self {
|
||||||
SighashCache {
|
SighashCache { tx, common_cache: None, taproot_cache: None, segwit_cache: None }
|
||||||
tx,
|
|
||||||
common_cache: None,
|
|
||||||
taproot_cache: None,
|
|
||||||
segwit_cache: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encodes the BIP341 signing data for any flag type into a given object implementing a
|
/// Encodes the BIP341 signing data for any flag type into a given object implementing a
|
||||||
|
@ -542,15 +536,9 @@ impl<R: Deref<Target = 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(&mut writer)?;
|
self.common_cache().prevouts.consensus_encode(&mut writer)?;
|
||||||
self.taproot_cache(prevouts.get_all()?)
|
self.taproot_cache(prevouts.get_all()?).amounts.consensus_encode(&mut writer)?;
|
||||||
.amounts
|
self.taproot_cache(prevouts.get_all()?).script_pubkeys.consensus_encode(&mut writer)?;
|
||||||
.consensus_encode(&mut writer)?;
|
self.common_cache().sequences.consensus_encode(&mut writer)?;
|
||||||
self.taproot_cache(prevouts.get_all()?)
|
|
||||||
.script_pubkeys
|
|
||||||
.consensus_encode(&mut writer)?;
|
|
||||||
self.common_cache()
|
|
||||||
.sequences
|
|
||||||
.consensus_encode(&mut writer)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If hash_type & 3 does not equal SIGHASH_NONE or SIGHASH_SINGLE:
|
// If hash_type & 3 does not equal SIGHASH_NONE or SIGHASH_SINGLE:
|
||||||
|
@ -577,21 +565,14 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
// scriptPubKey (35): scriptPubKey of the previous output spent by this input, serialized as script inside CTxOut. Its size is always 35 bytes.
|
// scriptPubKey (35): scriptPubKey of the previous output spent by this input, serialized as script inside CTxOut. Its size is always 35 bytes.
|
||||||
// nSequence (4): nSequence of this input.
|
// nSequence (4): nSequence of this input.
|
||||||
if anyone_can_pay {
|
if anyone_can_pay {
|
||||||
let txin =
|
let txin = &self.tx.input.get(input_index).ok_or(Error::IndexOutOfInputsBounds {
|
||||||
&self
|
|
||||||
.tx
|
|
||||||
.input
|
|
||||||
.get(input_index)
|
|
||||||
.ok_or(Error::IndexOutOfInputsBounds {
|
|
||||||
index: input_index,
|
index: input_index,
|
||||||
inputs_size: self.tx.input.len(),
|
inputs_size: self.tx.input.len(),
|
||||||
})?;
|
})?;
|
||||||
let previous_output = prevouts.get(input_index)?;
|
let previous_output = prevouts.get(input_index)?;
|
||||||
txin.previous_output.consensus_encode(&mut writer)?;
|
txin.previous_output.consensus_encode(&mut writer)?;
|
||||||
previous_output.value.consensus_encode(&mut writer)?;
|
previous_output.value.consensus_encode(&mut writer)?;
|
||||||
previous_output
|
previous_output.script_pubkey.consensus_encode(&mut writer)?;
|
||||||
.script_pubkey
|
|
||||||
.consensus_encode(&mut writer)?;
|
|
||||||
txin.sequence.consensus_encode(&mut writer)?;
|
txin.sequence.consensus_encode(&mut writer)?;
|
||||||
} else {
|
} else {
|
||||||
(input_index as u32).consensus_encode(&mut writer)?;
|
(input_index as u32).consensus_encode(&mut writer)?;
|
||||||
|
@ -726,20 +707,13 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
&& sighash != EcdsaSighashType::Single
|
&& sighash != EcdsaSighashType::Single
|
||||||
&& sighash != EcdsaSighashType::None
|
&& sighash != EcdsaSighashType::None
|
||||||
{
|
{
|
||||||
self.segwit_cache()
|
self.segwit_cache().sequences.consensus_encode(&mut writer)?;
|
||||||
.sequences
|
|
||||||
.consensus_encode(&mut writer)?;
|
|
||||||
} else {
|
} else {
|
||||||
zero_hash.consensus_encode(&mut writer)?;
|
zero_hash.consensus_encode(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let txin =
|
let txin = &self.tx.input.get(input_index).ok_or(Error::IndexOutOfInputsBounds {
|
||||||
&self
|
|
||||||
.tx
|
|
||||||
.input
|
|
||||||
.get(input_index)
|
|
||||||
.ok_or(Error::IndexOutOfInputsBounds {
|
|
||||||
index: input_index,
|
index: input_index,
|
||||||
inputs_size: self.tx.input.len(),
|
inputs_size: self.tx.input.len(),
|
||||||
})?;
|
})?;
|
||||||
|
@ -836,7 +810,8 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
script_pubkey: &Script,
|
script_pubkey: &Script,
|
||||||
sighash_type: u32,
|
sighash_type: u32,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
let (sighash, anyone_can_pay) = EcdsaSighashType::from_consensus(sighash_type).split_anyonecanpay_flag();
|
let (sighash, anyone_can_pay) =
|
||||||
|
EcdsaSighashType::from_consensus(sighash_type).split_anyonecanpay_flag();
|
||||||
|
|
||||||
// Build tx to sign
|
// Build tx to sign
|
||||||
let mut tx = Transaction {
|
let mut tx = Transaction {
|
||||||
|
@ -858,8 +833,19 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
for (n, input) in self_.input.iter().enumerate() {
|
for (n, input) in self_.input.iter().enumerate() {
|
||||||
tx.input.push(TxIn {
|
tx.input.push(TxIn {
|
||||||
previous_output: input.previous_output,
|
previous_output: input.previous_output,
|
||||||
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
|
script_sig: if n == input_index {
|
||||||
sequence: if n != input_index && (sighash == EcdsaSighashType::Single || sighash == EcdsaSighashType::None) { Sequence::ZERO } else { input.sequence },
|
script_pubkey.clone()
|
||||||
|
} else {
|
||||||
|
Script::new()
|
||||||
|
},
|
||||||
|
sequence: if n != input_index
|
||||||
|
&& (sighash == EcdsaSighashType::Single
|
||||||
|
|| sighash == EcdsaSighashType::None)
|
||||||
|
{
|
||||||
|
Sequence::ZERO
|
||||||
|
} else {
|
||||||
|
input.sequence
|
||||||
|
},
|
||||||
witness: Witness::default(),
|
witness: Witness::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -868,14 +854,24 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
tx.output = match sighash {
|
tx.output = match sighash {
|
||||||
EcdsaSighashType::All => self_.output.clone(),
|
EcdsaSighashType::All => self_.output.clone(),
|
||||||
EcdsaSighashType::Single => {
|
EcdsaSighashType::Single => {
|
||||||
let output_iter = self_.output.iter()
|
let output_iter = self_
|
||||||
|
.output
|
||||||
|
.iter()
|
||||||
.take(input_index + 1) // sign all outputs up to and including this one, but erase
|
.take(input_index + 1) // sign all outputs up to and including this one, but erase
|
||||||
.enumerate() // all of them except for this one
|
.enumerate() // all of them except for this one
|
||||||
.map(|(n, out)| if n == input_index { out.clone() } else { TxOut::default() });
|
.map(
|
||||||
|
|(n, out)| {
|
||||||
|
if n == input_index {
|
||||||
|
out.clone()
|
||||||
|
} else {
|
||||||
|
TxOut::default()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
output_iter.collect()
|
output_iter.collect()
|
||||||
}
|
}
|
||||||
EcdsaSighashType::None => vec![],
|
EcdsaSighashType::None => vec![],
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
// hash the result
|
// hash the result
|
||||||
tx.consensus_encode(&mut writer)?;
|
tx.consensus_encode(&mut writer)?;
|
||||||
|
@ -889,8 +885,9 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
writer,
|
writer,
|
||||||
input_index,
|
input_index,
|
||||||
script_pubkey,
|
script_pubkey,
|
||||||
sighash_type
|
sighash_type,
|
||||||
).map_err(|e| Error::Io(e.kind()))
|
)
|
||||||
|
.map_err(|e| Error::Io(e.kind())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,8 +917,10 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
sighash_type: u32,
|
sighash_type: u32,
|
||||||
) -> Result<Sighash, Error> {
|
) -> Result<Sighash, Error> {
|
||||||
let mut enc = Sighash::engine();
|
let mut enc = Sighash::engine();
|
||||||
if self.legacy_encode_signing_data_to(&mut enc, input_index, script_pubkey, sighash_type)
|
if self
|
||||||
.is_sighash_single_bug()? {
|
.legacy_encode_signing_data_to(&mut enc, input_index, script_pubkey, sighash_type)
|
||||||
|
.is_sighash_single_bug()?
|
||||||
|
{
|
||||||
Ok(Sighash::from_inner(UINT256_ONE))
|
Ok(Sighash::from_inner(UINT256_ONE))
|
||||||
} else {
|
} else {
|
||||||
Ok(Sighash::from_engine(enc))
|
Ok(Sighash::from_engine(enc))
|
||||||
|
@ -941,9 +940,7 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
let mut enc_prevouts = sha256::Hash::engine();
|
let mut enc_prevouts = sha256::Hash::engine();
|
||||||
let mut enc_sequences = sha256::Hash::engine();
|
let mut enc_sequences = sha256::Hash::engine();
|
||||||
for txin in tx.input.iter() {
|
for txin in tx.input.iter() {
|
||||||
txin.previous_output
|
txin.previous_output.consensus_encode(&mut enc_prevouts).unwrap();
|
||||||
.consensus_encode(&mut enc_prevouts)
|
|
||||||
.unwrap();
|
|
||||||
txin.sequence.consensus_encode(&mut enc_sequences).unwrap();
|
txin.sequence.consensus_encode(&mut enc_sequences).unwrap();
|
||||||
}
|
}
|
||||||
CommonCache {
|
CommonCache {
|
||||||
|
@ -979,18 +976,13 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn taproot_cache<T: Borrow<TxOut>>(&mut self, prevouts: &[T]) -> &TaprootCache
|
fn taproot_cache<T: Borrow<TxOut>>(&mut self, prevouts: &[T]) -> &TaprootCache {
|
||||||
{
|
|
||||||
self.taproot_cache.get_or_insert_with(|| {
|
self.taproot_cache.get_or_insert_with(|| {
|
||||||
let mut enc_amounts = sha256::Hash::engine();
|
let mut enc_amounts = sha256::Hash::engine();
|
||||||
let mut enc_script_pubkeys = sha256::Hash::engine();
|
let mut enc_script_pubkeys = sha256::Hash::engine();
|
||||||
for prevout in prevouts {
|
for prevout in prevouts {
|
||||||
prevout.borrow().value.consensus_encode(&mut enc_amounts).unwrap();
|
prevout.borrow().value.consensus_encode(&mut enc_amounts).unwrap();
|
||||||
prevout
|
prevout.borrow().script_pubkey.consensus_encode(&mut enc_script_pubkeys).unwrap();
|
||||||
.borrow()
|
|
||||||
.script_pubkey
|
|
||||||
.consensus_encode(&mut enc_script_pubkeys)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
TaprootCache {
|
TaprootCache {
|
||||||
amounts: sha256::Hash::from_engine(enc_amounts),
|
amounts: sha256::Hash::from_engine(enc_amounts),
|
||||||
|
@ -1026,9 +1018,7 @@ impl<R: DerefMut<Target=Transaction>> SighashCache<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(e: io::Error) -> Self {
|
fn from(e: io::Error) -> Self { Error::Io(e.kind()) }
|
||||||
Error::Io(e.kind())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Annex` struct is a slice wrapper enforcing first byte is `0x50`.
|
/// The `Annex` struct is a slice wrapper enforcing first byte is `0x50`.
|
||||||
|
@ -1046,9 +1036,7 @@ impl<'a> Annex<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Annex bytes data (including first byte `0x50`).
|
/// Returns the Annex bytes data (including first byte `0x50`).
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] { self.0 }
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Encodable for Annex<'a> {
|
impl<'a> Encodable for Annex<'a> {
|
||||||
|
@ -1064,23 +1052,22 @@ fn is_invalid_use_of_sighash_single(sighash: u32, input_index: usize, output_len
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use secp256k1::{self, SecretKey, XOnlyPublicKey};
|
use secp256k1::{self, SecretKey, XOnlyPublicKey};
|
||||||
|
|
||||||
use crate::{Script, Transaction, TxIn, TxOut, Address};
|
use super::*;
|
||||||
use crate::blockdata::locktime::absolute;
|
use crate::blockdata::locktime::absolute;
|
||||||
use crate::consensus::deserialize;
|
use crate::consensus::deserialize;
|
||||||
|
use crate::hash_types::Sighash;
|
||||||
use crate::hashes::hex::{FromHex, ToHex};
|
use crate::hashes::hex::{FromHex, ToHex};
|
||||||
use crate::hashes::{Hash, HashEngine};
|
use crate::hashes::{Hash, HashEngine};
|
||||||
use crate::hash_types::Sighash;
|
use crate::internal_macros::{hex_decode, hex_from_slice, hex_into, hex_script};
|
||||||
use crate::internal_macros::{hex_into, hex_script, hex_decode, hex_from_slice};
|
|
||||||
use crate::network::constants::Network;
|
use crate::network::constants::Network;
|
||||||
use crate::util::key::PublicKey;
|
|
||||||
use crate::sighash::{Annex, Error, Prevouts, ScriptPath, SighashCache};
|
use crate::sighash::{Annex, Error, Prevouts, ScriptPath, SighashCache};
|
||||||
use crate::util::taproot::{TapTweakHash, TapSighashHash, TapBranchHash, TapLeafHash};
|
use crate::util::key::PublicKey;
|
||||||
|
use crate::util::taproot::{TapBranchHash, TapLeafHash, TapSighashHash, TapTweakHash};
|
||||||
|
use crate::{Address, Script, Transaction, TxIn, TxOut};
|
||||||
|
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
|
@ -1108,9 +1095,16 @@ mod tests {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
fn legacy_sighash() {
|
fn legacy_sighash() {
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::sighash::SighashCache;
|
use crate::sighash::SighashCache;
|
||||||
|
|
||||||
fn run_test_sighash(tx: &str, script: &str, input_index: usize, hash_type: i64, expected_result: &str) {
|
fn run_test_sighash(
|
||||||
|
tx: &str,
|
||||||
|
script: &str,
|
||||||
|
input_index: usize,
|
||||||
|
hash_type: i64,
|
||||||
|
expected_result: &str,
|
||||||
|
) {
|
||||||
let tx: Transaction = deserialize(&Vec::from_hex(tx).unwrap()[..]).unwrap();
|
let tx: Transaction = deserialize(&Vec::from_hex(tx).unwrap()[..]).unwrap();
|
||||||
let script = Script::from(Vec::from_hex(script).unwrap());
|
let script = Script::from(Vec::from_hex(script).unwrap());
|
||||||
let mut raw_expected = Vec::from_hex(expected_result).unwrap();
|
let mut raw_expected = Vec::from_hex(expected_result).unwrap();
|
||||||
|
@ -1362,9 +1356,7 @@ mod tests {
|
||||||
let script_inner = Script::from_hex(script_hex).unwrap();
|
let script_inner = Script::from_hex(script_hex).unwrap();
|
||||||
Some(ScriptPath::with_defaults(&script_inner).leaf_hash())
|
Some(ScriptPath::with_defaults(&script_inner).leaf_hash())
|
||||||
}
|
}
|
||||||
(_, Some(script_leaf_hash)) => {
|
(_, Some(script_leaf_hash)) => Some(TapLeafHash::from_hex(script_leaf_hash).unwrap()),
|
||||||
Some(TapLeafHash::from_hex(script_leaf_hash).unwrap())
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
// All our tests use the default `0xFFFFFFFF` codeseparator value
|
// All our tests use the default `0xFFFFFFFF` codeseparator value
|
||||||
|
@ -1389,13 +1381,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bip_341_sighash_tests() {
|
fn bip_341_sighash_tests() {
|
||||||
|
|
||||||
let data = bip_341_read_json();
|
let data = bip_341_read_json();
|
||||||
assert!(data["version"].as_u64().unwrap() == 1u64);
|
assert!(data["version"].as_u64().unwrap() == 1u64);
|
||||||
let secp = &secp256k1::Secp256k1::new();
|
let secp = &secp256k1::Secp256k1::new();
|
||||||
let key_path = &data["keyPathSpending"].as_array().unwrap()[0];
|
let key_path = &data["keyPathSpending"].as_array().unwrap()[0];
|
||||||
|
|
||||||
let raw_unsigned_tx = hex_decode!(Transaction, key_path["given"]["rawUnsignedTx"].as_str().unwrap());
|
let raw_unsigned_tx =
|
||||||
|
hex_decode!(Transaction, key_path["given"]["rawUnsignedTx"].as_str().unwrap());
|
||||||
let mut utxos = vec![];
|
let mut utxos = vec![];
|
||||||
for utxo in key_path["given"]["utxosSpent"].as_array().unwrap() {
|
for utxo in key_path["given"]["utxosSpent"].as_array().unwrap() {
|
||||||
let spk = hex_script!(utxo["scriptPubKey"].as_str().unwrap());
|
let spk = hex_script!(utxo["scriptPubKey"].as_str().unwrap());
|
||||||
|
@ -1420,24 +1412,41 @@ mod tests {
|
||||||
|
|
||||||
for inp in key_path["inputSpending"].as_array().unwrap() {
|
for inp in key_path["inputSpending"].as_array().unwrap() {
|
||||||
let tx_ind = inp["given"]["txinIndex"].as_u64().unwrap() as usize;
|
let tx_ind = inp["given"]["txinIndex"].as_u64().unwrap() as usize;
|
||||||
let internal_priv_key = hex_from_slice!(SecretKey, inp["given"]["internalPrivkey"].as_str().unwrap());
|
let internal_priv_key =
|
||||||
|
hex_from_slice!(SecretKey, inp["given"]["internalPrivkey"].as_str().unwrap());
|
||||||
let merkle_root = if inp["given"]["merkleRoot"].is_null() {
|
let merkle_root = if inp["given"]["merkleRoot"].is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(hex_into!(TapBranchHash, inp["given"]["merkleRoot"].as_str().unwrap()))
|
Some(hex_into!(TapBranchHash, inp["given"]["merkleRoot"].as_str().unwrap()))
|
||||||
};
|
};
|
||||||
let hash_ty = SchnorrSighashType::from_consensus_u8(inp["given"]["hashType"].as_u64().unwrap() as u8).unwrap();
|
let hash_ty = SchnorrSighashType::from_consensus_u8(
|
||||||
|
inp["given"]["hashType"].as_u64().unwrap() as u8,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let expected_internal_pk = hex_from_slice!(XOnlyPublicKey, inp["intermediary"]["internalPubkey"].as_str().unwrap());
|
let expected_internal_pk = hex_from_slice!(
|
||||||
let expected_tweak = hex_into!(TapTweakHash, inp["intermediary"]["tweak"].as_str().unwrap());
|
XOnlyPublicKey,
|
||||||
let expected_tweaked_priv_key = hex_from_slice!(SecretKey, inp["intermediary"]["tweakedPrivkey"].as_str().unwrap());
|
inp["intermediary"]["internalPubkey"].as_str().unwrap()
|
||||||
let expected_sig_msg = Vec::<u8>::from_hex(inp["intermediary"]["sigMsg"].as_str().unwrap()).unwrap();
|
);
|
||||||
let expected_sighash = hex_into!(TapSighashHash, inp["intermediary"]["sigHash"].as_str().unwrap());
|
let expected_tweak =
|
||||||
|
hex_into!(TapTweakHash, inp["intermediary"]["tweak"].as_str().unwrap());
|
||||||
|
let expected_tweaked_priv_key =
|
||||||
|
hex_from_slice!(SecretKey, inp["intermediary"]["tweakedPrivkey"].as_str().unwrap());
|
||||||
|
let expected_sig_msg =
|
||||||
|
Vec::<u8>::from_hex(inp["intermediary"]["sigMsg"].as_str().unwrap()).unwrap();
|
||||||
|
let expected_sighash =
|
||||||
|
hex_into!(TapSighashHash, inp["intermediary"]["sigHash"].as_str().unwrap());
|
||||||
let sig_str = inp["expected"]["witness"][0].as_str().unwrap();
|
let sig_str = inp["expected"]["witness"][0].as_str().unwrap();
|
||||||
let (expected_key_spend_sig, expected_hash_ty) = if sig_str.len() == 128 {
|
let (expected_key_spend_sig, expected_hash_ty) = if sig_str.len() == 128 {
|
||||||
(secp256k1::schnorr::Signature::from_str(sig_str).unwrap(), SchnorrSighashType::Default)
|
(
|
||||||
|
secp256k1::schnorr::Signature::from_str(sig_str).unwrap(),
|
||||||
|
SchnorrSighashType::Default,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let hash_ty = SchnorrSighashType::from_consensus_u8(Vec::<u8>::from_hex(&sig_str[128..]).unwrap()[0]).unwrap();
|
let hash_ty = SchnorrSighashType::from_consensus_u8(
|
||||||
|
Vec::<u8>::from_hex(&sig_str[128..]).unwrap()[0],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
(secp256k1::schnorr::Signature::from_str(&sig_str[..128]).unwrap(), hash_ty)
|
(secp256k1::schnorr::Signature::from_str(&sig_str[..128]).unwrap(), hash_ty)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1447,21 +1456,19 @@ mod tests {
|
||||||
let tweak = TapTweakHash::from_key_and_tweak(internal_key, merkle_root);
|
let tweak = TapTweakHash::from_key_and_tweak(internal_key, merkle_root);
|
||||||
let tweaked_keypair = keypair.add_xonly_tweak(secp, &tweak.to_scalar()).unwrap();
|
let tweaked_keypair = keypair.add_xonly_tweak(secp, &tweak.to_scalar()).unwrap();
|
||||||
let mut sig_msg = Vec::new();
|
let mut sig_msg = Vec::new();
|
||||||
cache.taproot_encode_signing_data_to(
|
cache
|
||||||
|
.taproot_encode_signing_data_to(
|
||||||
&mut sig_msg,
|
&mut sig_msg,
|
||||||
tx_ind,
|
tx_ind,
|
||||||
&Prevouts::All(&utxos),
|
&Prevouts::All(&utxos),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
hash_ty
|
hash_ty,
|
||||||
).unwrap();
|
)
|
||||||
let sighash = cache.taproot_signature_hash(
|
.unwrap();
|
||||||
tx_ind,
|
let sighash = cache
|
||||||
&Prevouts::All(&utxos),
|
.taproot_signature_hash(tx_ind, &Prevouts::All(&utxos), None, None, hash_ty)
|
||||||
None,
|
.unwrap();
|
||||||
None,
|
|
||||||
hash_ty
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let msg = secp256k1::Message::from(sighash);
|
let msg = secp256k1::Message::from(sighash);
|
||||||
let key_spend_sig = secp.sign_schnorr_with_aux_rand(&msg, &tweaked_keypair, &[0u8; 32]);
|
let key_spend_sig = secp.sign_schnorr_with_aux_rand(&msg, &tweaked_keypair, &[0u8; 32]);
|
||||||
|
@ -1513,7 +1520,10 @@ mod tests {
|
||||||
"SigHash_NONE",
|
"SigHash_NONE",
|
||||||
];
|
];
|
||||||
for s in sht_mistakes {
|
for s in sht_mistakes {
|
||||||
assert_eq!(SchnorrSighashType::from_str(s).unwrap_err().to_string(), format!("Unrecognized SIGHASH string '{}'", s));
|
assert_eq!(
|
||||||
|
SchnorrSighashType::from_str(s).unwrap_err().to_string(),
|
||||||
|
format!("Unrecognized SIGHASH string '{}'", s)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,25 +1543,32 @@ mod tests {
|
||||||
).unwrap()[..],
|
).unwrap()[..],
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let witness_script = p2pkh_hex("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357");
|
let witness_script =
|
||||||
|
p2pkh_hex("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357");
|
||||||
let value = 600_000_000;
|
let value = 600_000_000;
|
||||||
|
|
||||||
let mut cache = SighashCache::new(&tx);
|
let mut cache = SighashCache::new(&tx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cache.segwit_signature_hash(1, &witness_script, value, EcdsaSighashType::All).unwrap(),
|
cache.segwit_signature_hash(1, &witness_script, value, EcdsaSighashType::All).unwrap(),
|
||||||
hex_from_slice!(Sighash, "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670")
|
hex_from_slice!(
|
||||||
|
Sighash,
|
||||||
|
"c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let cache = cache.segwit_cache();
|
let cache = cache.segwit_cache();
|
||||||
assert_eq!(cache.prevouts, hex_from_slice!(
|
assert_eq!(
|
||||||
"96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
|
cache.prevouts,
|
||||||
));
|
hex_from_slice!("96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37")
|
||||||
assert_eq!(cache.sequences, hex_from_slice!(
|
);
|
||||||
"52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b"
|
assert_eq!(
|
||||||
));
|
cache.sequences,
|
||||||
assert_eq!(cache.outputs, hex_from_slice!(
|
hex_from_slice!("52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b")
|
||||||
"863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5"
|
);
|
||||||
));
|
assert_eq!(
|
||||||
|
cache.outputs,
|
||||||
|
hex_from_slice!("863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1564,25 +1581,32 @@ mod tests {
|
||||||
).unwrap()[..],
|
).unwrap()[..],
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let witness_script = p2pkh_hex("03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873");
|
let witness_script =
|
||||||
|
p2pkh_hex("03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873");
|
||||||
let value = 1_000_000_000;
|
let value = 1_000_000_000;
|
||||||
|
|
||||||
let mut cache = SighashCache::new(&tx);
|
let mut cache = SighashCache::new(&tx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(),
|
cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(),
|
||||||
hex_from_slice!(Sighash, "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6")
|
hex_from_slice!(
|
||||||
|
Sighash,
|
||||||
|
"64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let cache = cache.segwit_cache();
|
let cache = cache.segwit_cache();
|
||||||
assert_eq!(cache.prevouts, hex_from_slice!(
|
assert_eq!(
|
||||||
"b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
|
cache.prevouts,
|
||||||
));
|
hex_from_slice!("b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a")
|
||||||
assert_eq!(cache.sequences, hex_from_slice!(
|
);
|
||||||
"18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198"
|
assert_eq!(
|
||||||
));
|
cache.sequences,
|
||||||
assert_eq!(cache.outputs, hex_from_slice!(
|
hex_from_slice!("18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198")
|
||||||
"de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83"
|
);
|
||||||
));
|
assert_eq!(
|
||||||
|
cache.outputs,
|
||||||
|
hex_from_slice!("de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1607,18 +1631,24 @@ mod tests {
|
||||||
let mut cache = SighashCache::new(&tx);
|
let mut cache = SighashCache::new(&tx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(),
|
cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(),
|
||||||
hex_from_slice!(Sighash, "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c")
|
hex_from_slice!(
|
||||||
|
Sighash,
|
||||||
|
"185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let cache = cache.segwit_cache();
|
let cache = cache.segwit_cache();
|
||||||
assert_eq!(cache.prevouts, hex_from_slice!(
|
assert_eq!(
|
||||||
"74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
|
cache.prevouts,
|
||||||
));
|
hex_from_slice!("74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0")
|
||||||
assert_eq!(cache.sequences, hex_from_slice!(
|
);
|
||||||
"3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044"
|
assert_eq!(
|
||||||
));
|
cache.sequences,
|
||||||
assert_eq!(cache.outputs, hex_from_slice!(
|
hex_from_slice!("3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044")
|
||||||
"bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc"
|
);
|
||||||
));
|
assert_eq!(
|
||||||
|
cache.outputs,
|
||||||
|
hex_from_slice!("bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue