Merge rust-bitcoin/rust-bitcoin#1353: Enable formating of the `network` module
e04a7a926d
network: Run cargo fmt (Tobin C. Harding)dc33c7999f
Enable formatting of the network module (Tobin C. Harding)81f69e846b
Exclude from formatting stuff in the network module (Tobin C. Harding)408d7737fb
Run cargo fmt (Tobin C. Harding)1ecf09359b
Add local variable to reduce line length (Tobin C. Harding)b2e74bc050
Exclude long function call (Tobin C. Harding)ff5a80dbd3
Exclude formatting of function fmt_satoshi_in (Tobin C. Harding)308a12b7cf
Exclude array from formatting (Tobin C. Harding)fb7ff46ccc
Improve crate root re-exports (Tobin C. Harding) Pull request description: The `network` module is not currently included in formatting. Also, at this point in time not much is happening in the `network` module so formatting it should not cause too many merge conflicts with other in-progress PRs. The first 3 patches are formatting preparation of the repo, the next 2 are formatting preparation of the `network` module. The last patch is the result of running `cargo +nightly fmt`. Can one reviewer please verify the last patch consists only of `rustfmt` changes by running the command on their branch. Thanks cc luckysori :) ACKs for top commit: apoelstra: ACKe04a7a926d
Tree-SHA512: 49b2873f0bfdd448df97073b462fa860c85b7f3c3094fdb0e16fd86aa467156ea8b2ceec61e2ee99848802cd171fd8abbc17e45c66672889a37c413fa4bea636
This commit is contained in:
commit
99a28db99d
|
@ -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;
|
||||||
|
@ -413,13 +411,15 @@ fn fmt_satoshi_in(
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = options.width.unwrap_or(0);
|
let width = options.width.unwrap_or(0);
|
||||||
let (left_pad, pad_right) = match (num_width < width, options.sign_aware_zero_pad, options.align.unwrap_or(fmt::Alignment::Right)) {
|
let align = options.align.unwrap_or(fmt::Alignment::Right);
|
||||||
|
let (left_pad, pad_right) = match (num_width < width, options.sign_aware_zero_pad, align) {
|
||||||
(false, _, _) => (0, 0),
|
(false, _, _) => (0, 0),
|
||||||
// Alignment is always right (ignored) when zero-padding
|
// Alignment is always right (ignored) when zero-padding
|
||||||
(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 {
|
||||||
|
@ -487,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> {
|
||||||
|
@ -558,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.
|
||||||
|
@ -581,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 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,6 +590,7 @@ impl Amount {
|
||||||
/// Format the value of this [Amount] in the given denomination.
|
/// Format the value of this [Amount] in the given denomination.
|
||||||
///
|
///
|
||||||
/// Does not include the denomination.
|
/// Does not include the denomination.
|
||||||
|
#[rustfmt::skip]
|
||||||
pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
|
pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
|
||||||
fmt_satoshi_in(self.to_sat(), false, f, denom, false, FormatOptions::default())
|
fmt_satoshi_in(self.to_sat(), false, f, denom, false, FormatOptions::default())
|
||||||
}
|
}
|
||||||
|
@ -638,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> {
|
||||||
|
@ -667,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 {
|
||||||
|
@ -696,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 {
|
||||||
|
@ -710,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 {
|
||||||
|
@ -724,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 {
|
||||||
|
@ -738,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 {
|
||||||
|
@ -808,10 +777,13 @@ impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Display {
|
impl fmt::Display for Display {
|
||||||
|
#[rustfmt::skip]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let format_options = FormatOptions::from_formatter(f);
|
let format_options = FormatOptions::from_formatter(f);
|
||||||
match &self.style {
|
match &self.style {
|
||||||
DisplayStyle::FixedDenomination { show_denomination, denomination } => fmt_satoshi_in(self.sats_abs, self.is_negative, f, *denomination, *show_denomination, format_options),
|
DisplayStyle::FixedDenomination { show_denomination, denomination } => {
|
||||||
|
fmt_satoshi_in(self.sats_abs, self.is_negative, f, *denomination, *show_denomination, format_options)
|
||||||
|
},
|
||||||
DisplayStyle::DynamicDenomination if self.sats_abs >= Amount::ONE_BTC.to_sat() => {
|
DisplayStyle::DynamicDenomination if self.sats_abs >= Amount::ONE_BTC.to_sat() => {
|
||||||
fmt_satoshi_in(self.sats_abs, self.is_negative, f, Denomination::Bitcoin, true, format_options)
|
fmt_satoshi_in(self.sats_abs, self.is_negative, f, Denomination::Bitcoin, true, format_options)
|
||||||
},
|
},
|
||||||
|
@ -824,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,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> {
|
||||||
|
@ -922,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.
|
||||||
|
@ -943,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 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,8 +931,8 @@ impl SignedAmount {
|
||||||
/// Format the value of this [SignedAmount] in the given denomination.
|
/// Format the value of this [SignedAmount] in the given denomination.
|
||||||
///
|
///
|
||||||
/// Does not include the denomination.
|
/// Does not include the denomination.
|
||||||
|
#[rustfmt::skip]
|
||||||
pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
|
pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
|
||||||
|
|
||||||
fmt_satoshi_in(self.to_sat_abs(), self.is_negative(), f, denom, false, FormatOptions::default())
|
fmt_satoshi_in(self.to_sat_abs(), self.is_negative(), f, denom, false, FormatOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,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.
|
||||||
|
@ -1082,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 {
|
||||||
|
@ -1111,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 {
|
||||||
|
@ -1125,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 {
|
||||||
|
@ -1139,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 {
|
||||||
|
@ -1153,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 {
|
||||||
|
@ -1167,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 {
|
||||||
|
@ -1194,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());
|
||||||
|
|
||||||
|
@ -1219,8 +1157,8 @@ mod private {
|
||||||
/// Used to seal the `CheckedSum` trait
|
/// Used to seal the `CheckedSum` trait
|
||||||
pub trait SumSeal<A> {}
|
pub trait SumSeal<A> {}
|
||||||
|
|
||||||
impl<T> SumSeal<Amount> for T where T: Iterator<Item=Amount> {}
|
impl<T> SumSeal<Amount> for T where T: Iterator<Item = Amount> {}
|
||||||
impl<T> SumSeal<SignedAmount> for T where T: Iterator<Item=SignedAmount> {}
|
impl<T> SumSeal<SignedAmount> for T where T: Iterator<Item = SignedAmount> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -1247,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
|
||||||
|
@ -1290,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())
|
||||||
}
|
}
|
||||||
|
@ -1318,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())
|
||||||
}
|
}
|
||||||
|
@ -1334,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> {
|
||||||
|
@ -1348,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,
|
||||||
|
@ -1398,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> {
|
||||||
|
@ -1412,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,
|
||||||
|
@ -1460,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;
|
||||||
|
@ -1608,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]
|
||||||
|
@ -1831,7 +1782,7 @@ mod tests {
|
||||||
assert_eq!(Amount::max_value().to_signed(), Err(E::TooBig));
|
assert_eq!(Amount::max_value().to_signed(), Err(E::TooBig));
|
||||||
assert_eq!(ua(i64::max_value() as u64).to_signed(), Ok(sa(i64::max_value())));
|
assert_eq!(ua(i64::max_value() as u64).to_signed(), Ok(sa(i64::max_value())));
|
||||||
assert_eq!(ua(0).to_signed(), Ok(sa(0)));
|
assert_eq!(ua(0).to_signed(), Ok(sa(0)));
|
||||||
assert_eq!(ua(1).to_signed(), Ok( sa(1)));
|
assert_eq!(ua(1).to_signed(), Ok(sa(1)));
|
||||||
assert_eq!(ua(1).to_signed(), Ok(sa(1)));
|
assert_eq!(ua(1).to_signed(), Ok(sa(1)));
|
||||||
assert_eq!(ua(i64::max_value() as u64 + 1).to_signed(), Err(E::TooBig));
|
assert_eq!(ua(i64::max_value() as u64 + 1).to_signed(), Err(E::TooBig));
|
||||||
|
|
||||||
|
@ -1840,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]
|
||||||
|
@ -1902,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]
|
||||||
|
@ -1953,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 {
|
||||||
|
@ -1964,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"),
|
||||||
|
@ -2034,15 +2023,12 @@ 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() {
|
||||||
let v = serde_json::to_string(s).unwrap();
|
let v = serde_json::to_string(s).unwrap();
|
||||||
let w : T = serde_json::from_str(&v).unwrap();
|
let w: T = serde_json::from_str(&v).unwrap();
|
||||||
assert_eq!(w, **s);
|
assert_eq!(w, **s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2079,15 +2065,12 @@ 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() {
|
||||||
let v = serde_json::to_string(s).unwrap();
|
let v = serde_json::to_string(s).unwrap();
|
||||||
let w : T = serde_json::from_str(&v).unwrap();
|
let w: T = serde_json::from_str(&v).unwrap();
|
||||||
assert_eq!(w, **s);
|
assert_eq!(w, **s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2110,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);
|
||||||
|
@ -2132,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);
|
||||||
|
@ -2159,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);
|
||||||
|
@ -2167,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);
|
||||||
|
@ -2176,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());
|
||||||
}
|
}
|
||||||
|
@ -2185,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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2202,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 {
|
||||||
|
|
|
@ -63,20 +63,18 @@ extern crate test;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
// Re-export dependencies we control.
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
pub use bitcoinconsensus;
|
|
||||||
pub use {bech32, bitcoin_hashes as hashes, secp256k1};
|
|
||||||
|
|
||||||
// Re-export base64 when enabled
|
|
||||||
#[cfg(feature = "base64")]
|
#[cfg(feature = "base64")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
||||||
pub use base64;
|
pub extern crate base64;
|
||||||
|
pub extern crate bech32;
|
||||||
// Re-export hashbrown when enabled
|
pub extern crate bitcoin_hashes as hashes;
|
||||||
|
#[cfg(feature = "bitcoinconsensus")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "bitcoinconsensus")))]
|
||||||
|
pub extern crate bitcoinconsensus;
|
||||||
#[cfg(feature = "hashbrown")]
|
#[cfg(feature = "hashbrown")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "hashbrown")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "hashbrown")))]
|
||||||
pub use hashbrown;
|
pub extern crate hashbrown;
|
||||||
|
pub extern crate secp256k1;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -116,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};
|
||||||
|
@ -125,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"))]
|
||||||
|
|
|
@ -7,14 +7,13 @@
|
||||||
//! network addresses in Bitcoin messages.
|
//! network addresses in Bitcoin messages.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use core::{fmt, iter};
|
use core::{fmt, iter};
|
||||||
use std::net::{SocketAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, Ipv4Addr, ToSocketAddrs};
|
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||||
|
|
||||||
|
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, VarInt, WriteExt};
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::network::constants::ServiceFlags;
|
use crate::network::constants::ServiceFlags;
|
||||||
use crate::consensus::encode::{self, Decodable, Encodable, VarInt, ReadExt, WriteExt};
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// A message which can be sent on the Bitcoin network
|
/// A message which can be sent on the Bitcoin network
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -24,17 +23,17 @@ pub struct Address {
|
||||||
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
||||||
pub address: [u16; 8],
|
pub address: [u16; 8],
|
||||||
/// Network port
|
/// Network port
|
||||||
pub port: u16
|
pub port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ONION: [u16; 3] = [0xFD87, 0xD87E, 0xEB43];
|
const ONION: [u16; 3] = [0xFD87, 0xD87E, 0xEB43];
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
/// Create an address message for a socket
|
/// Create an address message for a socket
|
||||||
pub fn new(socket :&SocketAddr, services: ServiceFlags) -> Address {
|
pub fn new(socket: &SocketAddr, services: ServiceFlags) -> Address {
|
||||||
let (address, port) = match *socket {
|
let (address, port) = match *socket {
|
||||||
SocketAddr::V4(addr) => (addr.ip().to_ipv6_mapped().segments(), addr.port()),
|
SocketAddr::V4(addr) => (addr.ip().to_ipv6_mapped().segments(), addr.port()),
|
||||||
SocketAddr::V6(addr) => (addr.ip().segments(), addr.port())
|
SocketAddr::V6(addr) => (addr.ip().segments(), addr.port()),
|
||||||
};
|
};
|
||||||
Address { address, port, services }
|
Address { address, port, services }
|
||||||
}
|
}
|
||||||
|
@ -47,7 +46,8 @@ impl Address {
|
||||||
if addr[0..3] == ONION {
|
if addr[0..3] == ONION {
|
||||||
return Err(io::Error::from(io::ErrorKind::AddrNotAvailable));
|
return Err(io::Error::from(io::ErrorKind::AddrNotAvailable));
|
||||||
}
|
}
|
||||||
let ipv6 = Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]);
|
let ipv6 =
|
||||||
|
Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]);
|
||||||
if let Some(ipv4) = ipv6.to_ipv4() {
|
if let Some(ipv4) = ipv6.to_ipv4() {
|
||||||
Ok(SocketAddr::V4(SocketAddrV4::new(ipv4, self.port)))
|
Ok(SocketAddr::V4(SocketAddrV4::new(ipv4, self.port)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,7 +79,7 @@ impl Decodable for Address {
|
||||||
Ok(Address {
|
Ok(Address {
|
||||||
services: Decodable::consensus_decode(r)?,
|
services: Decodable::consensus_decode(r)?,
|
||||||
address: read_be_address(r)?,
|
address: read_be_address(r)?,
|
||||||
port: u16::swap_bytes(Decodable::consensus_decode(r)?)
|
port: u16::swap_bytes(Decodable::consensus_decode(r)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,10 +101,16 @@ impl fmt::Debug for Address {
|
||||||
let ipv6 = Ipv6Addr::from(self.address);
|
let ipv6 = Ipv6Addr::from(self.address);
|
||||||
|
|
||||||
match ipv6.to_ipv4() {
|
match ipv6.to_ipv4() {
|
||||||
Some(addr) => write!(f, "Address {{services: {}, address: {}, port: {}}}",
|
Some(addr) => write!(
|
||||||
self.services, addr, self.port),
|
f,
|
||||||
None => write!(f, "Address {{services: {}, address: {}, port: {}}}",
|
"Address {{services: {}, address: {}, port: {}}}",
|
||||||
self.services, ipv6, self.port)
|
self.services, addr, self.port
|
||||||
|
),
|
||||||
|
None => write!(
|
||||||
|
f,
|
||||||
|
"Address {{services: {}, address: {}, port: {}}}",
|
||||||
|
self.services, ipv6, self.port
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +143,11 @@ pub enum AddrV2 {
|
||||||
|
|
||||||
impl Encodable for AddrV2 {
|
impl Encodable for AddrV2 {
|
||||||
fn consensus_encode<W: io::Write + ?Sized>(&self, e: &mut W) -> Result<usize, io::Error> {
|
fn consensus_encode<W: io::Write + ?Sized>(&self, e: &mut W) -> Result<usize, io::Error> {
|
||||||
fn encode_addr<W: io::Write + ?Sized>(w: &mut W, network: u8, bytes: &[u8]) -> Result<usize, io::Error> {
|
fn encode_addr<W: io::Write + ?Sized>(
|
||||||
|
w: &mut W,
|
||||||
|
network: u8,
|
||||||
|
bytes: &[u8],
|
||||||
|
) -> Result<usize, io::Error> {
|
||||||
let len = network.consensus_encode(w)?
|
let len = network.consensus_encode(w)?
|
||||||
+ VarInt(bytes.len() as u64).consensus_encode(w)?
|
+ VarInt(bytes.len() as u64).consensus_encode(w)?
|
||||||
+ bytes.len();
|
+ bytes.len();
|
||||||
|
@ -151,7 +161,7 @@ impl Encodable for AddrV2 {
|
||||||
AddrV2::TorV3(ref bytes) => encode_addr(e, 4, bytes)?,
|
AddrV2::TorV3(ref bytes) => encode_addr(e, 4, bytes)?,
|
||||||
AddrV2::I2p(ref bytes) => encode_addr(e, 5, bytes)?,
|
AddrV2::I2p(ref bytes) => encode_addr(e, 5, bytes)?,
|
||||||
AddrV2::Cjdns(ref addr) => encode_addr(e, 6, &addr.octets())?,
|
AddrV2::Cjdns(ref addr) => encode_addr(e, 6, &addr.octets())?,
|
||||||
AddrV2::Unknown(network, ref bytes) => encode_addr(e, network, bytes)?
|
AddrV2::Unknown(network, ref bytes) => encode_addr(e, network, bytes)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,41 +180,47 @@ impl Decodable for AddrV2 {
|
||||||
}
|
}
|
||||||
let addr: [u8; 4] = Decodable::consensus_decode(r)?;
|
let addr: [u8; 4] = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
|
AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
|
||||||
},
|
}
|
||||||
2 => {
|
2 => {
|
||||||
if len != 16 {
|
if len != 16 {
|
||||||
return Err(encode::Error::ParseFailed("Invalid IPv6 address"));
|
return Err(encode::Error::ParseFailed("Invalid IPv6 address"));
|
||||||
}
|
}
|
||||||
let addr: [u16; 8] = read_be_address(r)?;
|
let addr: [u16; 8] = read_be_address(r)?;
|
||||||
if addr[0..3] == ONION {
|
if addr[0..3] == ONION {
|
||||||
return Err(encode::Error::ParseFailed("OnionCat address sent with IPv6 network id"));
|
return Err(encode::Error::ParseFailed(
|
||||||
|
"OnionCat address sent with IPv6 network id",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if addr[0..6] == [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF] {
|
if addr[0..6] == [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF] {
|
||||||
return Err(encode::Error::ParseFailed("IPV4 wrapped address sent with IPv6 network id"));
|
return Err(encode::Error::ParseFailed(
|
||||||
|
"IPV4 wrapped address sent with IPv6 network id",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
AddrV2::Ipv6(Ipv6Addr::new(
|
||||||
|
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
AddrV2::Ipv6(Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]))
|
|
||||||
},
|
|
||||||
3 => {
|
3 => {
|
||||||
if len != 10 {
|
if len != 10 {
|
||||||
return Err(encode::Error::ParseFailed("Invalid TorV2 address"));
|
return Err(encode::Error::ParseFailed("Invalid TorV2 address"));
|
||||||
}
|
}
|
||||||
let id = Decodable::consensus_decode(r)?;
|
let id = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::TorV2(id)
|
AddrV2::TorV2(id)
|
||||||
},
|
}
|
||||||
4 => {
|
4 => {
|
||||||
if len != 32 {
|
if len != 32 {
|
||||||
return Err(encode::Error::ParseFailed("Invalid TorV3 address"));
|
return Err(encode::Error::ParseFailed("Invalid TorV3 address"));
|
||||||
}
|
}
|
||||||
let pubkey = Decodable::consensus_decode(r)?;
|
let pubkey = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::TorV3(pubkey)
|
AddrV2::TorV3(pubkey)
|
||||||
},
|
}
|
||||||
5 => {
|
5 => {
|
||||||
if len != 32 {
|
if len != 32 {
|
||||||
return Err(encode::Error::ParseFailed("Invalid I2P address"));
|
return Err(encode::Error::ParseFailed("Invalid I2P address"));
|
||||||
}
|
}
|
||||||
let hash = Decodable::consensus_decode(r)?;
|
let hash = Decodable::consensus_decode(r)?;
|
||||||
AddrV2::I2p(hash)
|
AddrV2::I2p(hash)
|
||||||
},
|
}
|
||||||
6 => {
|
6 => {
|
||||||
if len != 16 {
|
if len != 16 {
|
||||||
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
||||||
|
@ -214,8 +230,10 @@ impl Decodable for AddrV2 {
|
||||||
if addr[0] != u16::from_be_bytes([0xFC, 0x00]) {
|
if addr[0] != u16::from_be_bytes([0xFC, 0x00]) {
|
||||||
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
||||||
}
|
}
|
||||||
AddrV2::Cjdns(Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]))
|
AddrV2::Cjdns(Ipv6Addr::new(
|
||||||
},
|
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
|
||||||
|
))
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// len already checked above to be <= 512
|
// len already checked above to be <= 512
|
||||||
let mut addr = vec![0u8; len as usize];
|
let mut addr = vec![0u8; len as usize];
|
||||||
|
@ -236,7 +254,7 @@ pub struct AddrV2Message {
|
||||||
/// Network ID + Network Address
|
/// Network ID + Network Address
|
||||||
pub addr: AddrV2,
|
pub addr: AddrV2,
|
||||||
/// Network port, 0 if not applicable
|
/// Network port, 0 if not applicable
|
||||||
pub port: u16
|
pub port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddrV2Message {
|
impl AddrV2Message {
|
||||||
|
@ -287,22 +305,26 @@ impl ToSocketAddrs for AddrV2Message {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
use super::{AddrV2Message, AddrV2, Address};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
use crate::network::constants::ServiceFlags;
|
|
||||||
use std::net::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr};
|
|
||||||
use crate::hashes::hex::FromHex;
|
|
||||||
|
|
||||||
|
use super::{AddrV2, AddrV2Message, Address};
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
|
use crate::hashes::hex::FromHex;
|
||||||
|
use crate::network::constants::ServiceFlags;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_address_test() {
|
fn serialize_address_test() {
|
||||||
assert_eq!(serialize(&Address {
|
assert_eq!(
|
||||||
|
serialize(&Address {
|
||||||
services: ServiceFlags::NETWORK,
|
services: ServiceFlags::NETWORK,
|
||||||
address: [0, 0, 0, 0, 0, 0xffff, 0x0a00, 0x0001],
|
address: [0, 0, 0, 0, 0, 0xffff, 0x0a00, 0x0001],
|
||||||
port: 8333
|
port: 8333
|
||||||
}),
|
}),
|
||||||
vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
vec![
|
||||||
0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1, 0x20, 0x8d]);
|
1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||||
|
0x20, 0x8d
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -329,41 +351,47 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_address_test() {
|
fn deserialize_address_test() {
|
||||||
let mut addr: Result<Address, _> = deserialize(&[1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
let mut addr: Result<Address, _> = deserialize(&[
|
||||||
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||||
0, 1, 0x20, 0x8d]);
|
0x20, 0x8d,
|
||||||
|
]);
|
||||||
assert!(addr.is_ok());
|
assert!(addr.is_ok());
|
||||||
let full = addr.unwrap();
|
let full = addr.unwrap();
|
||||||
assert!(match full.socket_addr().unwrap() {
|
assert!(match full.socket_addr().unwrap() {
|
||||||
SocketAddr::V4(_) => true,
|
SocketAddr::V4(_) => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
assert_eq!(full.services, ServiceFlags::NETWORK);
|
assert_eq!(full.services, ServiceFlags::NETWORK);
|
||||||
assert_eq!(full.address, [0, 0, 0, 0, 0, 0xffff, 0x0a00, 0x0001]);
|
assert_eq!(full.address, [0, 0, 0, 0, 0, 0xffff, 0x0a00, 0x0001]);
|
||||||
assert_eq!(full.port, 8333);
|
assert_eq!(full.port, 8333);
|
||||||
|
|
||||||
addr = deserialize(&[1u8, 0, 0, 0, 0, 0, 0, 0, 0,
|
addr = deserialize(&[
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||||
|
]);
|
||||||
assert!(addr.is_err());
|
assert!(addr.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_socket_addr () {
|
fn test_socket_addr() {
|
||||||
let s4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(111,222,123,4)), 5555);
|
let s4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(111, 222, 123, 4)), 5555);
|
||||||
let a4 = Address::new(&s4, ServiceFlags::NETWORK | ServiceFlags::WITNESS);
|
let a4 = Address::new(&s4, ServiceFlags::NETWORK | ServiceFlags::WITNESS);
|
||||||
assert_eq!(a4.socket_addr().unwrap(), s4);
|
assert_eq!(a4.socket_addr().unwrap(), s4);
|
||||||
let s6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444,
|
let s6 = SocketAddr::new(
|
||||||
0x5555, 0x6666, 0x7777, 0x8888)), 9999);
|
IpAddr::V6(Ipv6Addr::new(
|
||||||
|
0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888,
|
||||||
|
)),
|
||||||
|
9999,
|
||||||
|
);
|
||||||
let a6 = Address::new(&s6, ServiceFlags::NETWORK | ServiceFlags::WITNESS);
|
let a6 = Address::new(&s6, ServiceFlags::NETWORK | ServiceFlags::WITNESS);
|
||||||
assert_eq!(a6.socket_addr().unwrap(), s6);
|
assert_eq!(a6.socket_addr().unwrap(), s6);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn onion_test () {
|
fn onion_test() {
|
||||||
let onionaddr = SocketAddr::new(
|
let onionaddr = SocketAddr::new(
|
||||||
IpAddr::V6(
|
IpAddr::V6(Ipv6Addr::from_str("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").unwrap()),
|
||||||
Ipv6Addr::from_str("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").unwrap()), 1111);
|
1111,
|
||||||
|
);
|
||||||
let addr = Address::new(&onionaddr, ServiceFlags::NONE);
|
let addr = Address::new(&onionaddr, ServiceFlags::NONE);
|
||||||
assert!(addr.socket_addr().is_err());
|
assert!(addr.socket_addr().is_err());
|
||||||
}
|
}
|
||||||
|
@ -375,17 +403,32 @@ mod test {
|
||||||
let ip = AddrV2::Ipv4(Ipv4Addr::new(1, 2, 3, 4));
|
let ip = AddrV2::Ipv4(Ipv4Addr::new(1, 2, 3, 4));
|
||||||
assert_eq!(serialize(&ip), Vec::from_hex("010401020304").unwrap());
|
assert_eq!(serialize(&ip), Vec::from_hex("010401020304").unwrap());
|
||||||
|
|
||||||
let ip = AddrV2::Ipv6(Ipv6Addr::from_str("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b").unwrap());
|
let ip =
|
||||||
|
AddrV2::Ipv6(Ipv6Addr::from_str("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b").unwrap());
|
||||||
assert_eq!(serialize(&ip), Vec::from_hex("02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b").unwrap());
|
assert_eq!(serialize(&ip), Vec::from_hex("02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b").unwrap());
|
||||||
|
|
||||||
let ip = AddrV2::TorV2(FromHex::from_hex("f1f2f3f4f5f6f7f8f9fa").unwrap());
|
let ip = AddrV2::TorV2(FromHex::from_hex("f1f2f3f4f5f6f7f8f9fa").unwrap());
|
||||||
assert_eq!(serialize(&ip), Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap());
|
assert_eq!(serialize(&ip), Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap());
|
||||||
|
|
||||||
let ip = AddrV2::TorV3(FromHex::from_hex("53cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88").unwrap());
|
let ip = AddrV2::TorV3(
|
||||||
assert_eq!(serialize(&ip), Vec::from_hex("042053cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88").unwrap());
|
FromHex::from_hex("53cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88")
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
serialize(&ip),
|
||||||
|
Vec::from_hex("042053cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88")
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
let ip = AddrV2::I2p(FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap());
|
let ip = AddrV2::I2p(
|
||||||
assert_eq!(serialize(&ip), Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap());
|
FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87")
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
serialize(&ip),
|
||||||
|
Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87")
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
let ip = AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap());
|
let ip = AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap());
|
||||||
assert_eq!(serialize(&ip), Vec::from_hex("0610fc000001000200030004000500060007").unwrap());
|
assert_eq!(serialize(&ip), Vec::from_hex("0610fc000001000200030004000500060007").unwrap());
|
||||||
|
@ -412,17 +455,27 @@ mod test {
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("01fd010201020304").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(&Vec::from_hex("01fd010201020304").unwrap()).is_err());
|
||||||
|
|
||||||
// Valid IPv6.
|
// Valid IPv6.
|
||||||
let ip: AddrV2 = deserialize(&Vec::from_hex("02100102030405060708090a0b0c0d0e0f10").unwrap()).unwrap();
|
let ip: AddrV2 =
|
||||||
assert_eq!(ip, AddrV2::Ipv6(Ipv6Addr::from_str("102:304:506:708:90a:b0c:d0e:f10").unwrap()));
|
deserialize(&Vec::from_hex("02100102030405060708090a0b0c0d0e0f10").unwrap()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
ip,
|
||||||
|
AddrV2::Ipv6(Ipv6Addr::from_str("102:304:506:708:90a:b0c:d0e:f10").unwrap())
|
||||||
|
);
|
||||||
|
|
||||||
// Invalid IPv6, with bogus length.
|
// Invalid IPv6, with bogus length.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("020400").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(&Vec::from_hex("020400").unwrap()).is_err());
|
||||||
|
|
||||||
// Invalid IPv6, contains embedded IPv4.
|
// Invalid IPv6, contains embedded IPv4.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("021000000000000000000000ffff01020304").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(
|
||||||
|
&Vec::from_hex("021000000000000000000000ffff01020304").unwrap()
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
// Invalid IPv6, contains embedded TORv2.
|
// Invalid IPv6, contains embedded TORv2.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("0210fd87d87eeb430102030405060708090a").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(
|
||||||
|
&Vec::from_hex("0210fd87d87eeb430102030405060708090a").unwrap()
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
// Valid TORv2.
|
// Valid TORv2.
|
||||||
let ip: AddrV2 = deserialize(&Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap()).unwrap();
|
let ip: AddrV2 = deserialize(&Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap()).unwrap();
|
||||||
|
@ -432,31 +485,61 @@ mod test {
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("030700").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(&Vec::from_hex("030700").unwrap()).is_err());
|
||||||
|
|
||||||
// Valid TORv3.
|
// Valid TORv3.
|
||||||
let ip: AddrV2 = deserialize(&Vec::from_hex("042079bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f").unwrap()).unwrap();
|
let ip: AddrV2 = deserialize(
|
||||||
assert_eq!(ip, AddrV2::TorV3(FromHex::from_hex("79bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f").unwrap()));
|
&Vec::from_hex("042079bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f")
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
ip,
|
||||||
|
AddrV2::TorV3(
|
||||||
|
FromHex::from_hex(
|
||||||
|
"79bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Invalid TORv3, with bogus length.
|
// Invalid TORv3, with bogus length.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("040000").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(&Vec::from_hex("040000").unwrap()).is_err());
|
||||||
|
|
||||||
// Valid I2P.
|
// Valid I2P.
|
||||||
let ip: AddrV2 = deserialize(&Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()).unwrap();
|
let ip: AddrV2 = deserialize(
|
||||||
assert_eq!(ip, AddrV2::I2p(FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()));
|
&Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87")
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
ip,
|
||||||
|
AddrV2::I2p(
|
||||||
|
FromHex::from_hex(
|
||||||
|
"a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Invalid I2P, with bogus length.
|
// Invalid I2P, with bogus length.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("050300").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(&Vec::from_hex("050300").unwrap()).is_err());
|
||||||
|
|
||||||
// Valid CJDNS.
|
// Valid CJDNS.
|
||||||
let ip: AddrV2 = deserialize(&Vec::from_hex("0610fc000001000200030004000500060007").unwrap()).unwrap();
|
let ip: AddrV2 =
|
||||||
|
deserialize(&Vec::from_hex("0610fc000001000200030004000500060007").unwrap()).unwrap();
|
||||||
assert_eq!(ip, AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap()));
|
assert_eq!(ip, AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap()));
|
||||||
|
|
||||||
// Invalid CJDNS, incorrect marker
|
// Invalid CJDNS, incorrect marker
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("0610fd000001000200030004000500060007").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(
|
||||||
|
&Vec::from_hex("0610fd000001000200030004000500060007").unwrap()
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
// Invalid CJDNS, with bogus length.
|
// Invalid CJDNS, with bogus length.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("060100").unwrap()).is_err());
|
assert!(deserialize::<AddrV2>(&Vec::from_hex("060100").unwrap()).is_err());
|
||||||
|
|
||||||
// Unknown, with extreme length.
|
// Unknown, with extreme length.
|
||||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("aafe0000000201020304050607").unwrap()).is_err());
|
assert!(
|
||||||
|
deserialize::<AddrV2>(&Vec::from_hex("aafe0000000201020304050607").unwrap()).is_err()
|
||||||
|
);
|
||||||
|
|
||||||
// Unknown, with reasonable length.
|
// Unknown, with reasonable length.
|
||||||
let ip: AddrV2 = deserialize(&Vec::from_hex("aa0401020304").unwrap()).unwrap();
|
let ip: AddrV2 = deserialize(&Vec::from_hex("aa0401020304").unwrap()).unwrap();
|
||||||
|
@ -472,10 +555,25 @@ mod test {
|
||||||
let raw = Vec::from_hex("0261bc6649019902abab208d79627683fd4804010409090909208d").unwrap();
|
let raw = Vec::from_hex("0261bc6649019902abab208d79627683fd4804010409090909208d").unwrap();
|
||||||
let addresses: Vec<AddrV2Message> = deserialize(&raw).unwrap();
|
let addresses: Vec<AddrV2Message> = deserialize(&raw).unwrap();
|
||||||
|
|
||||||
assert_eq!(addresses, vec![
|
assert_eq!(
|
||||||
AddrV2Message{services: ServiceFlags::NETWORK, time: 0x4966bc61, port: 8333, addr: AddrV2::Unknown(153, Vec::from_hex("abab").unwrap())},
|
addresses,
|
||||||
AddrV2Message{services: ServiceFlags::NETWORK_LIMITED | ServiceFlags::WITNESS | ServiceFlags::COMPACT_FILTERS, time: 0x83766279, port: 8333, addr: AddrV2::Ipv4(Ipv4Addr::new(9, 9, 9, 9))},
|
vec![
|
||||||
]);
|
AddrV2Message {
|
||||||
|
services: ServiceFlags::NETWORK,
|
||||||
|
time: 0x4966bc61,
|
||||||
|
port: 8333,
|
||||||
|
addr: AddrV2::Unknown(153, Vec::from_hex("abab").unwrap())
|
||||||
|
},
|
||||||
|
AddrV2Message {
|
||||||
|
services: ServiceFlags::NETWORK_LIMITED
|
||||||
|
| ServiceFlags::WITNESS
|
||||||
|
| ServiceFlags::COMPACT_FILTERS,
|
||||||
|
time: 0x83766279,
|
||||||
|
port: 8333,
|
||||||
|
addr: AddrV2::Ipv4(Ipv4Addr::new(9, 9, 9, 9))
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(serialize(&addresses), raw);
|
assert_eq!(serialize(&addresses), raw);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,19 +26,20 @@
|
||||||
//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
|
//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use core::{fmt, ops, convert::TryFrom, borrow::Borrow, borrow::BorrowMut};
|
use core::borrow::{Borrow, BorrowMut};
|
||||||
|
use core::convert::TryFrom;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
use core::{fmt, ops};
|
||||||
|
|
||||||
|
use bitcoin_internals::{debug_from_display, write_err};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use bitcoin_internals::{debug_from_display, write_err};
|
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||||
|
use crate::error::impl_std_error;
|
||||||
|
use crate::hashes::hex::{Error, FromHex};
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::prelude::{String, ToOwned};
|
use crate::prelude::{String, ToOwned};
|
||||||
use crate::consensus::encode::{self, Encodable, Decodable};
|
|
||||||
use crate::hashes::hex::{FromHex, Error};
|
|
||||||
use crate::error::impl_std_error;
|
|
||||||
|
|
||||||
/// Version of the protocol as appearing in network message headers
|
/// Version of the protocol as appearing in network message headers
|
||||||
/// This constant is used to signal to other peers which features you support.
|
/// This constant is used to signal to other peers which features you support.
|
||||||
|
@ -86,9 +87,7 @@ impl Network {
|
||||||
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9])));
|
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9])));
|
||||||
/// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
|
/// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_magic(magic: Magic) -> Option<Network> {
|
pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
|
||||||
Network::try_from(magic).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the network magic bytes, which should be encoded little-endian
|
/// Return the network magic bytes, which should be encoded little-endian
|
||||||
/// at the start of every message
|
/// at the start of every message
|
||||||
|
@ -101,9 +100,7 @@ impl Network {
|
||||||
/// let network = Network::Bitcoin;
|
/// let network = Network::Bitcoin;
|
||||||
/// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9]));
|
/// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9]));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn magic(self) -> Magic {
|
pub fn magic(self) -> Magic { Magic::from(self) }
|
||||||
Magic::from(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error in parsing network string.
|
/// An error in parsing network string.
|
||||||
|
@ -129,7 +126,7 @@ impl FromStr for Network {
|
||||||
"testnet" => Testnet,
|
"testnet" => Testnet,
|
||||||
"signet" => Signet,
|
"signet" => Signet,
|
||||||
"regtest" => Regtest,
|
"regtest" => Regtest,
|
||||||
_ => return Err(ParseNetworkError(s.to_owned()))
|
_ => return Err(ParseNetworkError(s.to_owned())),
|
||||||
};
|
};
|
||||||
Ok(network)
|
Ok(network)
|
||||||
}
|
}
|
||||||
|
@ -164,14 +161,10 @@ impl Magic {
|
||||||
pub const REGTEST: Self = Self([0xFA, 0xBF, 0xB5, 0xDA]);
|
pub const REGTEST: Self = Self([0xFA, 0xBF, 0xB5, 0xDA]);
|
||||||
|
|
||||||
/// Create network magic from bytes.
|
/// Create network magic from bytes.
|
||||||
pub fn from_bytes(bytes: [u8; 4]) -> Magic {
|
pub fn from_bytes(bytes: [u8; 4]) -> Magic { Magic(bytes) }
|
||||||
Magic(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get network magic bytes.
|
/// Get network magic bytes.
|
||||||
pub fn to_bytes(self) -> [u8; 4] {
|
pub fn to_bytes(self) -> [u8; 4] { self.0 }
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error in parsing magic bytes.
|
/// An error in parsing magic bytes.
|
||||||
|
@ -180,7 +173,7 @@ pub struct ParseMagicError {
|
||||||
/// The error that occurred when parsing the string.
|
/// The error that occurred when parsing the string.
|
||||||
error: Error,
|
error: Error,
|
||||||
/// The byte string that failed to parse.
|
/// The byte string that failed to parse.
|
||||||
magic: String
|
magic: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Magic {
|
impl FromStr for Magic {
|
||||||
|
@ -189,7 +182,7 @@ impl FromStr for Magic {
|
||||||
fn from_str(s: &str) -> Result<Magic, Self::Err> {
|
fn from_str(s: &str) -> Result<Magic, Self::Err> {
|
||||||
match <[u8; 4]>::from_hex(s) {
|
match <[u8; 4]>::from_hex(s) {
|
||||||
Ok(magic) => Ok(Magic::from_bytes(magic)),
|
Ok(magic) => Ok(Magic::from_bytes(magic)),
|
||||||
Err(e) => Err(ParseMagicError { error: e, magic: s.to_owned() })
|
Err(e) => Err(ParseMagicError { error: e, magic: s.to_owned() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,7 +194,7 @@ impl From<Network> for Magic {
|
||||||
Network::Bitcoin => Magic::BITCOIN,
|
Network::Bitcoin => Magic::BITCOIN,
|
||||||
Network::Testnet => Magic::TESTNET,
|
Network::Testnet => Magic::TESTNET,
|
||||||
Network::Signet => Magic::SIGNET,
|
Network::Signet => Magic::SIGNET,
|
||||||
Network::Regtest => Magic::REGTEST
|
Network::Regtest => Magic::REGTEST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +213,7 @@ impl TryFrom<Magic> for Network {
|
||||||
Magic::TESTNET => Ok(Network::Testnet),
|
Magic::TESTNET => Ok(Network::Testnet),
|
||||||
Magic::SIGNET => Ok(Network::Signet),
|
Magic::SIGNET => Ok(Network::Signet),
|
||||||
Magic::REGTEST => Ok(Network::Regtest),
|
Magic::REGTEST => Ok(Network::Regtest),
|
||||||
_ => Err(UnknownMagic(magic))
|
_ => Err(UnknownMagic(magic)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,51 +253,35 @@ impl Decodable for Magic {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<[u8]> for Magic {
|
impl AsRef<[u8]> for Magic {
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] { &self.0 }
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<[u8; 4]> for Magic {
|
impl AsRef<[u8; 4]> for Magic {
|
||||||
fn as_ref(&self) -> &[u8; 4] {
|
fn as_ref(&self) -> &[u8; 4] { &self.0 }
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsMut<[u8]> for Magic {
|
impl AsMut<[u8]> for Magic {
|
||||||
fn as_mut(&mut self) -> &mut [u8] {
|
fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsMut<[u8; 4]> for Magic {
|
impl AsMut<[u8; 4]> for Magic {
|
||||||
fn as_mut(&mut self) -> &mut [u8; 4] {
|
fn as_mut(&mut self) -> &mut [u8; 4] { &mut self.0 }
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<[u8]> for Magic {
|
impl Borrow<[u8]> for Magic {
|
||||||
fn borrow(&self) -> &[u8] {
|
fn borrow(&self) -> &[u8] { &self.0 }
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<[u8; 4]> for Magic {
|
impl Borrow<[u8; 4]> for Magic {
|
||||||
fn borrow(&self) -> &[u8; 4] {
|
fn borrow(&self) -> &[u8; 4] { &self.0 }
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowMut<[u8]> for Magic {
|
impl BorrowMut<[u8]> for Magic {
|
||||||
fn borrow_mut(&mut self) -> &mut [u8] {
|
fn borrow_mut(&mut self) -> &mut [u8] { &mut self.0 }
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorrowMut<[u8; 4]> for Magic {
|
impl BorrowMut<[u8; 4]> for Magic {
|
||||||
fn borrow_mut(&mut self) -> &mut [u8; 4] {
|
fn borrow_mut(&mut self) -> &mut [u8; 4] { &mut self.0 }
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParseMagicError {
|
impl fmt::Display for ParseMagicError {
|
||||||
|
@ -376,26 +353,18 @@ impl ServiceFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether [ServiceFlags] are included in this one.
|
/// Check whether [ServiceFlags] are included in this one.
|
||||||
pub fn has(self, flags: ServiceFlags) -> bool {
|
pub fn has(self, flags: ServiceFlags) -> bool { (self.0 | flags.0) == self.0 }
|
||||||
(self.0 | flags.0) == self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the integer representation of this [`ServiceFlags`].
|
/// Gets the integer representation of this [`ServiceFlags`].
|
||||||
pub fn to_u64(self) -> u64 {
|
pub fn to_u64(self) -> u64 { self.0 }
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::LowerHex for ServiceFlags {
|
impl fmt::LowerHex for ServiceFlags {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
|
||||||
fmt::LowerHex::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::UpperHex for ServiceFlags {
|
impl fmt::UpperHex for ServiceFlags {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
|
||||||
fmt::UpperHex::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ServiceFlags {
|
impl fmt::Display for ServiceFlags {
|
||||||
|
@ -415,7 +384,7 @@ impl fmt::Display for ServiceFlags {
|
||||||
write!(f, stringify!($f))?;
|
write!(f, stringify!($f))?;
|
||||||
flags.remove(ServiceFlags::$f);
|
flags.remove(ServiceFlags::$f);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
write!(f, "ServiceFlags(")?;
|
write!(f, "ServiceFlags(")?;
|
||||||
write_flag!(NETWORK);
|
write_flag!(NETWORK);
|
||||||
|
@ -436,43 +405,31 @@ impl fmt::Display for ServiceFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u64> for ServiceFlags {
|
impl From<u64> for ServiceFlags {
|
||||||
fn from(f: u64) -> Self {
|
fn from(f: u64) -> Self { ServiceFlags(f) }
|
||||||
ServiceFlags(f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ServiceFlags> for u64 {
|
impl From<ServiceFlags> for u64 {
|
||||||
fn from(flags: ServiceFlags) -> Self {
|
fn from(flags: ServiceFlags) -> Self { flags.0 }
|
||||||
flags.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::BitOr for ServiceFlags {
|
impl ops::BitOr for ServiceFlags {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn bitor(mut self, rhs: Self) -> Self {
|
fn bitor(mut self, rhs: Self) -> Self { self.add(rhs) }
|
||||||
self.add(rhs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::BitOrAssign for ServiceFlags {
|
impl ops::BitOrAssign for ServiceFlags {
|
||||||
fn bitor_assign(&mut self, rhs: Self) {
|
fn bitor_assign(&mut self, rhs: Self) { self.add(rhs); }
|
||||||
self.add(rhs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::BitXor for ServiceFlags {
|
impl ops::BitXor for ServiceFlags {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn bitxor(mut self, rhs: Self) -> Self {
|
fn bitxor(mut self, rhs: Self) -> Self { self.remove(rhs) }
|
||||||
self.remove(rhs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::BitXorAssign for ServiceFlags {
|
impl ops::BitXorAssign for ServiceFlags {
|
||||||
fn bitxor_assign(&mut self, rhs: Self) {
|
fn bitxor_assign(&mut self, rhs: Self) { self.remove(rhs); }
|
||||||
self.remove(rhs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for ServiceFlags {
|
impl Encodable for ServiceFlags {
|
||||||
|
@ -491,11 +448,12 @@ impl Decodable for ServiceFlags {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Network, ServiceFlags, Magic};
|
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::{Magic, Network, ServiceFlags};
|
||||||
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_test() {
|
fn serialize_test() {
|
||||||
assert_eq!(serialize(&Network::Bitcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd9]);
|
assert_eq!(serialize(&Network::Bitcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd9]);
|
||||||
|
@ -507,7 +465,6 @@ mod tests {
|
||||||
assert_eq!(deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(), Some(Network::Testnet.magic()));
|
assert_eq!(deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(), Some(Network::Testnet.magic()));
|
||||||
assert_eq!(deserialize(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Some(Network::Signet.magic()));
|
assert_eq!(deserialize(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Some(Network::Signet.magic()));
|
||||||
assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
|
assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -568,7 +525,12 @@ mod tests {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
fn serde_roundtrip() {
|
fn serde_roundtrip() {
|
||||||
use Network::*;
|
use Network::*;
|
||||||
let tests = vec![(Bitcoin, "bitcoin"), (Testnet, "testnet"), (Signet, "signet"), (Regtest, "regtest")];
|
let tests = vec![
|
||||||
|
(Bitcoin, "bitcoin"),
|
||||||
|
(Testnet, "testnet"),
|
||||||
|
(Signet, "signet"),
|
||||||
|
(Regtest, "regtest"),
|
||||||
|
];
|
||||||
|
|
||||||
for tc in tests {
|
for tc in tests {
|
||||||
let network = tc.0;
|
let network = tc.0;
|
||||||
|
|
|
@ -7,24 +7,22 @@
|
||||||
//! are used for (de)serializing Bitcoin objects for transmission on the network.
|
//! are used for (de)serializing Bitcoin objects for transmission on the network.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use core::{fmt, iter};
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
use core::{fmt, iter};
|
||||||
|
|
||||||
use crate::io;
|
|
||||||
use io::Read as _;
|
use io::Read as _;
|
||||||
use crate::blockdata::block;
|
|
||||||
use crate::blockdata::transaction;
|
use crate::blockdata::{block, transaction};
|
||||||
use crate::network::address::{Address, AddrV2Message};
|
|
||||||
use crate::network::{message_network, message_bloom};
|
|
||||||
use crate::network::message_blockdata;
|
|
||||||
use crate::network::message_filter;
|
|
||||||
use crate::network::message_compact_blocks;
|
|
||||||
use crate::network::constants::Magic;
|
|
||||||
use crate::consensus::encode::{CheckedData, Decodable, Encodable, VarInt};
|
use crate::consensus::encode::{CheckedData, Decodable, Encodable, VarInt};
|
||||||
use crate::consensus::{encode, serialize};
|
use crate::consensus::{encode, serialize};
|
||||||
|
use crate::io;
|
||||||
use crate::merkle_tree::MerkleBlock;
|
use crate::merkle_tree::MerkleBlock;
|
||||||
|
use crate::network::address::{AddrV2Message, Address};
|
||||||
|
use crate::network::constants::Magic;
|
||||||
|
use crate::network::{
|
||||||
|
message_blockdata, message_bloom, message_compact_blocks, message_filter, message_network,
|
||||||
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// The maximum number of [super::message_blockdata::Inventory] items in an `inv` message.
|
/// The maximum number of [super::message_blockdata::Inventory] items in an `inv` message.
|
||||||
///
|
///
|
||||||
|
@ -95,15 +93,11 @@ impl core::str::FromStr for CommandString {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CommandString {
|
impl fmt::Display for CommandString {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.0.as_ref()) }
|
||||||
f.write_str(self.0.as_ref())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<str> for CommandString {
|
impl AsRef<str> for CommandString {
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str { self.0.as_ref() }
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for CommandString {
|
impl Encodable for CommandString {
|
||||||
|
@ -121,11 +115,13 @@ impl Decodable for CommandString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||||
let rawbytes: [u8; 12] = Decodable::consensus_decode(r)?;
|
let rawbytes: [u8; 12] = Decodable::consensus_decode(r)?;
|
||||||
let rv = iter::FromIterator::from_iter(
|
let rv = iter::FromIterator::from_iter(rawbytes.iter().filter_map(|&u| {
|
||||||
rawbytes
|
if u > 0 {
|
||||||
.iter()
|
Some(u as char)
|
||||||
.filter_map(|&u| if u > 0 { Some(u as char) } else { None })
|
} else {
|
||||||
);
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
Ok(CommandString(rv))
|
Ok(CommandString(rv))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +136,12 @@ pub struct CommandStringError {
|
||||||
|
|
||||||
impl fmt::Display for CommandStringError {
|
impl fmt::Display for CommandStringError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "the command string '{}' has length {} which is larger than 12", self.cow, self.cow.len())
|
write!(
|
||||||
|
f,
|
||||||
|
"the command string '{}' has length {} which is larger than 12",
|
||||||
|
self.cow,
|
||||||
|
self.cow.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +153,7 @@ pub struct RawNetworkMessage {
|
||||||
/// Magic bytes to identify the network these messages are meant for
|
/// Magic bytes to identify the network these messages are meant for
|
||||||
pub magic: Magic,
|
pub magic: Magic,
|
||||||
/// The actual message data
|
/// The actual message data
|
||||||
pub payload: NetworkMessage
|
pub payload: NetworkMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Network message payload. Proper documentation is available on at
|
/// A Network message payload. Proper documentation is available on at
|
||||||
|
@ -241,7 +242,7 @@ pub enum NetworkMessage {
|
||||||
command: CommandString,
|
command: CommandString,
|
||||||
/// The payload of this message.
|
/// The payload of this message.
|
||||||
payload: Vec<u8>,
|
payload: Vec<u8>,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkMessage {
|
impl NetworkMessage {
|
||||||
|
@ -296,7 +297,7 @@ impl NetworkMessage {
|
||||||
pub fn command(&self) -> CommandString {
|
pub fn command(&self) -> CommandString {
|
||||||
match *self {
|
match *self {
|
||||||
NetworkMessage::Unknown { command: ref c, .. } => c.clone(),
|
NetworkMessage::Unknown { command: ref c, .. } => c.clone(),
|
||||||
_ => CommandString::try_from_static(self.cmd()).expect("cmd returns valid commands")
|
_ => CommandString::try_from_static(self.cmd()).expect("cmd returns valid commands"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,14 +308,10 @@ impl RawNetworkMessage {
|
||||||
/// This returns `"unknown"` for [NetworkMessage::Unknown],
|
/// This returns `"unknown"` for [NetworkMessage::Unknown],
|
||||||
/// regardless of the actual command in the unknown message.
|
/// regardless of the actual command in the unknown message.
|
||||||
/// Use the [Self::command] method to get the command for unknown messages.
|
/// Use the [Self::command] method to get the command for unknown messages.
|
||||||
pub fn cmd(&self) -> &'static str {
|
pub fn cmd(&self) -> &'static str { self.payload.cmd() }
|
||||||
self.payload.cmd()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the CommandString for the message command.
|
/// Return the CommandString for the message command.
|
||||||
pub fn command(&self) -> CommandString {
|
pub fn command(&self) -> CommandString { self.payload.command() }
|
||||||
self.payload.command()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HeaderSerializationWrapper<'a>(&'a Vec<block::Header>);
|
struct HeaderSerializationWrapper<'a>(&'a Vec<block::Header>);
|
||||||
|
@ -375,7 +372,8 @@ impl Encodable for RawNetworkMessage {
|
||||||
| NetworkMessage::FilterClear
|
| NetworkMessage::FilterClear
|
||||||
| NetworkMessage::SendAddrV2 => vec![],
|
| NetworkMessage::SendAddrV2 => vec![],
|
||||||
NetworkMessage::Unknown { payload: ref data, .. } => serialize(data),
|
NetworkMessage::Unknown { payload: ref data, .. } => serialize(data),
|
||||||
}).consensus_encode(w)?;
|
})
|
||||||
|
.consensus_encode(w)?;
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,7 +382,9 @@ struct HeaderDeserializationWrapper(Vec<block::Header>);
|
||||||
|
|
||||||
impl Decodable for HeaderDeserializationWrapper {
|
impl Decodable for HeaderDeserializationWrapper {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
|
||||||
|
r: &mut R,
|
||||||
|
) -> Result<Self, encode::Error> {
|
||||||
let len = VarInt::consensus_decode(r)?.0;
|
let len = VarInt::consensus_decode(r)?.0;
|
||||||
// should be above usual number of items to avoid
|
// should be above usual number of items to avoid
|
||||||
// allocation
|
// allocation
|
||||||
|
@ -392,7 +392,9 @@ impl Decodable for HeaderDeserializationWrapper {
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
ret.push(Decodable::consensus_decode(r)?);
|
ret.push(Decodable::consensus_decode(r)?);
|
||||||
if u8::consensus_decode(r)? != 0u8 {
|
if u8::consensus_decode(r)? != 0u8 {
|
||||||
return Err(encode::Error::ParseFailed("Headers message should not contain transactions"));
|
return Err(encode::Error::ParseFailed(
|
||||||
|
"Headers message should not contain transactions",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(HeaderDeserializationWrapper(ret))
|
Ok(HeaderDeserializationWrapper(ret))
|
||||||
|
@ -405,60 +407,99 @@ impl Decodable for HeaderDeserializationWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decodable for RawNetworkMessage {
|
impl Decodable for RawNetworkMessage {
|
||||||
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
|
||||||
|
r: &mut R,
|
||||||
|
) -> Result<Self, encode::Error> {
|
||||||
let magic = Decodable::consensus_decode_from_finite_reader(r)?;
|
let magic = Decodable::consensus_decode_from_finite_reader(r)?;
|
||||||
let cmd = CommandString::consensus_decode_from_finite_reader(r)?;
|
let cmd = CommandString::consensus_decode_from_finite_reader(r)?;
|
||||||
let raw_payload = CheckedData::consensus_decode_from_finite_reader(r)?.0;
|
let raw_payload = CheckedData::consensus_decode_from_finite_reader(r)?.0;
|
||||||
|
|
||||||
let mut mem_d = io::Cursor::new(raw_payload);
|
let mut mem_d = io::Cursor::new(raw_payload);
|
||||||
let payload = match &cmd.0[..] {
|
let payload = match &cmd.0[..] {
|
||||||
"version" => NetworkMessage::Version(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"version" =>
|
||||||
|
NetworkMessage::Version(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"verack" => NetworkMessage::Verack,
|
"verack" => NetworkMessage::Verack,
|
||||||
"addr" => NetworkMessage::Addr(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"addr" =>
|
||||||
"inv" => NetworkMessage::Inv(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
NetworkMessage::Addr(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"getdata" => NetworkMessage::GetData(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"inv" =>
|
||||||
"notfound" => NetworkMessage::NotFound(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
NetworkMessage::Inv(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"getblocks" => NetworkMessage::GetBlocks(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"getdata" =>
|
||||||
"getheaders" => NetworkMessage::GetHeaders(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
NetworkMessage::GetData(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
|
"notfound" => NetworkMessage::NotFound(Decodable::consensus_decode_from_finite_reader(
|
||||||
|
&mut mem_d,
|
||||||
|
)?),
|
||||||
|
"getblocks" => NetworkMessage::GetBlocks(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"getheaders" => NetworkMessage::GetHeaders(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
"mempool" => NetworkMessage::MemPool,
|
"mempool" => NetworkMessage::MemPool,
|
||||||
"block" => NetworkMessage::Block(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"block" =>
|
||||||
|
NetworkMessage::Block(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"headers" => NetworkMessage::Headers(
|
"headers" => NetworkMessage::Headers(
|
||||||
HeaderDeserializationWrapper::consensus_decode_from_finite_reader(&mut mem_d)?.0
|
HeaderDeserializationWrapper::consensus_decode_from_finite_reader(&mut mem_d)?.0,
|
||||||
),
|
),
|
||||||
"sendheaders" => NetworkMessage::SendHeaders,
|
"sendheaders" => NetworkMessage::SendHeaders,
|
||||||
"getaddr" => NetworkMessage::GetAddr,
|
"getaddr" => NetworkMessage::GetAddr,
|
||||||
"ping" => NetworkMessage::Ping(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"ping" =>
|
||||||
"pong" => NetworkMessage::Pong(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
NetworkMessage::Ping(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"merkleblock" => NetworkMessage::MerkleBlock(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"pong" =>
|
||||||
"filterload" => NetworkMessage::FilterLoad(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
NetworkMessage::Pong(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"filteradd" => NetworkMessage::FilterAdd(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"merkleblock" => NetworkMessage::MerkleBlock(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"filterload" => NetworkMessage::FilterLoad(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"filteradd" => NetworkMessage::FilterAdd(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
"filterclear" => NetworkMessage::FilterClear,
|
"filterclear" => NetworkMessage::FilterClear,
|
||||||
"tx" => NetworkMessage::Tx(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"tx" => NetworkMessage::Tx(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"getcfilters" => NetworkMessage::GetCFilters(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"getcfilters" => NetworkMessage::GetCFilters(
|
||||||
"cfilter" => NetworkMessage::CFilter(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
"getcfheaders" => NetworkMessage::GetCFHeaders(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
),
|
||||||
"cfheaders" => NetworkMessage::CFHeaders(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"cfilter" =>
|
||||||
"getcfcheckpt" => NetworkMessage::GetCFCheckpt(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
NetworkMessage::CFilter(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"cfcheckpt" => NetworkMessage::CFCheckpt(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"getcfheaders" => NetworkMessage::GetCFHeaders(
|
||||||
"reject" => NetworkMessage::Reject(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
"alert" => NetworkMessage::Alert(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
),
|
||||||
"feefilter" => NetworkMessage::FeeFilter(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"cfheaders" => NetworkMessage::CFHeaders(
|
||||||
"sendcmpct" => NetworkMessage::SendCmpct(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
"cmpctblock" => NetworkMessage::CmpctBlock(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
),
|
||||||
"getblocktxn" => NetworkMessage::GetBlockTxn(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"getcfcheckpt" => NetworkMessage::GetCFCheckpt(
|
||||||
"blocktxn" => NetworkMessage::BlockTxn(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"cfcheckpt" => NetworkMessage::CFCheckpt(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"reject" =>
|
||||||
|
NetworkMessage::Reject(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
|
"alert" =>
|
||||||
|
NetworkMessage::Alert(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
|
"feefilter" => NetworkMessage::FeeFilter(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"sendcmpct" => NetworkMessage::SendCmpct(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"cmpctblock" => NetworkMessage::CmpctBlock(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"getblocktxn" => NetworkMessage::GetBlockTxn(
|
||||||
|
Decodable::consensus_decode_from_finite_reader(&mut mem_d)?,
|
||||||
|
),
|
||||||
|
"blocktxn" => NetworkMessage::BlockTxn(Decodable::consensus_decode_from_finite_reader(
|
||||||
|
&mut mem_d,
|
||||||
|
)?),
|
||||||
"wtxidrelay" => NetworkMessage::WtxidRelay,
|
"wtxidrelay" => NetworkMessage::WtxidRelay,
|
||||||
"addrv2" => NetworkMessage::AddrV2(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
"addrv2" =>
|
||||||
|
NetworkMessage::AddrV2(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
|
||||||
"sendaddrv2" => NetworkMessage::SendAddrV2,
|
"sendaddrv2" => NetworkMessage::SendAddrV2,
|
||||||
_ => NetworkMessage::Unknown {
|
_ => NetworkMessage::Unknown { command: cmd, payload: mem_d.into_inner() },
|
||||||
command: cmd,
|
|
||||||
payload: mem_d.into_inner(),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(RawNetworkMessage {
|
Ok(RawNetworkMessage { magic, payload })
|
||||||
magic,
|
|
||||||
payload,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -469,29 +510,28 @@ impl Decodable for RawNetworkMessage {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use super::{RawNetworkMessage, NetworkMessage, CommandString};
|
|
||||||
use crate::network::constants::{ServiceFlags, Magic, Network};
|
use super::message_network::{Reject, RejectReason, VersionMessage};
|
||||||
|
use super::{CommandString, NetworkMessage, RawNetworkMessage, *};
|
||||||
|
use crate::bip152::BlockTransactionsRequest;
|
||||||
|
use crate::blockdata::block::{self, Block};
|
||||||
|
use crate::blockdata::script::Script;
|
||||||
|
use crate::blockdata::transaction::Transaction;
|
||||||
use crate::consensus::encode::{deserialize, deserialize_partial, serialize};
|
use crate::consensus::encode::{deserialize, deserialize_partial, serialize};
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::hashes::sha256d::Hash;
|
use crate::hashes::sha256d::Hash;
|
||||||
use crate::hashes::Hash as HashTrait;
|
use crate::hashes::Hash as HashTrait;
|
||||||
use crate::network::address::{Address, AddrV2, AddrV2Message};
|
use crate::network::address::{AddrV2, AddrV2Message, Address};
|
||||||
use super::message_network::{Reject, RejectReason, VersionMessage};
|
use crate::network::constants::{Magic, Network, ServiceFlags};
|
||||||
use crate::network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage};
|
use crate::network::message_blockdata::{GetBlocksMessage, GetHeadersMessage, Inventory};
|
||||||
use crate::blockdata::block::{self, Block};
|
use crate::network::message_bloom::{BloomFlags, FilterAdd, FilterLoad};
|
||||||
use crate::network::message_filter::{GetCFilters, CFilter, GetCFHeaders, CFHeaders, GetCFCheckpt, CFCheckpt};
|
|
||||||
use crate::blockdata::transaction::Transaction;
|
|
||||||
use crate::blockdata::script::Script;
|
|
||||||
use crate::network::message_bloom::{FilterAdd, FilterLoad, BloomFlags};
|
|
||||||
use crate::network::message_compact_blocks::{GetBlockTxn, SendCmpct};
|
use crate::network::message_compact_blocks::{GetBlockTxn, SendCmpct};
|
||||||
use crate::bip152::BlockTransactionsRequest;
|
use crate::network::message_filter::{
|
||||||
|
CFCheckpt, CFHeaders, CFilter, GetCFCheckpt, GetCFHeaders, GetCFilters,
|
||||||
|
};
|
||||||
|
|
||||||
fn hash(slice: [u8;32]) -> Hash {
|
fn hash(slice: [u8; 32]) -> Hash { Hash::from_slice(&slice).unwrap() }
|
||||||
Hash::from_slice(&slice).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_round_ser_der_raw_network_message_test() {
|
fn full_round_ser_der_raw_network_message_test() {
|
||||||
|
@ -500,21 +540,32 @@ mod test {
|
||||||
let tx: Transaction = deserialize(&Vec::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap()).unwrap();
|
let tx: Transaction = deserialize(&Vec::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap()).unwrap();
|
||||||
let block: Block = deserialize(&include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw")[..]).unwrap();
|
let block: Block = deserialize(&include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw")[..]).unwrap();
|
||||||
let header: block::Header = deserialize(&Vec::from_hex("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b").unwrap()).unwrap();
|
let header: block::Header = deserialize(&Vec::from_hex("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b").unwrap()).unwrap();
|
||||||
let script: Script = deserialize(&Vec::from_hex("1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac").unwrap()).unwrap();
|
let script: Script = deserialize(
|
||||||
|
&Vec::from_hex("1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac").unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let merkle_block: MerkleBlock = deserialize(&Vec::from_hex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101").unwrap()).unwrap();
|
let merkle_block: MerkleBlock = deserialize(&Vec::from_hex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101").unwrap()).unwrap();
|
||||||
let cmptblock = deserialize(&Vec::from_hex("00000030d923ad36ff2d955abab07f8a0a6e813bc6e066b973e780c5e36674cad5d1cd1f6e265f2a17a0d35cbe701fe9d06e2c6324cfe135f6233e8b767bfa3fb4479b71115dc562ffff7f2006000000000000000000000000010002000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0302ee00ffffffff0100f9029500000000015100000000").unwrap()).unwrap();
|
let cmptblock = deserialize(&Vec::from_hex("00000030d923ad36ff2d955abab07f8a0a6e813bc6e066b973e780c5e36674cad5d1cd1f6e265f2a17a0d35cbe701fe9d06e2c6324cfe135f6233e8b767bfa3fb4479b71115dc562ffff7f2006000000000000000000000000010002000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0302ee00ffffffff0100f9029500000000015100000000").unwrap()).unwrap();
|
||||||
let blocktxn = deserialize(&Vec::from_hex("2e93c0cff39ff605020072d96bc3a8d20b8447e294d08092351c8583e08d9b5a01020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402dc0000ffffffff0200f90295000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()).unwrap();
|
let blocktxn = deserialize(&Vec::from_hex("2e93c0cff39ff605020072d96bc3a8d20b8447e294d08092351c8583e08d9b5a01020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402dc0000ffffffff0200f90295000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()).unwrap();
|
||||||
|
|
||||||
|
|
||||||
let msgs = vec![
|
let msgs = vec![
|
||||||
NetworkMessage::Version(version_msg),
|
NetworkMessage::Version(version_msg),
|
||||||
NetworkMessage::Verack,
|
NetworkMessage::Verack,
|
||||||
NetworkMessage::Addr(vec![(45, Address::new(&([123,255,000,100], 833).into(), ServiceFlags::NETWORK))]),
|
NetworkMessage::Addr(vec![(
|
||||||
|
45,
|
||||||
|
Address::new(&([123, 255, 000, 100], 833).into(), ServiceFlags::NETWORK),
|
||||||
|
)]),
|
||||||
NetworkMessage::Inv(vec![Inventory::Block(hash([8u8; 32]).into())]),
|
NetworkMessage::Inv(vec![Inventory::Block(hash([8u8; 32]).into())]),
|
||||||
NetworkMessage::GetData(vec![Inventory::Transaction(hash([45u8; 32]).into())]),
|
NetworkMessage::GetData(vec![Inventory::Transaction(hash([45u8; 32]).into())]),
|
||||||
NetworkMessage::NotFound(vec![Inventory::Error]),
|
NetworkMessage::NotFound(vec![Inventory::Error]),
|
||||||
NetworkMessage::GetBlocks(GetBlocksMessage::new(vec![hash([1u8; 32]).into(), hash([4u8; 32]).into()], hash([5u8; 32]).into())),
|
NetworkMessage::GetBlocks(GetBlocksMessage::new(
|
||||||
NetworkMessage::GetHeaders(GetHeadersMessage::new(vec![hash([10u8; 32]).into(), hash([40u8; 32]).into()], hash([50u8; 32]).into())),
|
vec![hash([1u8; 32]).into(), hash([4u8; 32]).into()],
|
||||||
|
hash([5u8; 32]).into(),
|
||||||
|
)),
|
||||||
|
NetworkMessage::GetHeaders(GetHeadersMessage::new(
|
||||||
|
vec![hash([10u8; 32]).into(), hash([40u8; 32]).into()],
|
||||||
|
hash([50u8; 32]).into(),
|
||||||
|
)),
|
||||||
NetworkMessage::MemPool,
|
NetworkMessage::MemPool,
|
||||||
NetworkMessage::Tx(tx),
|
NetworkMessage::Tx(tx),
|
||||||
NetworkMessage::Block(block),
|
NetworkMessage::Block(block),
|
||||||
|
@ -524,39 +575,86 @@ mod test {
|
||||||
NetworkMessage::Ping(15),
|
NetworkMessage::Ping(15),
|
||||||
NetworkMessage::Pong(23),
|
NetworkMessage::Pong(23),
|
||||||
NetworkMessage::MerkleBlock(merkle_block),
|
NetworkMessage::MerkleBlock(merkle_block),
|
||||||
NetworkMessage::FilterLoad(FilterLoad {filter: Vec::from_hex("03614e9b050000000000000001").unwrap(), hash_funcs: 1, tweak: 2, flags: BloomFlags::All}),
|
NetworkMessage::FilterLoad(FilterLoad {
|
||||||
NetworkMessage::FilterAdd(FilterAdd {data: script.as_bytes().to_vec()}),
|
filter: Vec::from_hex("03614e9b050000000000000001").unwrap(),
|
||||||
NetworkMessage::FilterAdd(FilterAdd {data: hash([29u8; 32]).to_vec()}),
|
hash_funcs: 1,
|
||||||
|
tweak: 2,
|
||||||
|
flags: BloomFlags::All,
|
||||||
|
}),
|
||||||
|
NetworkMessage::FilterAdd(FilterAdd { data: script.as_bytes().to_vec() }),
|
||||||
|
NetworkMessage::FilterAdd(FilterAdd { data: hash([29u8; 32]).to_vec() }),
|
||||||
NetworkMessage::FilterClear,
|
NetworkMessage::FilterClear,
|
||||||
NetworkMessage::GetCFilters(GetCFilters{filter_type: 2, start_height: 52, stop_hash: hash([42u8; 32]).into()}),
|
NetworkMessage::GetCFilters(GetCFilters {
|
||||||
NetworkMessage::CFilter(CFilter{filter_type: 7, block_hash: hash([25u8; 32]).into(), filter: vec![1,2,3]}),
|
filter_type: 2,
|
||||||
NetworkMessage::GetCFHeaders(GetCFHeaders{filter_type: 4, start_height: 102, stop_hash: hash([47u8; 32]).into()}),
|
start_height: 52,
|
||||||
NetworkMessage::CFHeaders(CFHeaders{filter_type: 13, stop_hash: hash([53u8; 32]).into(), previous_filter_header: hash([12u8; 32]).into(), filter_hashes: vec![hash([4u8; 32]).into(), hash([12u8; 32]).into()]}),
|
stop_hash: hash([42u8; 32]).into(),
|
||||||
NetworkMessage::GetCFCheckpt(GetCFCheckpt{filter_type: 17, stop_hash: hash([25u8; 32]).into()}),
|
}),
|
||||||
NetworkMessage::CFCheckpt(CFCheckpt{filter_type: 27, stop_hash: hash([77u8; 32]).into(), filter_headers: vec![hash([3u8; 32]).into(), hash([99u8; 32]).into()]}),
|
NetworkMessage::CFilter(CFilter {
|
||||||
NetworkMessage::Alert(vec![45,66,3,2,6,8,9,12,3,130]),
|
filter_type: 7,
|
||||||
NetworkMessage::Reject(Reject{message: "Test reject".into(), ccode: RejectReason::Duplicate, reason: "Cause".into(), hash: hash([255u8; 32])}),
|
block_hash: hash([25u8; 32]).into(),
|
||||||
|
filter: vec![1, 2, 3],
|
||||||
|
}),
|
||||||
|
NetworkMessage::GetCFHeaders(GetCFHeaders {
|
||||||
|
filter_type: 4,
|
||||||
|
start_height: 102,
|
||||||
|
stop_hash: hash([47u8; 32]).into(),
|
||||||
|
}),
|
||||||
|
NetworkMessage::CFHeaders(CFHeaders {
|
||||||
|
filter_type: 13,
|
||||||
|
stop_hash: hash([53u8; 32]).into(),
|
||||||
|
previous_filter_header: hash([12u8; 32]).into(),
|
||||||
|
filter_hashes: vec![hash([4u8; 32]).into(), hash([12u8; 32]).into()],
|
||||||
|
}),
|
||||||
|
NetworkMessage::GetCFCheckpt(GetCFCheckpt {
|
||||||
|
filter_type: 17,
|
||||||
|
stop_hash: hash([25u8; 32]).into(),
|
||||||
|
}),
|
||||||
|
NetworkMessage::CFCheckpt(CFCheckpt {
|
||||||
|
filter_type: 27,
|
||||||
|
stop_hash: hash([77u8; 32]).into(),
|
||||||
|
filter_headers: vec![hash([3u8; 32]).into(), hash([99u8; 32]).into()],
|
||||||
|
}),
|
||||||
|
NetworkMessage::Alert(vec![45, 66, 3, 2, 6, 8, 9, 12, 3, 130]),
|
||||||
|
NetworkMessage::Reject(Reject {
|
||||||
|
message: "Test reject".into(),
|
||||||
|
ccode: RejectReason::Duplicate,
|
||||||
|
reason: "Cause".into(),
|
||||||
|
hash: hash([255u8; 32]),
|
||||||
|
}),
|
||||||
NetworkMessage::FeeFilter(1000),
|
NetworkMessage::FeeFilter(1000),
|
||||||
NetworkMessage::WtxidRelay,
|
NetworkMessage::WtxidRelay,
|
||||||
NetworkMessage::AddrV2(vec![AddrV2Message{ addr: AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)), port: 0, services: ServiceFlags::NONE, time: 0 }]),
|
NetworkMessage::AddrV2(vec![AddrV2Message {
|
||||||
|
addr: AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)),
|
||||||
|
port: 0,
|
||||||
|
services: ServiceFlags::NONE,
|
||||||
|
time: 0,
|
||||||
|
}]),
|
||||||
NetworkMessage::SendAddrV2,
|
NetworkMessage::SendAddrV2,
|
||||||
NetworkMessage::CmpctBlock(cmptblock),
|
NetworkMessage::CmpctBlock(cmptblock),
|
||||||
NetworkMessage::GetBlockTxn(GetBlockTxn { txs_request: BlockTransactionsRequest { block_hash: hash([11u8; 32]).into(), indexes: vec![0, 1, 2, 3, 10, 3002] } }),
|
NetworkMessage::GetBlockTxn(GetBlockTxn {
|
||||||
|
txs_request: BlockTransactionsRequest {
|
||||||
|
block_hash: hash([11u8; 32]).into(),
|
||||||
|
indexes: vec![0, 1, 2, 3, 10, 3002],
|
||||||
|
},
|
||||||
|
}),
|
||||||
NetworkMessage::BlockTxn(blocktxn),
|
NetworkMessage::BlockTxn(blocktxn),
|
||||||
NetworkMessage::SendCmpct(SendCmpct{send_compact: true, version: 8333}),
|
NetworkMessage::SendCmpct(SendCmpct { send_compact: true, version: 8333 }),
|
||||||
];
|
];
|
||||||
|
|
||||||
for msg in msgs {
|
for msg in msgs {
|
||||||
let raw_msg = RawNetworkMessage {magic: Magic::from_bytes([57, 0, 0, 0]), payload: msg};
|
let raw_msg =
|
||||||
|
RawNetworkMessage { magic: Magic::from_bytes([57, 0, 0, 0]), payload: msg };
|
||||||
assert_eq!(deserialize::<RawNetworkMessage>(&serialize(&raw_msg)).unwrap(), raw_msg);
|
assert_eq!(deserialize::<RawNetworkMessage>(&serialize(&raw_msg)).unwrap(), raw_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn commandstring_test() {
|
fn commandstring_test() {
|
||||||
// Test converting.
|
// Test converting.
|
||||||
assert_eq!(CommandString::try_from_static("AndrewAndrew").unwrap().as_ref(), "AndrewAndrew");
|
assert_eq!(
|
||||||
|
CommandString::try_from_static("AndrewAndrew").unwrap().as_ref(),
|
||||||
|
"AndrewAndrew"
|
||||||
|
);
|
||||||
assert!(CommandString::try_from_static("AndrewAndrewA").is_err());
|
assert!(CommandString::try_from_static("AndrewAndrewA").is_err());
|
||||||
|
|
||||||
// Test serializing.
|
// Test serializing.
|
||||||
|
@ -564,16 +662,19 @@ mod test {
|
||||||
assert_eq!(serialize(&cs), vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
assert_eq!(serialize(&cs), vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
// Test deserializing
|
// Test deserializing
|
||||||
let cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
let cs: Result<CommandString, _> =
|
||||||
|
deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
||||||
assert!(cs.is_ok());
|
assert!(cs.is_ok());
|
||||||
assert_eq!(cs.as_ref().unwrap().to_string(), "Andrew".to_owned());
|
assert_eq!(cs.as_ref().unwrap().to_string(), "Andrew".to_owned());
|
||||||
assert_eq!(cs.unwrap(), CommandString::try_from_static("Andrew").unwrap());
|
assert_eq!(cs.unwrap(), CommandString::try_from_static("Andrew").unwrap());
|
||||||
|
|
||||||
let short_cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
|
let short_cs: Result<CommandString, _> =
|
||||||
|
deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
|
||||||
assert!(short_cs.is_err());
|
assert!(short_cs.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[rustfmt::skip]
|
||||||
fn serialize_verack_test() {
|
fn serialize_verack_test() {
|
||||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::Verack }),
|
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::Verack }),
|
||||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x61,
|
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x61,
|
||||||
|
@ -582,6 +683,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[rustfmt::skip]
|
||||||
fn serialize_ping_test() {
|
fn serialize_ping_test() {
|
||||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::Ping(100) }),
|
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::Ping(100) }),
|
||||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x70, 0x69, 0x6e, 0x67,
|
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x70, 0x69, 0x6e, 0x67,
|
||||||
|
@ -590,8 +692,8 @@ mod test {
|
||||||
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[rustfmt::skip]
|
||||||
fn serialize_mempool_test() {
|
fn serialize_mempool_test() {
|
||||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::MemPool }),
|
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::MemPool }),
|
||||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x6d, 0x65, 0x6d, 0x70,
|
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x6d, 0x65, 0x6d, 0x70,
|
||||||
|
@ -600,6 +702,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[rustfmt::skip]
|
||||||
fn serialize_getaddr_test() {
|
fn serialize_getaddr_test() {
|
||||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::GetAddr }),
|
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::GetAddr }),
|
||||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61,
|
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61,
|
||||||
|
@ -609,21 +712,27 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_getaddr_test() {
|
fn deserialize_getaddr_test() {
|
||||||
let msg = deserialize(
|
#[rustfmt::skip]
|
||||||
&[0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61,
|
let msg = deserialize(&[
|
||||||
|
0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61,
|
||||||
0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]);
|
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2
|
||||||
let preimage = RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::GetAddr };
|
]);
|
||||||
|
let preimage = RawNetworkMessage {
|
||||||
|
magic: Magic::from(Network::Bitcoin),
|
||||||
|
payload: NetworkMessage::GetAddr,
|
||||||
|
};
|
||||||
assert!(msg.is_ok());
|
assert!(msg.is_ok());
|
||||||
let msg : RawNetworkMessage = msg.unwrap();
|
let msg: RawNetworkMessage = msg.unwrap();
|
||||||
assert_eq!(preimage.magic, msg.magic);
|
assert_eq!(preimage.magic, msg.magic);
|
||||||
assert_eq!(preimage.payload, msg.payload);
|
assert_eq!(preimage.payload, msg.payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_version_test() {
|
fn deserialize_version_test() {
|
||||||
let msg = deserialize::<RawNetworkMessage>(
|
#[rustfmt::skip]
|
||||||
&[ 0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x73,
|
let msg = deserialize::<RawNetworkMessage>(&[
|
||||||
|
0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x73,
|
||||||
0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x66, 0x00, 0x00, 0x00, 0xbe, 0x61, 0xb8, 0x27,
|
0x66, 0x00, 0x00, 0x00, 0xbe, 0x61, 0xb8, 0x27,
|
||||||
0x7f, 0x11, 0x01, 0x00, 0x0d, 0x04, 0x00, 0x00,
|
0x7f, 0x11, 0x01, 0x00, 0x0d, 0x04, 0x00, 0x00,
|
||||||
|
@ -638,14 +747,21 @@ mod test {
|
||||||
0xfa, 0xa9, 0x95, 0x59, 0xcc, 0x68, 0xa1, 0xc1,
|
0xfa, 0xa9, 0x95, 0x59, 0xcc, 0x68, 0xa1, 0xc1,
|
||||||
0x10, 0x2f, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68,
|
0x10, 0x2f, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68,
|
||||||
0x69, 0x3a, 0x30, 0x2e, 0x31, 0x37, 0x2e, 0x31,
|
0x69, 0x3a, 0x30, 0x2e, 0x31, 0x37, 0x2e, 0x31,
|
||||||
0x2f, 0x93, 0x8c, 0x08, 0x00, 0x01 ]);
|
0x2f, 0x93, 0x8c, 0x08, 0x00, 0x01
|
||||||
|
]);
|
||||||
|
|
||||||
assert!(msg.is_ok());
|
assert!(msg.is_ok());
|
||||||
let msg = msg.unwrap();
|
let msg = msg.unwrap();
|
||||||
assert_eq!(msg.magic, Magic::from(Network::Bitcoin));
|
assert_eq!(msg.magic, Magic::from(Network::Bitcoin));
|
||||||
if let NetworkMessage::Version(version_msg) = msg.payload {
|
if let NetworkMessage::Version(version_msg) = msg.payload {
|
||||||
assert_eq!(version_msg.version, 70015);
|
assert_eq!(version_msg.version, 70015);
|
||||||
assert_eq!(version_msg.services, ServiceFlags::NETWORK | ServiceFlags::BLOOM | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED);
|
assert_eq!(
|
||||||
|
version_msg.services,
|
||||||
|
ServiceFlags::NETWORK
|
||||||
|
| ServiceFlags::BLOOM
|
||||||
|
| ServiceFlags::WITNESS
|
||||||
|
| ServiceFlags::NETWORK_LIMITED
|
||||||
|
);
|
||||||
assert_eq!(version_msg.timestamp, 1548554224);
|
assert_eq!(version_msg.timestamp, 1548554224);
|
||||||
assert_eq!(version_msg.nonce, 13952548347456104954);
|
assert_eq!(version_msg.nonce, 13952548347456104954);
|
||||||
assert_eq!(version_msg.user_agent, "/Satoshi:0.17.1/");
|
assert_eq!(version_msg.user_agent, "/Satoshi:0.17.1/");
|
||||||
|
@ -658,7 +774,9 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_partial_message_test() {
|
fn deserialize_partial_message_test() {
|
||||||
let data = [ 0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x73,
|
#[rustfmt::skip]
|
||||||
|
let data = [
|
||||||
|
0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x73,
|
||||||
0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x66, 0x00, 0x00, 0x00, 0xbe, 0x61, 0xb8, 0x27,
|
0x66, 0x00, 0x00, 0x00, 0xbe, 0x61, 0xb8, 0x27,
|
||||||
0x7f, 0x11, 0x01, 0x00, 0x0d, 0x04, 0x00, 0x00,
|
0x7f, 0x11, 0x01, 0x00, 0x0d, 0x04, 0x00, 0x00,
|
||||||
|
@ -673,7 +791,8 @@ mod test {
|
||||||
0xfa, 0xa9, 0x95, 0x59, 0xcc, 0x68, 0xa1, 0xc1,
|
0xfa, 0xa9, 0x95, 0x59, 0xcc, 0x68, 0xa1, 0xc1,
|
||||||
0x10, 0x2f, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68,
|
0x10, 0x2f, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68,
|
||||||
0x69, 0x3a, 0x30, 0x2e, 0x31, 0x37, 0x2e, 0x31,
|
0x69, 0x3a, 0x30, 0x2e, 0x31, 0x37, 0x2e, 0x31,
|
||||||
0x2f, 0x93, 0x8c, 0x08, 0x00, 0x01, 0, 0 ];
|
0x2f, 0x93, 0x8c, 0x08, 0x00, 0x01, 0x00, 0x00
|
||||||
|
];
|
||||||
let msg = deserialize_partial::<RawNetworkMessage>(&data);
|
let msg = deserialize_partial::<RawNetworkMessage>(&data);
|
||||||
assert!(msg.is_ok());
|
assert!(msg.is_ok());
|
||||||
|
|
||||||
|
@ -682,7 +801,13 @@ mod test {
|
||||||
assert_eq!(msg.magic, Magic::from(Network::Bitcoin));
|
assert_eq!(msg.magic, Magic::from(Network::Bitcoin));
|
||||||
if let NetworkMessage::Version(version_msg) = msg.payload {
|
if let NetworkMessage::Version(version_msg) = msg.payload {
|
||||||
assert_eq!(version_msg.version, 70015);
|
assert_eq!(version_msg.version, 70015);
|
||||||
assert_eq!(version_msg.services, ServiceFlags::NETWORK | ServiceFlags::BLOOM | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED);
|
assert_eq!(
|
||||||
|
version_msg.services,
|
||||||
|
ServiceFlags::NETWORK
|
||||||
|
| ServiceFlags::BLOOM
|
||||||
|
| ServiceFlags::WITNESS
|
||||||
|
| ServiceFlags::NETWORK_LIMITED
|
||||||
|
);
|
||||||
assert_eq!(version_msg.timestamp, 1548554224);
|
assert_eq!(version_msg.timestamp, 1548554224);
|
||||||
assert_eq!(version_msg.nonce, 13952548347456104954);
|
assert_eq!(version_msg.nonce, 13952548347456104954);
|
||||||
assert_eq!(version_msg.user_agent, "/Satoshi:0.17.1/");
|
assert_eq!(version_msg.user_agent, "/Satoshi:0.17.1/");
|
||||||
|
|
|
@ -7,16 +7,13 @@
|
||||||
//! Bitcoin data (blocks and transactions) around.
|
//! Bitcoin data (blocks and transactions) around.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use crate::io;
|
|
||||||
|
|
||||||
use crate::hashes::{Hash as _, sha256d};
|
|
||||||
|
|
||||||
use crate::network::constants;
|
|
||||||
use crate::consensus::encode::{self, Decodable, Encodable};
|
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||||
use crate::hash_types::{BlockHash, Txid, Wtxid};
|
use crate::hash_types::{BlockHash, Txid, Wtxid};
|
||||||
|
use crate::hashes::{sha256d, Hash as _};
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::internal_macros::impl_consensus_encoding;
|
||||||
|
use crate::io;
|
||||||
|
use crate::network::constants;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// An inventory item.
|
/// An inventory item.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
|
||||||
|
@ -41,7 +38,7 @@ pub enum Inventory {
|
||||||
inv_type: u32,
|
inv_type: u32,
|
||||||
/// The hash of the inventory item
|
/// The hash of the inventory item
|
||||||
hash: [u8; 32],
|
hash: [u8; 32],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for Inventory {
|
impl Encodable for Inventory {
|
||||||
|
@ -50,7 +47,7 @@ impl Encodable for Inventory {
|
||||||
macro_rules! encode_inv {
|
macro_rules! encode_inv {
|
||||||
($code:expr, $item:expr) => {
|
($code:expr, $item:expr) => {
|
||||||
u32::consensus_encode(&$code, w)? + $item.consensus_encode(w)?
|
u32::consensus_encode(&$code, w)? + $item.consensus_encode(w)?
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
Ok(match *self {
|
Ok(match *self {
|
||||||
Inventory::Error => encode_inv!(0, sha256d::Hash::all_zeros()),
|
Inventory::Error => encode_inv!(0, sha256d::Hash::all_zeros()),
|
||||||
|
@ -77,10 +74,7 @@ impl Decodable for Inventory {
|
||||||
5 => Inventory::WTx(Decodable::consensus_decode(r)?),
|
5 => Inventory::WTx(Decodable::consensus_decode(r)?),
|
||||||
0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(r)?),
|
0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(r)?),
|
||||||
0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(r)?),
|
0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(r)?),
|
||||||
tp => Inventory::Unknown {
|
tp => Inventory::Unknown { inv_type: tp, hash: Decodable::consensus_decode(r)? },
|
||||||
inv_type: tp,
|
|
||||||
hash: Decodable::consensus_decode(r)?,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,17 +104,13 @@ pub struct GetHeadersMessage {
|
||||||
/// if possible and block 1 otherwise.
|
/// if possible and block 1 otherwise.
|
||||||
pub locator_hashes: Vec<BlockHash>,
|
pub locator_hashes: Vec<BlockHash>,
|
||||||
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||||
pub stop_hash: BlockHash
|
pub stop_hash: BlockHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetBlocksMessage {
|
impl GetBlocksMessage {
|
||||||
/// Construct a new `getblocks` message
|
/// Construct a new `getblocks` message
|
||||||
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
|
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
|
||||||
GetBlocksMessage {
|
GetBlocksMessage { version: constants::PROTOCOL_VERSION, locator_hashes, stop_hash }
|
||||||
version: constants::PROTOCOL_VERSION,
|
|
||||||
locator_hashes,
|
|
||||||
stop_hash,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,11 +119,7 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
|
||||||
impl GetHeadersMessage {
|
impl GetHeadersMessage {
|
||||||
/// Construct a new `getheaders` message
|
/// Construct a new `getheaders` message
|
||||||
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
|
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
|
||||||
GetHeadersMessage {
|
GetHeadersMessage { version: constants::PROTOCOL_VERSION, locator_hashes, stop_hash }
|
||||||
version: constants::PROTOCOL_VERSION,
|
|
||||||
locator_hashes,
|
|
||||||
stop_hash,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,16 +127,17 @@ impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Vec, GetHeadersMessage, GetBlocksMessage};
|
use super::{GetBlocksMessage, GetHeadersMessage, Vec};
|
||||||
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::hashes::Hash;
|
use crate::hashes::Hash;
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn getblocks_message_test() {
|
fn getblocks_message_test() {
|
||||||
let from_sat = Vec::from_hex("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let from_sat = Vec::from_hex("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
let genhash = Vec::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap();
|
let genhash =
|
||||||
|
Vec::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
|
let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
|
@ -166,7 +153,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn getheaders_message_test() {
|
fn getheaders_message_test() {
|
||||||
let from_sat = Vec::from_hex("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let from_sat = Vec::from_hex("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
let genhash = Vec::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap();
|
let genhash =
|
||||||
|
Vec::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
|
let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
//! This module describes BIP37 Connection Bloom filtering network messages.
|
//! This module describes BIP37 Connection Bloom filtering network messages.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::consensus::encode;
|
|
||||||
use crate::consensus::{Decodable, Encodable, ReadExt};
|
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use crate::consensus::{encode, Decodable, Encodable, ReadExt};
|
||||||
|
use crate::internal_macros::impl_consensus_encoding;
|
||||||
|
|
||||||
/// `filterload` message sets the current bloom filter
|
/// `filterload` message sets the current bloom filter
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct FilterLoad {
|
pub struct FilterLoad {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
//! BIP152 Compact Blocks network messages
|
//! BIP152 Compact Blocks network messages
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
|
||||||
use crate::bip152;
|
use crate::bip152;
|
||||||
|
use crate::internal_macros::impl_consensus_encoding;
|
||||||
|
|
||||||
/// sendcmpct message
|
/// sendcmpct message
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, PartialOrd, Ord, Hash)]
|
#[derive(PartialEq, Eq, Clone, Debug, Copy, PartialOrd, Ord, Hash)]
|
||||||
|
|
|
@ -7,16 +7,13 @@
|
||||||
//! capabilities.
|
//! capabilities.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::consensus::{encode, Decodable, Encodable, ReadExt};
|
||||||
|
|
||||||
use crate::io;
|
|
||||||
|
|
||||||
use crate::network::address::Address;
|
|
||||||
use crate::network::constants::{self, ServiceFlags};
|
|
||||||
use crate::consensus::{Encodable, Decodable, ReadExt};
|
|
||||||
use crate::consensus::encode;
|
|
||||||
use crate::hashes::sha256d;
|
use crate::hashes::sha256d;
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::internal_macros::impl_consensus_encoding;
|
||||||
|
use crate::io;
|
||||||
|
use crate::network::address::Address;
|
||||||
|
use crate::network::constants::{self, ServiceFlags};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// Some simple messages
|
/// Some simple messages
|
||||||
|
|
||||||
|
@ -42,7 +39,7 @@ pub struct VersionMessage {
|
||||||
/// Whether the receiving peer should relay messages to the sender; used
|
/// Whether the receiving peer should relay messages to the sender; used
|
||||||
/// if the sender is bandwidth-limited and would like to support bloom
|
/// if the sender is bandwidth-limited and would like to support bloom
|
||||||
/// filtering. Defaults to false.
|
/// filtering. Defaults to false.
|
||||||
pub relay: bool
|
pub relay: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VersionMessage {
|
impl VersionMessage {
|
||||||
|
@ -70,9 +67,18 @@ impl VersionMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(VersionMessage, version, services, timestamp,
|
impl_consensus_encoding!(
|
||||||
receiver, sender, nonce,
|
VersionMessage,
|
||||||
user_agent, start_height, relay);
|
version,
|
||||||
|
services,
|
||||||
|
timestamp,
|
||||||
|
receiver,
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
user_agent,
|
||||||
|
start_height,
|
||||||
|
relay
|
||||||
|
);
|
||||||
|
|
||||||
/// message rejection reason as a code
|
/// message rejection reason as a code
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
@ -92,7 +98,7 @@ pub enum RejectReason {
|
||||||
/// insufficient fee
|
/// insufficient fee
|
||||||
Fee = 0x42,
|
Fee = 0x42,
|
||||||
/// checkpoint
|
/// checkpoint
|
||||||
Checkpoint = 0x43
|
Checkpoint = 0x43,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for RejectReason {
|
impl Encodable for RejectReason {
|
||||||
|
@ -113,7 +119,7 @@ impl Decodable for RejectReason {
|
||||||
0x41 => RejectReason::Dust,
|
0x41 => RejectReason::Dust,
|
||||||
0x42 => RejectReason::Fee,
|
0x42 => RejectReason::Fee,
|
||||||
0x43 => RejectReason::Checkpoint,
|
0x43 => RejectReason::Checkpoint,
|
||||||
_ => return Err(encode::Error::ParseFailed("unknown reject code"))
|
_ => return Err(encode::Error::ParseFailed("unknown reject code")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,23 +134,19 @@ pub struct Reject {
|
||||||
/// reason of rejectection
|
/// reason of rejectection
|
||||||
pub reason: Cow<'static, str>,
|
pub reason: Cow<'static, str>,
|
||||||
/// reference to rejected item
|
/// reference to rejected item
|
||||||
pub hash: sha256d::Hash
|
pub hash: sha256d::Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(Reject, message, ccode, reason, hash);
|
impl_consensus_encoding!(Reject, message, ccode, reason, hash);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::VersionMessage;
|
use super::{Reject, RejectReason, VersionMessage};
|
||||||
use super::Reject;
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
use super::RejectReason;
|
|
||||||
|
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::hashes::sha256d::Hash;
|
use crate::hashes::sha256d::Hash;
|
||||||
use crate::network::constants::ServiceFlags;
|
use crate::network::constants::ServiceFlags;
|
||||||
|
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn version_message_test() {
|
fn version_message_test() {
|
||||||
// This message is from my satoshi node, morning of May 27 2014
|
// This message is from my satoshi node, morning of May 27 2014
|
||||||
|
@ -181,7 +183,8 @@ mod tests {
|
||||||
assert_eq!(RejectReason::Duplicate, conflict.ccode);
|
assert_eq!(RejectReason::Duplicate, conflict.ccode);
|
||||||
assert_eq!("txn-mempool-conflict", conflict.reason);
|
assert_eq!("txn-mempool-conflict", conflict.reason);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Hash::from_hex("0470f4f2dc4191221b59884bcffaaf00932748ab46356a80413c0b86d354df05").unwrap(),
|
Hash::from_hex("0470f4f2dc4191221b59884bcffaaf00932748ab46356a80413c0b86d354df05")
|
||||||
|
.unwrap(),
|
||||||
conflict.hash
|
conflict.hash
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -190,7 +193,8 @@ mod tests {
|
||||||
assert_eq!(RejectReason::NonStandard, nonfinal.ccode);
|
assert_eq!(RejectReason::NonStandard, nonfinal.ccode);
|
||||||
assert_eq!("non-final", nonfinal.reason);
|
assert_eq!("non-final", nonfinal.reason);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Hash::from_hex("0b46a539138b5fde4e341b37f2d945c23d41193b30caa7fcbd8bdb836cbe9b25").unwrap(),
|
Hash::from_hex("0b46a539138b5fde4e341b37f2d945c23d41193b30caa7fcbd8bdb836cbe9b25")
|
||||||
|
.unwrap(),
|
||||||
nonfinal.hash
|
nonfinal.hash
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub mod message_bloom;
|
||||||
pub mod message_compact_blocks;
|
pub mod message_compact_blocks;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
pub mod message_network;
|
pub mod message_filter;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
pub mod message_filter;
|
pub mod message_network;
|
||||||
|
|
|
@ -8,20 +8,21 @@
|
||||||
//! 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]
|
||||||
pub(crate) const UINT256_ONE: [u8; 32] = [
|
pub(crate) const UINT256_ONE: [u8; 32] = [
|
||||||
1, 0, 0, 0, 0, 0, 0, 0,
|
1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
@ -31,7 +32,7 @@ pub(crate) const UINT256_ONE: [u8; 32] = [
|
||||||
|
|
||||||
/// Efficiently calculates signature hash message for legacy, segwit and taproot inputs.
|
/// Efficiently calculates signature hash message for legacy, segwit and taproot inputs.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SighashCache<T: Deref<Target=Transaction>> {
|
pub struct SighashCache<T: Deref<Target = Transaction>> {
|
||||||
/// Access to transaction required for transaction introspection. Moreover, type
|
/// Access to transaction required for transaction introspection. Moreover, type
|
||||||
/// `T: Deref<Target=Transaction>` allows us to use borrowed and mutable borrowed types,
|
/// `T: Deref<Target=Transaction>` allows us to use borrowed and mutable borrowed types,
|
||||||
/// the latter in particular is necessary for [`SighashCache::witness_mut`].
|
/// the latter in particular is necessary for [`SighashCache::witness_mut`].
|
||||||
|
@ -76,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.
|
||||||
|
@ -149,7 +153,7 @@ impl str::FromStr for SchnorrSighashType {
|
||||||
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::AllPlusAnyoneCanPay),
|
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::AllPlusAnyoneCanPay),
|
||||||
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::NonePlusAnyoneCanPay),
|
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::NonePlusAnyoneCanPay),
|
||||||
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::SinglePlusAnyoneCanPay),
|
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::SinglePlusAnyoneCanPay),
|
||||||
_ => Err(SighashTypeParseError{ unrecognized: s.to_owned() }),
|
_ => Err(SighashTypeParseError { unrecognized: s.to_owned() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,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() {
|
||||||
|
@ -251,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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,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)
|
||||||
|
@ -290,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.
|
||||||
|
@ -315,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");
|
||||||
|
@ -359,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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,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
|
||||||
|
@ -541,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:
|
||||||
|
@ -576,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)?;
|
||||||
|
@ -725,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(),
|
||||||
})?;
|
})?;
|
||||||
|
@ -835,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 {
|
||||||
|
@ -857,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(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -867,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)?;
|
||||||
|
@ -888,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())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -919,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))
|
||||||
|
@ -940,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 {
|
||||||
|
@ -978,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),
|
||||||
|
@ -999,7 +992,7 @@ impl<R: Deref<Target = Transaction>> SighashCache<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: DerefMut<Target=Transaction>> SighashCache<R> {
|
impl<R: DerefMut<Target = Transaction>> SighashCache<R> {
|
||||||
/// When the `SighashCache` is initialized with a mutable reference to a transaction instead of
|
/// When the `SighashCache` is initialized with a mutable reference to a transaction instead of
|
||||||
/// a regular reference, this method is available to allow modification to the witnesses.
|
/// a regular reference, this method is available to allow modification to the witnesses.
|
||||||
///
|
///
|
||||||
|
@ -1025,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`.
|
||||||
|
@ -1045,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> {
|
||||||
|
@ -1063,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;
|
||||||
|
|
||||||
|
@ -1107,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();
|
||||||
|
@ -1268,6 +1263,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[rustfmt::skip] // Allow long function call `taproot_signature_hash`.
|
||||||
fn test_sighash_errors() {
|
fn test_sighash_errors() {
|
||||||
let dumb_tx = Transaction {
|
let dumb_tx = Transaction {
|
||||||
version: 0,
|
version: 0,
|
||||||
|
@ -1360,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
|
||||||
|
@ -1387,18 +1381,18 @@ 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());
|
||||||
let amt = utxo["amountSats"].as_u64().unwrap();
|
let amt = utxo["amountSats"].as_u64().unwrap();
|
||||||
utxos.push(TxOut {value: amt, script_pubkey: spk });
|
utxos.push(TxOut { value: amt, script_pubkey: spk });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test intermediary
|
// Test intermediary
|
||||||
|
@ -1418,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)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1445,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]);
|
||||||
|
@ -1511,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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1531,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]
|
||||||
|
@ -1562,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]
|
||||||
|
@ -1605,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")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
ignore = [
|
ignore = [
|
||||||
"bitcoin/src/blockdata",
|
"bitcoin/src/blockdata",
|
||||||
"bitcoin/src/consensus",
|
"bitcoin/src/consensus",
|
||||||
"bitcoin/src/network",
|
|
||||||
"bitcoin/src/psbt",
|
"bitcoin/src/psbt",
|
||||||
"bitcoin/src/util",
|
"bitcoin/src/util",
|
||||||
"hashes",
|
"hashes",
|
||||||
|
|
Loading…
Reference in New Issue