diff --git a/Cargo.toml b/Cargo.toml index ea6694d..3b4f701 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ path = "src/lib.rs" [features] fuzztarget = ["secp256k1/fuzztarget", "bitcoin_hashes/fuzztarget"] -serde-decimal = ["use-serde", "strason"] unstable = [] use-serde = ["serde", "bitcoin_hashes/serde"] @@ -32,15 +31,6 @@ version = "1" features = ["derive"] optional = true -[dependencies.serde_test] -version = "1" -optional = true - -[dependencies.strason] -version = "0.4" -optional = true -default-features = false - [dependencies.hex] version = "=0.3.2" diff --git a/contrib/test.sh b/contrib/test.sh index bdab184..53ed283 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -1,6 +1,6 @@ #!/bin/sh -ex -FEATURES="bitcoinconsensus use-serde serde-decimal" +FEATURES="bitcoinconsensus use-serde" # Use toolchain if explicitly specified if [ -n "$TOOLCHAIN" ] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 16641da..4b3941d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -14,7 +14,7 @@ honggfuzz_fuzz = ["honggfuzz"] [dependencies] honggfuzz = { version = "0.5", optional = true } afl = { version = "0.4", optional = true } -bitcoin = { path = "..", features = ["fuzztarget", "serde-decimal"] } +bitcoin = { path = "..", features = ["fuzztarget"] } # Prevent this from interfering with workspaces [workspace] @@ -40,14 +40,6 @@ path = "fuzz_targets/deserialize_address.rs" name = "deserialize_amount" path = "fuzz_targets/deserialize_amount.rs" -[[bin]] -name = "deserialize_decimal" -path = "fuzz_targets/deserialize_decimal.rs" - -[[bin]] -name = "deserialize_udecimal" -path = "fuzz_targets/deserialize_udecimal.rs" - [[bin]] name = "outpoint_string" path = "fuzz_targets/outpoint_string.rs" diff --git a/fuzz/fuzz_targets/deserialize_decimal.rs b/fuzz/fuzz_targets/deserialize_decimal.rs deleted file mode 100644 index 78e3c60..0000000 --- a/fuzz/fuzz_targets/deserialize_decimal.rs +++ /dev/null @@ -1,61 +0,0 @@ -extern crate bitcoin; -use std::str::FromStr; -fn do_test(data: &[u8]) { - let data_str = String::from_utf8_lossy(data); - let dec = match bitcoin::util::decimal::Decimal::from_str(&data_str) { - Ok(dec) => dec, - Err(_) => return, - }; - let dec_roundtrip = match bitcoin::util::decimal::Decimal::from_str(&dec.to_string()) { - Ok(dec) => dec, - Err(_) => return, - }; - assert_eq!(dec, dec_roundtrip); -} - -#[cfg(feature = "afl")] -#[macro_use] extern crate afl; -#[cfg(feature = "afl")] -fn main() { - fuzz!(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} - -#[cfg(test)] -mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } - - #[test] - fn duplicate_crash() { - let mut a = Vec::new(); - extend_vec_from_hex("00000000", &mut a); - super::do_test(&a); - } -} diff --git a/fuzz/fuzz_targets/deserialize_udecimal.rs b/fuzz/fuzz_targets/deserialize_udecimal.rs deleted file mode 100644 index 1451ad2..0000000 --- a/fuzz/fuzz_targets/deserialize_udecimal.rs +++ /dev/null @@ -1,61 +0,0 @@ -extern crate bitcoin; -use std::str::FromStr; -fn do_test(data: &[u8]) { - let data_str = String::from_utf8_lossy(data); - let dec = match bitcoin::util::decimal::UDecimal::from_str(&data_str) { - Ok(dec) => dec, - Err(_) => return, - }; - let dec_roundtrip = match bitcoin::util::decimal::UDecimal::from_str(&dec.to_string()) { - Ok(dec) => dec, - Err(_) => return, - }; - assert_eq!(dec, dec_roundtrip); -} - -#[cfg(feature = "afl")] -#[macro_use] extern crate afl; -#[cfg(feature = "afl")] -fn main() { - fuzz!(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} - -#[cfg(test)] -mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } - - #[test] - fn duplicate_crash() { - let mut a = Vec::new(); - extend_vec_from_hex("00000000", &mut a); - super::do_test(&a); - } -} diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 39ccc7f..738229f 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -805,15 +805,14 @@ mod test { } #[test] - #[cfg(all(feature = "serde", feature = "strason"))] + #[cfg(feature = "serde")] fn script_json_serialize() { - use strason::Json; + use serde_json; let original = hex_script!("827651a0698faaa9a8a7a687"); - let json = Json::from_serialize(&original).unwrap(); - assert_eq!(json.to_bytes(), b"\"827651a0698faaa9a8a7a687\""); - assert_eq!(json.string(), Some("827651a0698faaa9a8a7a687")); - let des = json.into_deserialize().unwrap(); + let json = serde_json::to_value(&original).unwrap(); + assert_eq!(json, serde_json::Value::String("827651a0698faaa9a8a7a687".to_owned())); + let des = serde_json::from_value(json).unwrap(); assert_eq!(original, des); } diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index e82aab7..d0d1830 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -598,9 +598,6 @@ impl SigHashType { #[cfg(test)] mod tests { - #[cfg(all(feature = "serde", feature = "strason"))] - use strason::Json; - use super::{OutPoint, ParseOutPointError, Transaction, TxIn}; use std::str::FromStr; @@ -771,14 +768,11 @@ mod tests { } #[test] - #[cfg(all(feature = "serde", feature = "strason"))] + #[cfg(feature = "serde")] fn test_txn_encode_decode() { let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap(); let tx: Transaction = deserialize(&hex_tx).unwrap(); - - let encoded = Json::from_serialize(&tx).unwrap(); - let decoded = encoded.into_deserialize().unwrap(); - assert_eq!(tx, decoded); + serde_round_trip!(tx); } fn run_test_sighash(tx: &str, script: &str, input_index: usize, hash_type: i32, expected_result: &str) { @@ -795,15 +789,12 @@ mod tests { // Test decoding transaction `4be105f158ea44aec57bf12c5817d073a712ab131df6f37786872cfc70734188` // from testnet, which is the first BIP144-encoded transaction I encountered. #[test] - #[cfg(all(feature = "serde", feature = "strason"))] + #[cfg(feature = "serde")] fn test_segwit_tx_decode() { let hex_tx = hex_bytes("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3603da1b0e00045503bd5704c7dd8a0d0ced13bb5785010800000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542fffffffff02b4e5a212000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9edf91c46b49eb8a29089980f02ee6b57e7d63d33b18b4fddac2bcd7db2a39837040120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); let tx: Transaction = deserialize(&hex_tx).unwrap(); assert_eq!(tx.get_weight(), 780); - - let encoded = Json::from_serialize(&tx).unwrap(); - let decoded = encoded.into_deserialize().unwrap(); - assert_eq!(tx, decoded); + serde_round_trip!(tx); let consensus_encoded = serialize(&tx); assert_eq!(consensus_encoded, hex_tx); diff --git a/src/lib.rs b/src/lib.rs index 590c4c9..6f1297e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,6 @@ extern crate hex; extern crate rand; extern crate secp256k1; #[cfg(feature = "serde")] extern crate serde; -#[cfg(feature = "strason")] extern crate strason; #[cfg(all(test, feature = "serde"))] #[macro_use] extern crate serde_derive; // for 1.22.0 compat #[cfg(all(test, feature = "serde"))] extern crate serde_json; #[cfg(all(test, feature = "serde"))] extern crate serde_test; @@ -84,5 +83,3 @@ pub use util::amount::SignedAmount; pub use util::hash::BitcoinHash; pub use util::key::PrivateKey; pub use util::key::PublicKey; -pub use util::decimal::Decimal; -pub use util::decimal::UDecimal; diff --git a/src/test_macros.rs b/src/test_macros.rs index 6bf5e3d..11c4622 100644 --- a/src/test_macros.rs +++ b/src/test_macros.rs @@ -16,15 +16,14 @@ //! //! Internal macros used for unit tests -#[cfg(all(feature = "serde", feature = "strason"))] +#[cfg(feature = "serde")] macro_rules! serde_round_trip ( ($var:expr) => ({ - use $crate::strason::Json; + use serde_json; - let start = $var; - let encoded = Json::from_serialize(&start).unwrap(); - let decoded = encoded.into_deserialize().unwrap(); - assert_eq!(start, decoded); + let encoded = serde_json::to_value(&$var).unwrap(); + let decoded = serde_json::from_value(encoded).unwrap(); + assert_eq!($var, decoded); }) ); diff --git a/src/util/address.rs b/src/util/address.rs index 16fb3e6..240eeea 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -500,14 +500,14 @@ mod tests { } #[test] - #[cfg(all(feature = "serde", feature = "strason"))] + #[cfg(feature = "serde")] fn test_json_serialize() { - use strason::Json; + use serde_json; let addr = Address::from_str("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM").unwrap(); - let json = Json::from_serialize(&addr).unwrap(); - assert_eq!(json.string(), Some("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM")); - let into: Address = json.into_deserialize().unwrap(); + let json = serde_json::to_value(&addr).unwrap(); + assert_eq!(json, serde_json::Value::String("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".to_owned())); + let into: Address = serde_json::from_value(json).unwrap(); assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), @@ -515,9 +515,9 @@ mod tests { ); let addr = Address::from_str("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k").unwrap(); - let json = Json::from_serialize(&addr).unwrap(); - assert_eq!(json.string(), Some("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k")); - let into: Address = json.into_deserialize().unwrap(); + let json = serde_json::to_value(&addr).unwrap(); + assert_eq!(json, serde_json::Value::String("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k".to_owned())); + let into: Address = serde_json::from_value(json).unwrap(); assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), @@ -525,9 +525,9 @@ mod tests { ); let addr = Address::from_str("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7").unwrap(); - let json = Json::from_serialize(&addr).unwrap(); - assert_eq!(json.string(), Some("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7")); - let into: Address = json.into_deserialize().unwrap(); + let json = serde_json::to_value(&addr).unwrap(); + assert_eq!(json, serde_json::Value::String("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7".to_owned())); + let into: Address = serde_json::from_value(json).unwrap(); assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), @@ -535,9 +535,9 @@ mod tests { ); let addr = Address::from_str("bcrt1q2nfxmhd4n3c8834pj72xagvyr9gl57n5r94fsl").unwrap(); - let json = Json::from_serialize(&addr).unwrap(); - assert_eq!(json.string(), Some("bcrt1q2nfxmhd4n3c8834pj72xagvyr9gl57n5r94fsl")); - let into: Address = json.into_deserialize().unwrap(); + let json = serde_json::to_value(&addr).unwrap(); + assert_eq!(json, serde_json::Value::String("bcrt1q2nfxmhd4n3c8834pj72xagvyr9gl57n5r94fsl".to_owned())); + let into: Address = serde_json::from_value(json).unwrap(); assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), diff --git a/src/util/bip32.rs b/src/util/bip32.rs index 34c2067..926e1c8 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -988,7 +988,7 @@ mod tests { } #[test] - #[cfg(all(feature = "serde", feature = "strason"))] + #[cfg(feature = "serde")] pub fn encode_decode_childnumber() { serde_round_trip!(ChildNumber::from_normal_idx(0).unwrap()); serde_round_trip!(ChildNumber::from_normal_idx(1).unwrap()); diff --git a/src/util/decimal.rs b/src/util/decimal.rs deleted file mode 100644 index ab308eb..0000000 --- a/src/util/decimal.rs +++ /dev/null @@ -1,604 +0,0 @@ -// Rust Bitcoin Library -// Written in 2014 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! Floating-point decimal type -//! -//! `i64`-based floating-point decimal type designed to hold Bitcoin -//! amounts. For satoshi amounts (8 decimal places) the maximum -//! amounts that can be represented is ~92.25bn, well over the 21m -//! maximum number of bitcoin in existence. Be aware that some -//! altcoins with different granularity may require a wider type. -//! - -use std::{fmt, ops}; -#[cfg(feature = "serde-decimal")] use std::error; -#[cfg(feature = "serde-decimal")] use std::str::FromStr; - -#[cfg(feature = "serde-decimal")] use serde; -#[cfg(feature = "serde-decimal")] use strason::{self, Json}; - -/// A fixed-point decimal type -#[derive(Copy, Clone, Debug, Eq, Ord)] -pub struct Decimal { - mantissa: i64, - exponent: usize, -} - -/// Unsigned fixed-point decimal type -#[derive(Copy, Clone, Debug, Eq, Ord)] -pub struct UDecimal { - mantissa: u64, - exponent: usize, -} - -impl PartialEq for Decimal { - fn eq(&self, other: &Decimal) -> bool { - use std::cmp::max; - let exp = max(self.exponent(), other.exponent()); - self.integer_value(exp) == other.integer_value(exp) - } -} - -impl PartialOrd for Decimal { - fn partial_cmp(&self, other: &Decimal) -> Option<::std::cmp::Ordering> { - use std::cmp::max; - let exp = max(self.exponent(), other.exponent()); - self.integer_value(exp).partial_cmp(&other.integer_value(exp)) - } -} - -impl fmt::Display for Decimal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let ten = 10i64.pow(self.exponent as u32); - let int_part = self.mantissa / ten; - let dec_part = (self.mantissa % ten).abs(); - if int_part == 0 && self.mantissa < 0 { - write!(f, "-{}.{:02$}", int_part, dec_part, self.exponent) - } else { - write!(f, "{}.{:02$}", int_part, dec_part, self.exponent) - } - } -} - -impl ops::Add for Decimal { - type Output = Decimal; - - #[inline] - fn add(self, other: Decimal) -> Decimal { - if self.exponent > other.exponent { - Decimal { - mantissa: other.mantissa * 10i64.pow((self.exponent - other.exponent) as u32) + self.mantissa, - exponent: self.exponent - } - } else { - Decimal { - mantissa: self.mantissa * 10i64.pow((other.exponent - self.exponent) as u32) + other.mantissa, - exponent: other.exponent - } - } - } -} - -impl ops::Neg for Decimal { - type Output = Decimal; - #[inline] - fn neg(self) -> Decimal { Decimal { mantissa: -self.mantissa, exponent: self.exponent } } -} - -impl ops::Sub for Decimal { - type Output = Decimal; - #[inline] - fn sub(self, other: Decimal) -> Decimal { self + (-other) } -} - -impl Decimal { - /// Creates a new Decimal - pub fn new(mantissa: i64, exponent: usize) -> Decimal { - Decimal { - mantissa: mantissa, - exponent: exponent - } - } - - /// Returns the mantissa - #[inline] - pub fn mantissa(&self) -> i64 { self.mantissa } - /// Returns the exponent - #[inline] - pub fn exponent(&self) -> usize { self.exponent } - - /// Get the decimal's value in an integer type, by multiplying - /// by some power of ten to ensure the returned value is 10 ** - /// `exponent` types the actual value. - pub fn integer_value(&self, exponent: usize) -> i64 { - if exponent < self.exponent { - self.mantissa / 10i64.pow((self.exponent - exponent) as u32) - } else { - self.mantissa * 10i64.pow((exponent - self.exponent) as u32) - } - } - - /// Returns whether or not the number is nonnegative - #[inline] - pub fn nonnegative(&self) -> bool { self.mantissa >= 0 } - - // Converts a JSON number to a Decimal previously parsed by strason - #[cfg(feature = "serde-decimal")] - fn parse_decimal(s: &str) -> Result { - // We know this will be a well-formed Json number, so we can - // be pretty lax about parsing - let mut negative = false; - let mut past_dec = false; - let mut exponent = 0; - let mut mantissa = 0i64; - - for b in s.as_bytes() { - match *b { - b'-' => { negative = true; } - b'0'...b'9' => { - match 10i64.checked_mul(mantissa) { - None => return Err(ParseDecimalError::TooBig), - Some(n) => { - match n.checked_add((b - b'0') as i64) { - None => return Err(ParseDecimalError::TooBig), - Some(n) => mantissa = n, - } - } - } - if past_dec { - exponent += 1; - if exponent > 18 { - return Err(ParseDecimalError::TooBig); - } - } - } - b'.' => { past_dec = true; } - _ => { /* whitespace or something, just ignore it */ } - } - } - if negative { mantissa *= -1; } - Ok(Decimal { - mantissa: mantissa, - exponent: exponent, - }) - } -} - -#[cfg(feature = "serde-decimal")] -impl FromStr for Decimal { - type Err = ParseDecimalError; - - /// Parses a `Decimal` from the given amount string. - fn from_str(s: &str) -> Result { - Json::from_str(s)? - .num() - .ok_or(ParseDecimalError::NotANumber) - .and_then(Decimal::parse_decimal) - } -} - -#[cfg(feature = "serde-decimal")] -impl<'de> serde::Deserialize<'de> for Decimal { - /// Deserialize a `Decimal`. - /// - /// This type is deserialized through [`strason`][1] for the same reason as - /// it's explained in the `Serialize` implementation. - /// - /// [1]: https://github.com/apoelstra/strason - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de; - - Json::deserialize(deserializer)? - .num() - .ok_or(de::Error::custom("expected decimal, got non-numeric")) - .and_then(|s| Decimal::parse_decimal(s).map_err(de::Error::custom)) - } -} - -#[cfg(feature = "serde-decimal")] -impl serde::Serialize for Decimal { - /// Serialize a `Decimal`. - /// - /// This type is serialized through [`strason`][1] since it will not lose - /// precision (when serializing to [`strason`][1] itself, the value will be - /// passed through; otherwise it will be encoded as a string). - /// - /// [1]: https://github.com/apoelstra/strason - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let json = Json::from_str(&self.to_string()).unwrap(); - json.serialize(serializer) - } -} - -impl PartialEq for UDecimal { - fn eq(&self, other: &UDecimal) -> bool { - use std::cmp::max; - let exp = max(self.exponent(), other.exponent()); - self.integer_value(exp) == other.integer_value(exp) - } -} - -impl PartialOrd for UDecimal { - fn partial_cmp(&self, other: &UDecimal) -> Option<::std::cmp::Ordering> { - use std::cmp::max; - let exp = max(self.exponent(), other.exponent()); - self.integer_value(exp).partial_cmp(&other.integer_value(exp)) - } -} - -impl fmt::Display for UDecimal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let ten = 10u64.pow(self.exponent as u32); - let int_part = self.mantissa / ten; - let dec_part = self.mantissa % ten; - write!(f, "{}.{:02$}", int_part, dec_part, self.exponent) - } -} - -impl ops::Add for UDecimal { - type Output = UDecimal; - - #[inline] - fn add(self, other: UDecimal) -> UDecimal { - if self.exponent > other.exponent { - UDecimal { - mantissa: other.mantissa * 10u64.pow((self.exponent - other.exponent) as u32) + self.mantissa, - exponent: self.exponent - } - } else { - UDecimal { - mantissa: self.mantissa * 10u64.pow((other.exponent - self.exponent) as u32) + other.mantissa, - exponent: other.exponent - } - } - } -} - -impl UDecimal { - /// Creates a new Decimal - pub fn new(mantissa: u64, exponent: usize) -> UDecimal { - UDecimal { - mantissa: mantissa, - exponent: exponent - } - } - - /// Returns the mantissa - #[inline] - pub fn mantissa(&self) -> u64 { self.mantissa } - /// Returns the exponent - #[inline] - pub fn exponent(&self) -> usize { self.exponent } - - /// Get the decimal's value in an integer type, by multiplying - /// by some power of ten to ensure the returned value is 10 ** - /// `exponent` types the actual value. - pub fn integer_value(&self, exponent: usize) -> u64 { - if exponent < self.exponent { - self.mantissa / 10u64.pow((self.exponent - exponent) as u32) - } else { - self.mantissa * 10u64.pow((exponent - self.exponent) as u32) - } - } - - // Converts a JSON number to a Decimal previously parsed by strason - #[cfg(feature = "serde-decimal")] - fn parse_udecimal(s: &str) -> Result { - // We know this will be a well-formed Json number, so we can - // be pretty lax about parsing - let mut past_dec = false; - let mut exponent = 0; - let mut mantissa = 0u64; - - for b in s.as_bytes() { - match *b { - b'0'...b'9' => { - match 10u64.checked_mul(mantissa) { - None => return Err(ParseDecimalError::TooBig), - Some(n) => { - match n.checked_add((b - b'0') as u64) { - None => return Err(ParseDecimalError::TooBig), - Some(n) => mantissa = n, - } - } - } - if past_dec { - exponent += 1; - if exponent > 18 { - return Err(ParseDecimalError::TooBig); - } - } - } - b'.' => { past_dec = true; } - _ => { /* whitespace or something, just ignore it */ } - } - } - Ok(UDecimal { - mantissa: mantissa, - exponent: exponent, - }) - } -} - -#[cfg(feature = "serde-decimal")] -impl FromStr for UDecimal { - type Err = ParseDecimalError; - - /// Parses a `UDecimal` from the given amount string. - fn from_str(s: &str) -> Result { - Json::from_str(s)? - .num() - .ok_or(ParseDecimalError::NotANumber) - .and_then(UDecimal::parse_udecimal) - } -} - -#[cfg(feature = "serde-decimal")] -impl<'de> serde::Deserialize<'de> for UDecimal { - /// Deserialize an `UDecimal`. - /// - /// This type is deserialized through [`strason`][1] for the same reason as - /// it's explained in the `Serialize` implementation. - /// - /// [1]: https://github.com/apoelstra/strason - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de; - - Json::deserialize(deserializer)? - .num() - .ok_or(de::Error::custom("expected decimal, got non-numeric")) - .and_then(|s| UDecimal::parse_udecimal(s).map_err(de::Error::custom)) - } -} - -#[cfg(feature = "serde-decimal")] -impl serde::Serialize for UDecimal { - /// Serialize an `UDecimal`. - /// - /// This type is serialized through [`strason`][1] since it will not lose - /// precision (when serializing to [`strason`][1] itself, the value will be - /// passed through; otherwise it will be encoded as a string). - /// - /// [1]: https://github.com/apoelstra/strason - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let json = Json::from_str(&self.to_string()).unwrap(); - json.serialize(serializer) - } -} - -/// Errors that occur during `Decimal`/`UDecimal` parsing. -#[cfg(feature = "serde-decimal")] -#[derive(Debug)] -pub enum ParseDecimalError { - /// An error ocurred while parsing the JSON number. - Json(strason::Error), - /// Not a number. - NotANumber, - /// The number is too big to fit in a `Decimal` or `UDecimal`. - TooBig, -} - -#[cfg(feature = "serde-decimal")] -#[doc(hidden)] -impl From for ParseDecimalError { - fn from(e: strason::Error) -> ParseDecimalError { - ParseDecimalError::Json(e) - } -} - -#[cfg(feature = "serde-decimal")] -impl fmt::Display for ParseDecimalError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - ParseDecimalError::Json(ref e) => fmt::Display::fmt(e, fmt), - ParseDecimalError::NotANumber => fmt.write_str("not a valid JSON number"), - ParseDecimalError::TooBig => fmt.write_str("number is too big"), - } - } -} - -#[cfg(feature = "serde-decimal")] -impl error::Error for ParseDecimalError { - fn description(&self) -> &str { - match *self { - ParseDecimalError::Json(ref e) => e.description(), - ParseDecimalError::NotANumber => "not a valid JSON number", - ParseDecimalError::TooBig => "number is too big", - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - ParseDecimalError::Json(ref e) => Some(e), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[cfg(feature = "serde-decimal")] - use strason::Json; - - #[test] - fn integer_value() { - let d = Decimal::new(12345678, 4); - assert_eq!(d.mantissa(), 12345678); - assert_eq!(d.exponent(), 4); - - assert_eq!(d.integer_value(0), 1234); - assert_eq!(d.integer_value(1), 12345); - assert_eq!(d.integer_value(2), 123456); - assert_eq!(d.integer_value(3), 1234567); - assert_eq!(d.integer_value(4), 12345678); - assert_eq!(d.integer_value(5), 123456780); - assert_eq!(d.integer_value(6), 1234567800); - assert_eq!(d.integer_value(7), 12345678000); - assert_eq!(d.integer_value(8), 123456780000); - - let u = UDecimal::new(12345678, 4); - assert_eq!(u.mantissa(), 12345678); - assert_eq!(u.exponent(), 4); - - assert_eq!(u.integer_value(0), 1234); - assert_eq!(u.integer_value(1), 12345); - assert_eq!(u.integer_value(2), 123456); - assert_eq!(u.integer_value(3), 1234567); - assert_eq!(u.integer_value(4), 12345678); - assert_eq!(u.integer_value(5), 123456780); - assert_eq!(u.integer_value(6), 1234567800); - assert_eq!(u.integer_value(7), 12345678000); - assert_eq!(u.integer_value(8), 123456780000); - } - - #[cfg(feature = "serde-decimal")] - macro_rules! deserialize_round_trip( - ($dec:expr, $s:expr) => ({ - let d = $dec; - let encoded = Json::from_serialize(&d).unwrap(); - assert_eq!(encoded, Json::from_reader(&$s[..]).unwrap()); - assert_eq!(encoded.to_bytes(), &$s[..]); - - // hack to force type inference - let mut decoded_res = encoded.into_deserialize(); - if false { decoded_res = Ok($dec); } - let decoded = decoded_res.unwrap(); - assert_eq!(decoded, d); - }) - ); - - #[test] - #[cfg(feature = "serde-decimal")] - fn deserialize() { - deserialize_round_trip!(Decimal::new(0, 0), b"0.0"); - deserialize_round_trip!(UDecimal::new(0, 0), b"0.0"); - - deserialize_round_trip!(Decimal::new(123456789001, 8), b"1234.56789001"); - deserialize_round_trip!(UDecimal::new(123456789001, 8), b"1234.56789001"); - deserialize_round_trip!(Decimal::new(-123456789001, 8), b"-1234.56789001"); - deserialize_round_trip!(Decimal::new(123456789001, 1), b"12345678900.1"); - deserialize_round_trip!(UDecimal::new(123456789001, 1), b"12345678900.1"); - deserialize_round_trip!(Decimal::new(-123456789001, 1), b"-12345678900.1"); - deserialize_round_trip!(Decimal::new(123456789001, 0), b"123456789001.0"); - deserialize_round_trip!(UDecimal::new(123456789001, 0), b"123456789001.0"); - deserialize_round_trip!(Decimal::new(-123456789001, 0), b"-123456789001.0"); - - deserialize_round_trip!(Decimal::new(123400000001, 8), b"1234.00000001"); - deserialize_round_trip!(UDecimal::new(123400000001, 8), b"1234.00000001"); - deserialize_round_trip!(Decimal::new(-123400000001, 8), b"-1234.00000001"); - } - - #[test] - fn equality() { - let d1 = Decimal::new(1234, 8); - let d2 = Decimal::new(12340, 9); - let d3 = Decimal::new(12340, 8); - assert_eq!(d1, d1); - assert_eq!(d1, d2); - assert!(d1 != d3); - assert!(d2 != d3); - - assert!(d1 <= d1); - assert!(d2 <= d2); - assert!(d3 <= d3); - assert!(d1 <= d2); - assert!(d1 <= d3); - assert!(d3 > d1); - assert!(d3 > d2); - } - - #[test] - fn arithmetic() { - let d1 = Decimal::new(5, 1); // 0.5 - let d2 = Decimal::new(-2, 2); // -0.02 - let d3 = Decimal::new(3, 0); // 3.0 - let d4 = Decimal::new(0, 5); // 0.00000 - let u1 = UDecimal::new(5, 1); // 0.5 - let u3 = UDecimal::new(3, 0); // 3.0 - let u4 = UDecimal::new(0, 5); // 0.00000 - - assert!(d1.nonnegative()); - assert!(!d2.nonnegative()); - assert!(d3.nonnegative()); - assert!(d4.nonnegative()); - - assert_eq!(d1 + d2, Decimal::new(48, 2)); - assert_eq!(d1 - d2, Decimal::new(52, 2)); - assert_eq!(d1 + d3, Decimal::new(35, 1)); - assert_eq!(u1 + u3, UDecimal::new(35, 1)); - assert_eq!(d1 - d3, Decimal::new(-25, 1)); - assert_eq!(d2 + d3, Decimal::new(298, 2)); - assert_eq!(d2 - d3, Decimal::new(-302, 2)); - - assert_eq!(d1 + d4, d1); - assert_eq!(u1 + u4, u1); - assert_eq!(d1 - d4, d1); - assert_eq!(d1 + d4, d1 - d4); - assert_eq!(d4 + d4, d4); - assert_eq!(u4 + u4, u4); - } - - #[test] - #[cfg(feature = "serde-decimal")] - fn json_parse() { - let json = Json::from_str("0.00980000").unwrap(); - assert_eq!(json.to_bytes(), b"0.00980000"); - let dec: Decimal = json.into_deserialize().unwrap(); - assert_eq!(dec, Decimal::new(980000, 8)); - - let json = Json::from_str("0.00980000").unwrap(); - assert_eq!(json.to_bytes(), b"0.00980000"); - let dec: UDecimal = json.into_deserialize().unwrap(); - assert_eq!(dec, UDecimal::new(980000, 8)); - - let json = Json::from_str("0.00980").unwrap(); - assert_eq!(json.to_bytes(), b"0.00980"); - let dec: Decimal = json.into_deserialize().unwrap(); - assert_eq!(dec, Decimal::new(98000, 7)); - - let json = Json::from_str("0.00980").unwrap(); - assert_eq!(json.to_bytes(), b"0.00980"); - let dec: UDecimal = json.into_deserialize().unwrap(); - assert_eq!(dec, UDecimal::new(98000, 7)); - } - - #[test] - #[cfg(feature = "serde-decimal")] - fn parse_decimal_udecimal() { - let dec = "0.00980000".parse::().unwrap(); - assert_eq!(dec, Decimal::new(980000, 8)); - - let dec = "0.00980000".parse::().unwrap(); - assert_eq!(dec, UDecimal::new(980000, 8)); - - let dec = "0.00980".parse::().unwrap(); - assert_eq!(dec, Decimal::new(98000, 7)); - - let dec = "0.00980".parse::().unwrap(); - assert_eq!(dec, UDecimal::new(98000, 7)); - } -} diff --git a/src/util/mod.rs b/src/util/mod.rs index c69f30b..3b926f7 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -23,7 +23,6 @@ pub mod base58; pub mod bip32; pub mod bip143; pub mod contracthash; -pub mod decimal; pub mod hash; pub mod misc; pub mod psbt;