Merge rust-bitcoin/rust-bitcoin#6894: Harden `Copy` policy and apply to all pre-1.0 crates
What changed, and why it matters
This commit removes the automatic `Copy` trait from several public error types in the rust-bitcoin library and updates the project's written policy to discourage `Copy` on error types. `Copy` is a Rust trait that lets values be duplicated silently by the compiler. The team wants to drop it from errors because once an error type promises to be `Copy`, it can never later hold a `String` or other non-copyable data without a breaking change. This is a forward-looking API-cleanup change, not a fix for an active security bug. It also changes some error methods from taking `self` by value to taking `&self`, which is a minor API adjustment.
No immediate action required. Library consumers who depend on these error types being `Copy` will need to update their code to clone explicitly when upgrading. Reviewers should confirm that no remaining public error types unintentionally retain `Copy` where the new policy forbids it, and that CI passes after the API change.
Security signals we found
API hardening: removes `Copy` from public error types to preserve future flexibility
Policy update: docs/policy.md now explicitly discourages `Copy` on error types
No vulnerability fix: change is defensive/preventive, not reactive to a disclosed issue
No unsafe code, no cryptographic changes, no input parsing changes
Evidence from the diff
The merge commit hardens the project’s policy on Copy derivation for error types: errors should derive Debug, Clone, PartialEq, Eq and should not derive Copy unless required by a containing Copy type. The diff removes Copy from chacha20_poly1305::Error, key_expression BIP32 error types, p2p address/BIP434 error types, and units::amount::OutOfRangeError. It also converts OutOfRangeError accessor methods from self receivers to &self receivers, which is a natural consequence of no longer being Copy. The policy document is updated accordingly. No memory-safety vulnerability, cryptographic flaw, or exploitable bug is present in the diff.
Changed components
chacha20_poly1305/src/lib.rskey_expression/src/bip32.rsp2p/src/address.rsp2p/src/bip434.rsunits/src/amount/error.rsdocs/policy.mdInspect captured patch +14 / −14
### chacha20_poly1305/src/lib.rs
@@ -169,7 +169,7 @@ pub mod error {
use core::fmt;
/// Errors encrypting and decrypting messages with `ChaCha20` and `Poly1305` authentication tags.
- #[derive(Copy, Clone, Debug, PartialEq, Eq)]
+ #[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// Additional data showing up when it is not expected.
UnauthenticatedAdditionalData,
### docs/policy.md
@@ -204,7 +204,7 @@ More specifically an error should
- be `non_exhaustive` unless we _really_ never want to change it.
- have private fields unless we are very confident they won't change.
-- derive `Debug, Clone, PartialEq, Eq` (and `Copy` if and only if not `non_exhaustive`).
+- derive `Debug, Clone, PartialEq, Eq`. Do not derive `Copy` unless you have to.
- implement Display using `write_err!()` macro if a variant contains an inner error source.
- have `Error` suffix on error types (structs and enums).
- not have `Error` suffix on enum variants.
### key_expression/src/bip32.rs
@@ -1362,7 +1362,7 @@ pub mod error {
/// Attempted to derive a child of depth 256 or higher.
///
/// There is no way to encode such xkeys.
- #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct MaximumDepthExceededError {}
@@ -1382,7 +1382,7 @@ pub mod error {
/// Attempted to derive a hardened child from an xpub.
///
/// You can only derive hardened children from xprivs.
- #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct CannotDeriveHardenedChildError {}
@@ -1400,7 +1400,7 @@ pub mod error {
}
/// Error deriving an extended public key.
- #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DeriveXpubError {
/// Attempted to derive a hardened child from an xpub.
@@ -1602,7 +1602,7 @@ pub mod error {
}
/// Master seed had an invalid length.
- #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidSeedLengthError {
pub(crate) length: usize,
}
### p2p/src/address.rs
@@ -776,7 +776,7 @@ pub mod error {
///
/// Addresses like Tor, I2P, and CJDNS use different routing mechanisms
/// and cannot be represented as standard IP addresses or socket addresses.
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum UnroutableAddressError {
/// Tor V2 onion address.
### p2p/src/bip434.rs
@@ -236,7 +236,7 @@ pub mod error {
use super::{Feature, FeatureData, FeatureId};
/// Errors related to a [`FeatureId`].
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
pub enum FeatureIdError {
/// Invalid length for [`FeatureId`].
InvalidLength(usize),
@@ -270,7 +270,7 @@ pub mod error {
}
/// Errors related to a [`FeatureData`].
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
pub struct FeatureDataError {
/// Data too long.
pub too_long: usize,
### units/src/amount/error.rs
@@ -120,15 +120,15 @@ impl std::error::Error for ParseAmountError {
}
/// Error returned when a parsed amount is too big or too small.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct OutOfRangeError {
pub(super) is_signed: bool,
pub(super) is_greater_than_max: bool,
}
impl OutOfRangeError {
/// Returns the minimum value of the type that was attempted to be parsed.
- fn lower_bound(self) -> SignedAmount {
+ fn lower_bound(&self) -> SignedAmount {
if self.is_signed() {
SignedAmount::MIN
} else {
@@ -139,15 +139,15 @@ impl OutOfRangeError {
/// Returns true if the type that was attempted to be parsed is signed (`SignedAmount`).
///
/// This can be used to hint to users to enter non-negative values specifically.
- pub fn is_signed(self) -> bool { self.is_signed }
+ pub fn is_signed(&self) -> bool { self.is_signed }
/// Returns true if the input value was larger than the maximum allowed value.
#[inline]
- pub fn is_above_max(self) -> bool { self.is_greater_than_max }
+ pub fn is_above_max(&self) -> bool { self.is_greater_than_max }
/// Returns true if the input value was smaller than the minimum allowed value.
#[inline]
- pub fn is_below_min(self) -> bool { !self.is_greater_than_max }
+ pub fn is_below_min(&self) -> bool { !self.is_greater_than_max }
#[cfg(test)]
#[inline]Why this scored 21/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.