From ef28e9a52966e3c348d6d1bd22ab26624c628132 Mon Sep 17 00:00:00 2001 From: Rob N Date: Sat, 16 Nov 2024 10:36:11 -1000 Subject: [PATCH] fix(wallet)!: enforce OP_RETURN standardness --- crates/wallet/src/wallet/error.rs | 10 ++++++++++ crates/wallet/src/wallet/mod.rs | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/crates/wallet/src/wallet/error.rs b/crates/wallet/src/wallet/error.rs index adce5b188..0cfda9191 100644 --- a/crates/wallet/src/wallet/error.rs +++ b/crates/wallet/src/wallet/error.rs @@ -104,6 +104,10 @@ pub enum CreateTxError { MissingNonWitnessUtxo(OutPoint), /// Miniscript PSBT error MiniscriptPsbt(MiniscriptPsbtError), + /// Multiple recipients are OP_RETURN outputs + MultipleOpReturn, + /// The OP_RETURN data contains too many bytes + MaxDataCarrierSize, } impl fmt::Display for CreateTxError { @@ -171,6 +175,12 @@ impl fmt::Display for CreateTxError { CreateTxError::MiniscriptPsbt(err) => { write!(f, "Miniscript PSBT error: {}", err) } + CreateTxError::MultipleOpReturn => { + write!(f, "Multiple recipients are OP_RETURN outputs") + } + CreateTxError::MaxDataCarrierSize => { + write!(f, "The OP_RETURN data contains too many bytes") + } } } } diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 68d5e6bec..fc307d011 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -87,6 +87,7 @@ pub use persisted::*; pub use utils::IsDust; const COINBASE_MATURITY: u32 = 100; +const MAX_OP_RETURN_BYTES: usize = 83; /// A Bitcoin wallet /// @@ -1388,12 +1389,23 @@ impl Wallet { let mut received = Amount::ZERO; let recipients = params.recipients.iter().map(|(r, v)| (r, *v)); + let mut contains_op_return = false; for (index, (script_pubkey, value)) in recipients.enumerate() { if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() { return Err(CreateTxError::OutputBelowDustLimit(index)); } + if script_pubkey.is_op_return() { + if contains_op_return { + return Err(CreateTxError::MultipleOpReturn); + } + if script_pubkey.len() > MAX_OP_RETURN_BYTES { + return Err(CreateTxError::MaxDataCarrierSize); + } + contains_op_return = true; + } + if self.is_mine(script_pubkey.clone()) { received += value; }