Skip to content

Commit d66ec30

Browse files
committed
f Enforce BOLT2 compat in new_op_return and test we fail if violated
1 parent 0ade7ec commit d66ec30

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

lightning/src/ln/script.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,18 @@ impl ShutdownScript {
8181
/// This is only needed and valid for channels supporting `option_simple_close`. Please refer
8282
/// to [BOLT-2] for more information.
8383
///
84-
/// Note this only supports creating a script with data of up to 76 bytes length via
85-
/// [`PushBytes`].
84+
/// Will return [`InvalidShutdownScript`] if the given data is not [BOLT-2] compliant based on
85+
/// the given features.
8686
///
8787
/// [BOLT-2]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#closing-negotiation-closing_complete-and-closing_sig
88-
pub fn new_op_return<T: AsRef<PushBytes>>(data: T) -> Self {
88+
pub fn new_op_return<T: AsRef<PushBytes>>(
89+
data: T, features: &InitFeatures,
90+
) -> Result<Self, InvalidShutdownScript> {
8991
let script = ScriptBuf::new_op_return(data);
90-
Self(ShutdownScriptImpl::Bolt2(script))
92+
if !is_bolt2_compliant(&script, features) {
93+
return Err(InvalidShutdownScript { script });
94+
}
95+
Ok(Self(ShutdownScriptImpl::Bolt2(script)))
9196
}
9297

9398
/// Generates a witness script pubkey from the given segwit version and program.
@@ -157,9 +162,8 @@ pub(crate) fn is_bolt2_compliant(script: &Script, features: &InitFeatures) -> bo
157162
return instruction_iter.next().is_none();
158163
}
159164

160-
// While `rust-bitcoin` doesn't allow to construct `PushBytes` from arrays
161-
// longer than 75 bytes, itself curiously interprets `OP_PUSHDATA1` as
162-
// `Instruction::PushBytes`, having us land here in this case, too.
165+
// `rust-bitcoin` interprets `OP_PUSHDATA1` as `Instruction::PushBytes`, having
166+
// us land here in this case, too.
163167
//
164168
// * `76` followed by `76` to `80` followed by exactly that many bytes
165169
if (76..=80).contains(&bytes.len()) {
@@ -228,13 +232,15 @@ mod shutdown_script_tests {
228232
use super::ShutdownScript;
229233

230234
use bitcoin::opcodes;
231-
use bitcoin::script::{Builder, ScriptBuf};
235+
use bitcoin::script::{Builder, PushBytes, ScriptBuf};
232236
use bitcoin::secp256k1::Secp256k1;
233237
use bitcoin::secp256k1::{PublicKey, SecretKey};
234238
use bitcoin::{WitnessProgram, WitnessVersion};
235239

240+
use crate::ln::channelmanager::provided_init_features;
236241
use crate::prelude::*;
237242
use crate::types::features::InitFeatures;
243+
use crate::util::config::UserConfig;
238244

239245
fn pubkey() -> bitcoin::key::PublicKey {
240246
let secp_ctx = Secp256k1::signing_only();
@@ -310,8 +316,9 @@ mod shutdown_script_tests {
310316
#[test]
311317
fn generates_op_return_from_data() {
312318
let data = [6; 6];
319+
let features = provided_init_features(&UserConfig::default());
313320
let op_return_script = ScriptBuf::new_op_return(&data);
314-
let shutdown_script = ShutdownScript::new_op_return(&data);
321+
let shutdown_script = ShutdownScript::new_op_return(&data, &features).unwrap();
315322
assert!(shutdown_script.is_compatible(&simple_close_features()));
316323
assert!(!shutdown_script.is_compatible(&InitFeatures::empty()));
317324
assert_eq!(shutdown_script.into_inner(), op_return_script);
@@ -370,5 +377,11 @@ mod shutdown_script_tests {
370377
pushdata_vec.extend_from_slice(&[1u8; 81]);
371378
let pushdata_script = ScriptBuf::from_bytes(pushdata_vec);
372379
assert!(ShutdownScript::try_from(pushdata_script).is_err());
380+
381+
// - In `ShutdownScript::new_op_return` the OP_RETURN data is longer than 80 bytes.
382+
let features = provided_init_features(&UserConfig::default());
383+
let big_buffer = &[0xfau8; 81][..];
384+
let push_bytes: &PushBytes = big_buffer.try_into().unwrap();
385+
assert!(ShutdownScript::new_op_return(&push_bytes, &features).is_err());
373386
}
374387
}

0 commit comments

Comments
 (0)