diff --git a/primitives/tests/api.rs b/primitives/tests/api.rs new file mode 100644 index 000000000..913089cdb --- /dev/null +++ b/primitives/tests/api.rs @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Test the API surface of `primitives`. +//! +//! The point of these tests are to check the API surface as opposed to test the API functionality. +//! +//! ref: + +#![allow(dead_code)] +#![allow(unused_imports)] +// No benefit in running this test without features enabled. +#![cfg(feature = "alloc")] +#![cfg(feature = "hex")] +#![cfg(feature = "arbitrary")] + +use arbitrary::Arbitrary; +use bitcoin_primitives::block::{Checked, Unchecked}; +use bitcoin_primitives::script::{self, ScriptHash, WScriptHash}; +use bitcoin_primitives::{ + absolute, block, merkle_tree, pow, relative, transaction, witness, OutPoint, Script, ScriptBuf, + Sequence, Transaction, TxIn, TxOut, Txid, Witness, Wtxid, +}; +use hashes::sha256t; + +/// A struct that includes all public non-error enums. +#[derive(Debug)] // All public types implement Debug (C-DEBUG). +struct Enums { + a: block::Checked, // Empty enums are not constructable. + b: block::Unchecked, + c: absolute::LockTime, + d: relative::LockTime, +} + +/// A struct that includes all public non-error structs. +#[derive(Debug)] // All public types implement Debug (C-DEBUG). +struct Structs<'a> { + a: block::Block, + b: block::Block, + c: block::Header, + d: block::Version, + e: block::BlockHash, + f: block::WitnessCommitment, + g: merkle_tree::TxMerkleNode, + h: merkle_tree::WitnessMerkleNode, + i: pow::CompactTarget, + j: &'a Script, + k: ScriptHash, + l: WScriptHash, + m: ScriptBuf, + n: Sequence, + o: Transaction, + p: TxIn, + q: TxOut, + r: OutPoint, + s: Txid, + t: Wtxid, + u: transaction::Version, + v: Witness, + // w: witness::Iter<'a>, +} + +static SCRIPT: ScriptBuf = ScriptBuf::new(); +static BYTES: [u8; 32] = [0x00; 32]; + +/// Public structs that derive common traits. +// C-COMMON-TRAITS excluding `Debug, Default, Display, Ord, PartialOrd, Hash`. +#[derive(Clone, PartialEq, Eq)] +struct CommonTraits { + a: block::Block, + b: block::Block, + c: block::Header, + d: block::Version, + e: block::BlockHash, + f: block::WitnessCommitment, + g: merkle_tree::TxMerkleNode, + h: merkle_tree::WitnessMerkleNode, + i: pow::CompactTarget, + // j: &'a Script, + k: ScriptHash, + l: WScriptHash, + m: ScriptBuf, + n: Sequence, + o: Transaction, + p: TxIn, + q: TxOut, + r: OutPoint, + s: Txid, + t: Wtxid, + u: transaction::Version, + v: Witness, + // w: witness::Iter<'a>, +} + +/// A struct that includes all types that implement `Clone`. +#[derive(Clone)] // C-COMMON-TRAITS: `Clone` +struct Clone<'a> { + a: block::Block, + b: block::Block, + c: block::Header, + d: block::Version, + e: block::BlockHash, + f: block::WitnessCommitment, + g: merkle_tree::TxMerkleNode, + h: merkle_tree::WitnessMerkleNode, + i: pow::CompactTarget, + // j: &'a Script, + k: ScriptHash, + l: WScriptHash, + m: ScriptBuf, + n: Sequence, + o: Transaction, + p: TxIn, + q: TxOut, + r: OutPoint, + s: Txid, + t: Wtxid, + u: transaction::Version, + v: Witness, + w: witness::Iter<'a>, +} + +/// Public structs that derive common traits. +// C-COMMON-TRAITS excluding `Clone`, `Debug, `Default`, and `Display` +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Ord { + // a: block::Block, + // b: block::Block, + c: block::Header, + d: block::Version, + e: block::BlockHash, + f: block::WitnessCommitment, + g: merkle_tree::TxMerkleNode, + h: merkle_tree::WitnessMerkleNode, + i: pow::CompactTarget, + // j: &'a Script, // Doesn't implement `Clone`. + k: ScriptHash, + l: WScriptHash, + m: ScriptBuf, + n: Sequence, + o: Transaction, + p: TxIn, + q: TxOut, + r: OutPoint, + s: Txid, + t: Wtxid, + u: transaction::Version, + v: Witness, + // w: witness::Iter<'a>, +} + +/// A struct that includes all types that implement `Default`. +#[derive(Default, Debug, PartialEq, Eq)] // C-COMMON-TRAITS: `Default` (others just so we can test). +struct Default { + a: block::Version, + b: &'static Script, + c: ScriptBuf, + d: Sequence, + e: Witness, +} + +/// A struct that includes all public error types. +// These derives are the policy of `rust-bitcoin` not Rust API guidelines. +#[derive(Debug, Clone, PartialEq, Eq)] // All public types implement Debug (C-DEBUG). +struct Errors { + a: transaction::ParseOutPointError, + b: relative::IncompatibleHeightError, + c: relative::IncompatibleTimeError, + d: relative::IncompatibleHeightError, + e: relative::IncompatibleTimeError, + f: relative::DisabledLockTimeError, + g: relative::DisabledLockTimeError, + h: script::RedeemScriptSizeError, + i: script::WitnessScriptSizeError, +} + +#[test] +fn api_can_use_units_modules_from_crate_root() { + use bitcoin_primitives::{amount, block, fee_rate, locktime, weight}; +} + +#[test] +fn api_can_use_units_types_from_crate_root() { + use bitcoin_primitives::{Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, Weight}; +} + +#[test] +fn api_can_use_all_units_types_from_module_amount() { + use bitcoin_primitives::amount::{ + Amount, Denomination, Display, InputTooLargeError, InvalidCharacterError, + MissingDenominationError, MissingDigitsError, OutOfRangeError, ParseAmountError, + ParseDenominationError, ParseError, PossiblyConfusingDenominationError, SignedAmount, + TooPreciseError, UnknownDenominationError, + }; +} + +#[test] +fn api_can_use_modules_from_crate_root() { + use bitcoin_primitives::{ + block, locktime, merkle_tree, pow, script, sequence, transaction, witness, + }; +} + +#[test] +fn api_can_use_types_from_crate_root() { + use bitcoin_primitives::{ + Block, BlockHash, BlockHeader, BlockVersion, CompactTarget, OutPoint, Script, ScriptBuf, + Sequence, Transaction, TransactionVersion, TxIn, TxMerkleNode, TxOut, Txid, Witness, + WitnessCommitment, WitnessMerkleNode, Wtxid, + }; +} + +#[test] +fn api_can_use_all_types_from_module_locktime() { + use bitcoin_primitives::locktime::relative::{ + DisabledLockTimeError, IncompatibleHeightError, IncompatibleTimeError, LockTime, + }; + use bitcoin_primitives::locktime::{absolute, relative}; +} + +#[test] +fn api_can_use_all_types_from_module_script() { + use bitcoin_primitives::script::{ + RedeemScriptSizeError, Script, ScriptBuf, ScriptHash, WScriptHash, WitnessScriptSizeError, + }; +} + +// `Debug` representation is never empty (C-DEBUG-NONEMPTY). +#[test] +fn api_all_non_error_types_have_non_empty_debug() { + macro_rules! check_debug { + ($($t:expr);* $(;)?) => { + $( + let debug = format!("{:?}", $t); + assert!(!debug.is_empty()); + )* + } + } + + // All the enums. + check_debug! { + absolute::LockTime::ZERO; + relative::LockTime::ZERO + }; + + // We abuse `Arbitrary` here to get a quick and dirty instance. + let ab: [u8; 32] = [0xab; 32]; + let mut u = arbitrary::Unstructured::new(&ab); + let transaction = Transaction::arbitrary(&mut u).unwrap(); + + // All the structs. + check_debug! { + block::Block::::arbitrary(&mut u).unwrap().assume_checked(None); + block::Block::::arbitrary(&mut u).unwrap(); + block::Header::arbitrary(&mut u).unwrap(); + block::Version::arbitrary(&mut u).unwrap(); + block::BlockHash::from_byte_array(BYTES); + block::WitnessCommitment::from_byte_array(BYTES); + merkle_tree::TxMerkleNode::from_byte_array(BYTES); + merkle_tree::WitnessMerkleNode::from_byte_array(BYTES); + pow::CompactTarget::from_consensus(0x1d00_ffff); + SCRIPT.as_script(); + ScriptHash::from_script(&SCRIPT).unwrap(); + WScriptHash::from_script(&SCRIPT).unwrap(); + SCRIPT.clone(); + Sequence::arbitrary(&mut u).unwrap(); + Transaction::arbitrary(&mut u).unwrap(); + TxIn::arbitrary(&mut u).unwrap(); + TxOut::arbitrary(&mut u).unwrap(); + OutPoint::arbitrary(&mut u).unwrap(); + transaction.compute_txid(); + transaction.compute_wtxid(); + transaction.version; + Witness::arbitrary(&mut u).unwrap(); + // ad: witness::Iter<'a>, + }; +} + +#[test] +fn all_types_implement_send_sync() { + fn assert_send() {} + fn assert_sync() {} + + // Types are `Send` and `Sync` where possible (C-SEND-SYNC). + assert_send::(); + assert_sync::(); + assert_send::(); + assert_sync::(); + + // Error types should implement the Send and Sync traits (C-GOOD-ERR). + assert_send::(); + assert_sync::(); +} + +#[test] +fn regression_default() { + let got: Default = Default::default(); + let want = Default { + a: block::Version::NO_SOFT_FORK_SIGNALLING, + b: Script::from_bytes(&[]), + c: ScriptBuf::from_bytes(Vec::new()), + d: Sequence::MAX, + e: Witness::new(), + }; + assert_eq!(got, want); +} + +#[test] +// The only trait in this crate is `block::Validation` and it is not dyn compatible. +fn dyn_compatible() {}