Add bitcoin-units crate
Add a new crate `bitcoin-units`, move the `amount` module over to it and re-export all types from `bitcoin::amount` so this as not a breaking change.
This commit is contained in:
parent
4ecb1fe7da
commit
69e56a64ed
|
@ -17,7 +17,6 @@ jobs:
|
|||
matrix:
|
||||
fuzz_target: [
|
||||
bitcoin_deserialize_address,
|
||||
bitcoin_deserialize_amount,
|
||||
bitcoin_deserialize_block,
|
||||
bitcoin_deserialize_prefilled_transaction,
|
||||
bitcoin_deserialize_psbt,
|
||||
|
@ -34,6 +33,7 @@ jobs:
|
|||
hashes_sha256,
|
||||
hashes_sha512_256,
|
||||
hashes_sha512,
|
||||
units_deserialize_amount,
|
||||
]
|
||||
steps:
|
||||
- name: Install test dependencies
|
||||
|
|
|
@ -39,6 +39,7 @@ dependencies = [
|
|||
"bincode",
|
||||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
"bitcoin-units",
|
||||
"bitcoin_hashes",
|
||||
"bitcoinconsensus",
|
||||
"hex-conservative",
|
||||
|
@ -72,6 +73,15 @@ dependencies = [
|
|||
name = "bitcoin-io"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin-units"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.13.0"
|
||||
|
|
|
@ -38,6 +38,7 @@ dependencies = [
|
|||
"bincode",
|
||||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
"bitcoin-units",
|
||||
"bitcoin_hashes",
|
||||
"bitcoinconsensus",
|
||||
"hex-conservative",
|
||||
|
@ -71,6 +72,15 @@ dependencies = [
|
|||
name = "bitcoin-io"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin-units"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.13.0"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["bitcoin", "hashes", "internals", "fuzz", "io"]
|
||||
members = ["bitcoin", "hashes", "internals", "fuzz", "io", "units"]
|
||||
resolver = "2"
|
||||
|
||||
[patch.crates-io.bitcoin]
|
||||
|
@ -13,3 +13,6 @@ path = "internals"
|
|||
|
||||
[patch.crates-io.bitcoin-io]
|
||||
path = "io"
|
||||
|
||||
[patch.crates-io.bitcoin-units]
|
||||
path = "units"
|
||||
|
|
|
@ -15,10 +15,10 @@ exclude = ["tests", "contrib"]
|
|||
|
||||
[features]
|
||||
default = [ "std", "secp-recovery" ]
|
||||
std = ["bech32/std", "hashes/std", "hex/std", "internals/std", "io/std", "secp256k1/std"]
|
||||
std = ["bech32/std", "hashes/std", "hex/std", "internals/std", "io/std", "secp256k1/std", "units/std"]
|
||||
rand-std = ["secp256k1/rand-std", "std"]
|
||||
rand = ["secp256k1/rand"]
|
||||
serde = ["actual-serde", "hashes/serde", "secp256k1/serde", "internals/serde"]
|
||||
serde = ["actual-serde", "hashes/serde", "secp256k1/serde", "internals/serde", "units/serde"]
|
||||
secp-lowmemory = ["secp256k1/lowmemory"]
|
||||
secp-recovery = ["secp256k1/recovery"]
|
||||
bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
|
||||
|
@ -35,6 +35,7 @@ hex_lit = "0.1.1"
|
|||
internals = { package = "bitcoin-internals", version = "0.2.0" }
|
||||
io = { package = "bitcoin-io", version = "0.1", default-features = false, features = ["alloc"] }
|
||||
secp256k1 = { version = "0.28.0", default-features = false, features = ["hashes", "alloc"] }
|
||||
units = { package = "bitcoin-units", version = "0.1.0", default-features = false, features = ["alloc"] }
|
||||
|
||||
base64 = { version = "0.21.3", optional = true }
|
||||
# Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
|
||||
|
|
|
@ -35,3 +35,6 @@ path = "../../internals"
|
|||
|
||||
[patch.crates-io.bitcoin-io]
|
||||
path = "../../io"
|
||||
|
||||
[patch.crates-io.bitcoin-units]
|
||||
path = "../../units"
|
||||
|
|
|
@ -91,7 +91,6 @@ mod serde_utils;
|
|||
#[macro_use]
|
||||
pub mod p2p;
|
||||
pub mod address;
|
||||
pub mod amount;
|
||||
pub mod base58;
|
||||
pub mod bip152;
|
||||
pub mod bip158;
|
||||
|
@ -187,3 +186,35 @@ mod prelude {
|
|||
|
||||
pub use hex::DisplayHex;
|
||||
}
|
||||
|
||||
pub mod amount {
|
||||
//! Bitcoin amounts.
|
||||
//!
|
||||
//! This module mainly introduces the [Amount] and [SignedAmount] types.
|
||||
//! We refer to the documentation on the types for more information.
|
||||
|
||||
use crate::consensus::{encode, Decodable, Encodable};
|
||||
use crate::io;
|
||||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[doc(inline)]
|
||||
pub use units::amount::{
|
||||
Amount, CheckedSum, Denomination, Display, ParseAmountError, SignedAmount,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
pub use units::amount::serde;
|
||||
|
||||
impl Decodable for Amount {
|
||||
#[inline]
|
||||
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||
Ok(Amount::from_sat(Decodable::consensus_decode(r)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Amount {
|
||||
#[inline]
|
||||
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
self.to_sat().consensus_encode(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
set -ex
|
||||
|
||||
CRATES="bitcoin hashes internals fuzz"
|
||||
CRATES="bitcoin hashes units internals fuzz"
|
||||
DEPS="recent minimal"
|
||||
|
||||
for dep in $DEPS
|
||||
|
|
|
@ -21,10 +21,6 @@ serde_cbor = "0.9"
|
|||
name = "bitcoin_deserialize_address"
|
||||
path = "fuzz_targets/bitcoin/deserialize_address.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "bitcoin_deserialize_amount"
|
||||
path = "fuzz_targets/bitcoin/deserialize_amount.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "bitcoin_deserialize_block"
|
||||
path = "fuzz_targets/bitcoin/deserialize_block.rs"
|
||||
|
@ -88,3 +84,7 @@ path = "fuzz_targets/hashes/sha512_256.rs"
|
|||
[[bin]]
|
||||
name = "hashes_sha512"
|
||||
path = "fuzz_targets/hashes/sha512.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "units_deserialize_amount"
|
||||
path = "fuzz_targets/units/deserialize_amount.rs"
|
||||
|
|
|
@ -36,3 +36,6 @@ path = "../../internals"
|
|||
|
||||
[patch.crates-io.bitcoin-io]
|
||||
path = "../../io"
|
||||
|
||||
[patch.crates-io.bitcoin-units]
|
||||
path = "../../units"
|
||||
|
|
|
@ -24,3 +24,6 @@ path = "../../../internals"
|
|||
|
||||
[patch.crates-io.bitcoin-io]
|
||||
path = "../../../io"
|
||||
|
||||
[patch.crates-io.bitcoin-units]
|
||||
path = "../../../units"
|
||||
|
|
|
@ -36,7 +36,7 @@ macro_rules! parse_error_type {
|
|||
|
||||
impl core::fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
$crate::error::write_err!("{}", self.input.display_cannot_parse($subject); self.source)
|
||||
$crate::error::write_err!(f, "{}", self.input.display_cannot_parse($subject); self.source)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "bitcoin-units"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
|
||||
license = "CC0-1.0"
|
||||
repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
|
||||
description = "Basic Bitcoin numeric units such as amount"
|
||||
categories = ["cryptography::cryptocurrencies"]
|
||||
keywords = ["bitcoin", "newtypes"]
|
||||
readme = "README.md"
|
||||
edition = "2021"
|
||||
rust-version = "1.56.1"
|
||||
exclude = ["tests", "contrib"]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["alloc"]
|
||||
alloc = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0"
|
||||
serde_json = "1.0"
|
|
@ -0,0 +1,15 @@
|
|||
Bitcoin Units
|
||||
=============
|
||||
|
||||
This crate provides basic Bitcoin numeric units such as `Amount`.
|
||||
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This library should always compile with any combination of features on **Rust 1.56.1**.
|
||||
|
||||
|
||||
## Licensing
|
||||
|
||||
The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](LICENSE).
|
||||
We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/).
|
|
@ -0,0 +1,79 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
FEATURES="std alloc serde"
|
||||
|
||||
cargo --version
|
||||
rustc --version
|
||||
|
||||
# Work out if we are using a nightly toolchain.
|
||||
NIGHTLY=false
|
||||
if cargo --version | grep nightly >/dev/null; then
|
||||
NIGHTLY=true
|
||||
fi
|
||||
|
||||
# Make all cargo invocations verbose
|
||||
export CARGO_TERM_VERBOSE=true
|
||||
|
||||
# Defaults / sanity checks
|
||||
cargo build
|
||||
cargo test
|
||||
|
||||
if [ "$DO_LINT" = true ]
|
||||
then
|
||||
cargo clippy --locked --all-features --all-targets -- -D warnings
|
||||
fi
|
||||
|
||||
if [ "$DO_FEATURE_MATRIX" = true ]; then
|
||||
# No features
|
||||
cargo build --locked --no-default-features
|
||||
cargo test --locked --no-default-features
|
||||
|
||||
# Default features (this is std and alloc)
|
||||
cargo build --locked
|
||||
cargo test --locked
|
||||
|
||||
# All features
|
||||
cargo build --locked --no-default-features --all-features
|
||||
cargo test --locked --no-default-features --all-features
|
||||
fi
|
||||
|
||||
REPO_DIR=$(git rev-parse --show-toplevel)
|
||||
|
||||
# Build the docs if told to (this only works with the nightly toolchain)
|
||||
if [ "$DO_DOCSRS" = true ]; then
|
||||
RUSTDOCFLAGS="--cfg docsrs -D warnings -D rustdoc::broken-intra-doc-links" cargo +nightly doc --all-features
|
||||
fi
|
||||
|
||||
# Build the docs with a stable toolchain, in unison with the DO_DOCSRS command
|
||||
# above this checks that we feature guarded docs imports correctly.
|
||||
if [ "$DO_DOCS" = true ]; then
|
||||
RUSTDOCFLAGS="-D warnings" cargo +stable doc --all-features
|
||||
fi
|
||||
|
||||
# Run formatter if told to.
|
||||
if [ "$DO_FMT" = true ]; then
|
||||
if [ "$NIGHTLY" = false ]; then
|
||||
echo "DO_FMT requires a nightly toolchain (consider using RUSTUP_TOOLCHAIN)"
|
||||
exit 1
|
||||
fi
|
||||
rustup component add rustfmt
|
||||
cargo fmt --check
|
||||
fi
|
||||
|
||||
# Bench if told to, only works with non-stable toolchain (nightly, beta).
|
||||
if [ "$DO_BENCH" = true ]
|
||||
then
|
||||
if [ "$NIGHTLY" = false ]
|
||||
then
|
||||
if [ -n "$RUSTUP_TOOLCHAIN" ]
|
||||
then
|
||||
echo "RUSTUP_TOOLCHAIN is set to a non-nightly toolchain but DO_BENCH requires a nightly toolchain"
|
||||
else
|
||||
echo "DO_BENCH requires a nightly toolchain"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
RUSTFLAGS='--cfg=bench' cargo bench
|
||||
fi
|
|
@ -4,22 +4,24 @@
|
|||
//!
|
||||
//! This module mainly introduces the [Amount] and [SignedAmount] types.
|
||||
//! We refer to the documentation on the types for more information.
|
||||
//!
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{self, Write};
|
||||
use core::str::FromStr;
|
||||
use core::{default, ops};
|
||||
|
||||
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "serde")]
|
||||
use ::serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::prelude::{String, ToString};
|
||||
|
||||
/// A set of denominations in which amounts can be expressed.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use core::str::FromStr;
|
||||
/// # use bitcoin::Amount;
|
||||
/// # use bitcoin_units::Amount;
|
||||
///
|
||||
/// assert_eq!(Amount::from_str("1 BTC").unwrap(), Amount::from_sat(100_000_000));
|
||||
/// assert_eq!(Amount::from_str("1 cBTC").unwrap(), Amount::from_sat(1_000_000));
|
||||
|
@ -126,12 +128,12 @@ impl FromStr for Denomination {
|
|||
use self::ParseAmountError::*;
|
||||
|
||||
if CONFUSING_FORMS.contains(&s) {
|
||||
return Err(PossiblyConfusingDenomination(s.to_owned()));
|
||||
return Err(PossiblyConfusingDenomination(s.to_string()));
|
||||
};
|
||||
|
||||
let form = self::Denomination::forms(s);
|
||||
|
||||
form.ok_or_else(|| UnknownDenomination(s.to_owned()))
|
||||
form.ok_or_else(|| UnknownDenomination(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -493,7 +495,6 @@ fn fmt_satoshi_in(
|
|||
///
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||
pub struct Amount(u64);
|
||||
|
||||
impl Amount {
|
||||
|
@ -582,7 +583,7 @@ impl Amount {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use bitcoin::{Amount, Denomination};
|
||||
/// # use bitcoin_units::amount::{Amount, Denomination};
|
||||
/// let amount = Amount::from_sat(100_000);
|
||||
/// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin))
|
||||
/// ```
|
||||
|
@ -687,20 +688,6 @@ impl Amount {
|
|||
}
|
||||
}
|
||||
|
||||
impl Decodable for Amount {
|
||||
#[inline]
|
||||
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||
Ok(Amount(Decodable::consensus_decode(r)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Amount {
|
||||
#[inline]
|
||||
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
self.0.consensus_encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl default::Default for Amount {
|
||||
fn default() -> Self { Amount::ZERO }
|
||||
}
|
||||
|
@ -1215,12 +1202,11 @@ pub mod serde {
|
|||
//!
|
||||
//! ```rust,ignore
|
||||
//! use serde::{Serialize, Deserialize};
|
||||
//! use bitcoin::Amount;
|
||||
//! use bitcoin_units::Amount;
|
||||
//!
|
||||
//! #[derive(Serialize, Deserialize)]
|
||||
//! # #[serde(crate = "actual_serde")]
|
||||
//! pub struct HasAmount {
|
||||
//! #[serde(with = "bitcoin::amount::serde::as_btc")]
|
||||
//! #[serde(with = "bitcoin_units::amount::serde::as_btc")]
|
||||
//! pub amount: Amount,
|
||||
//! }
|
||||
//! ```
|
||||
|
@ -1999,7 +1985,7 @@ mod tests {
|
|||
assert_eq!(SignedAmount::from_str(&s.replace(' ', "")), expected);
|
||||
}
|
||||
|
||||
case("5 BCH", Err(E::UnknownDenomination("BCH".to_owned())));
|
||||
case("5 BCH", Err(E::UnknownDenomination("BCH".to_string())));
|
||||
|
||||
case("-1 BTC", Err(E::Negative));
|
||||
case("-0.0 BTC", Err(E::Negative));
|
||||
|
@ -2135,7 +2121,6 @@ mod tests {
|
|||
#[test]
|
||||
fn serde_as_sat() {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(crate = "actual_serde")]
|
||||
struct T {
|
||||
#[serde(with = "crate::amount::serde::as_sat")]
|
||||
pub amt: Amount,
|
||||
|
@ -2163,7 +2148,6 @@ mod tests {
|
|||
use serde_json;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(crate = "actual_serde")]
|
||||
struct T {
|
||||
#[serde(with = "crate::amount::serde::as_btc")]
|
||||
pub amt: Amount,
|
||||
|
@ -2199,7 +2183,6 @@ mod tests {
|
|||
use serde_json;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Eq)]
|
||||
#[serde(crate = "actual_serde")]
|
||||
struct T {
|
||||
#[serde(default, with = "crate::amount::serde::as_btc::opt")]
|
||||
pub amt: Option<Amount>,
|
||||
|
@ -2241,7 +2224,6 @@ mod tests {
|
|||
use serde_json;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Eq)]
|
||||
#[serde(crate = "actual_serde")]
|
||||
struct T {
|
||||
#[serde(default, with = "crate::amount::serde::as_sat::opt")]
|
||||
pub amt: Option<Amount>,
|
|
@ -0,0 +1,47 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
//! Rust Bitcoin units library
|
||||
//!
|
||||
//! This library provides basic types used by the Rust Bitcoin ecosystem.
|
||||
|
||||
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
|
||||
// Experimental features we need.
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
// Coding conventions
|
||||
#![warn(missing_docs)]
|
||||
// Exclude clippy lints we don't think are valuable
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
|
||||
// Disable 16-bit support at least for now as we can't guarantee it yet.
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
compile_error!(
|
||||
"rust-bitcoin currently only supports architectures with pointers wider than 16 bits, let us
|
||||
know if you want 16-bit support. Note that we do NOT guarantee that we will implement it!"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate core;
|
||||
|
||||
/// A generic serialization/deserialization framework.
|
||||
#[cfg(feature = "serde")]
|
||||
pub extern crate serde;
|
||||
|
||||
// TODO: Make amount module less dependent on an allocator.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod amount;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[doc(inline)]
|
||||
pub use self::amount::{Amount, ParseAmountError, SignedAmount};
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod prelude {
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
pub use alloc::{string::{String, ToString}, borrow::ToOwned};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use std::{string::{String, ToString}, borrow::ToOwned};
|
||||
}
|
Loading…
Reference in New Issue