Skip to content

Commit ef28e9a

Browse files
committed
fix(wallet)!: enforce OP_RETURN standardness
1 parent 3bc45b5 commit ef28e9a

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

crates/wallet/src/wallet/error.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ pub enum CreateTxError {
104104
MissingNonWitnessUtxo(OutPoint),
105105
/// Miniscript PSBT error
106106
MiniscriptPsbt(MiniscriptPsbtError),
107+
/// Multiple recipients are OP_RETURN outputs
108+
MultipleOpReturn,
109+
/// The OP_RETURN data contains too many bytes
110+
MaxDataCarrierSize,
107111
}
108112

109113
impl fmt::Display for CreateTxError {
@@ -171,6 +175,12 @@ impl fmt::Display for CreateTxError {
171175
CreateTxError::MiniscriptPsbt(err) => {
172176
write!(f, "Miniscript PSBT error: {}", err)
173177
}
178+
CreateTxError::MultipleOpReturn => {
179+
write!(f, "Multiple recipients are OP_RETURN outputs")
180+
}
181+
CreateTxError::MaxDataCarrierSize => {
182+
write!(f, "The OP_RETURN data contains too many bytes")
183+
}
174184
}
175185
}
176186
}

crates/wallet/src/wallet/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub use persisted::*;
8787
pub use utils::IsDust;
8888

8989
const COINBASE_MATURITY: u32 = 100;
90+
const MAX_OP_RETURN_BYTES: usize = 83;
9091

9192
/// A Bitcoin wallet
9293
///
@@ -1388,12 +1389,23 @@ impl Wallet {
13881389
let mut received = Amount::ZERO;
13891390

13901391
let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
1392+
let mut contains_op_return = false;
13911393

13921394
for (index, (script_pubkey, value)) in recipients.enumerate() {
13931395
if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() {
13941396
return Err(CreateTxError::OutputBelowDustLimit(index));
13951397
}
13961398

1399+
if script_pubkey.is_op_return() {
1400+
if contains_op_return {
1401+
return Err(CreateTxError::MultipleOpReturn);
1402+
}
1403+
if script_pubkey.len() > MAX_OP_RETURN_BYTES {
1404+
return Err(CreateTxError::MaxDataCarrierSize);
1405+
}
1406+
contains_op_return = true;
1407+
}
1408+
13971409
if self.is_mine(script_pubkey.clone()) {
13981410
received += value;
13991411
}

0 commit comments

Comments
 (0)