From: Jeffrey Czyz Date: Thu, 22 Jul 2021 01:12:14 +0000 (-0500) Subject: Add ShutdownScript for BOLT 2 acceptable scripts X-Git-Tag: v0.0.100~7^2~9 X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=ecc70757f9863a76ab9d4cc8b6bfe1a9cc9f4977;p=rust-lightning Add ShutdownScript for BOLT 2 acceptable scripts BOLT 2 enumerates the script formats that may be used for a shutdown script. KeysInterface::get_shutdown_pubkey returns a PublicKey used to form one of the acceptable formats (P2WPKH). Add a ShutdownScript abstraction to encapsulate all accept formats and be backwards compatible with P2WPKH scripts serialized as the corresponding PublicKey. --- diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 7500b93c0..5f928eea0 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -27,6 +27,7 @@ pub mod msgs; pub mod peer_handler; pub mod chan_utils; pub mod features; +pub mod script; #[cfg(feature = "fuzztarget")] pub mod peer_channel_encryptor; diff --git a/lightning/src/ln/script.rs b/lightning/src/ln/script.rs new file mode 100644 index 000000000..b6fcdbd19 --- /dev/null +++ b/lightning/src/ln/script.rs @@ -0,0 +1,268 @@ +//! Abstractions for scripts used in the Lightning Network. + +use bitcoin::blockdata::opcodes::all::OP_PUSHBYTES_0 as SEGWIT_V0; +use bitcoin::blockdata::script::{Builder, Script}; +use bitcoin::hashes::Hash; +use bitcoin::hash_types::{PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash}; +use bitcoin::secp256k1::key::PublicKey; + +use ln::features::InitFeatures; + +use core::convert::TryFrom; +use core::num::NonZeroU8; + +/// A script pubkey for shutting down a channel as defined by [BOLT #2]. +/// +/// [BOLT #2]: https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md +pub struct ShutdownScript(ShutdownScriptImpl); + +/// An error occurring when converting from [`Script`] to [`ShutdownScript`]. +#[derive(Debug)] +pub struct InvalidShutdownScript { + /// The script that did not meet the requirements from [BOLT #2]. + /// + /// [BOLT #2]: https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md + pub script: Script +} + +enum ShutdownScriptImpl { + /// [`PublicKey`] used to form a P2WPKH script pubkey. Used to support backward-compatible + /// serialization. + Legacy(PublicKey), + + /// [`Script`] adhering to a script pubkey format specified in BOLT #2. + Bolt2(Script), +} + +impl ShutdownScript { + /// Generates a P2WPKH script pubkey from the given [`PublicKey`]. + pub fn new_p2wpkh_from_pubkey(pubkey: PublicKey) -> Self { + Self(ShutdownScriptImpl::Legacy(pubkey)) + } + + /// Generates a P2PKH script pubkey from the given [`PubkeyHash`]. + pub fn new_p2pkh(pubkey_hash: &PubkeyHash) -> Self { + Self(ShutdownScriptImpl::Bolt2(Script::new_p2pkh(pubkey_hash))) + } + + /// Generates a P2SH script pubkey from the given [`ScriptHash`]. + pub fn new_p2sh(script_hash: &ScriptHash) -> Self { + Self(ShutdownScriptImpl::Bolt2(Script::new_p2sh(script_hash))) + } + + /// Generates a P2WPKH script pubkey from the given [`WPubkeyHash`]. + pub fn new_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self { + Self(ShutdownScriptImpl::Bolt2(Script::new_v0_wpkh(pubkey_hash))) + } + + /// Generates a P2WSH script pubkey from the given [`WScriptHash`]. + pub fn new_p2wsh(script_hash: &WScriptHash) -> Self { + Self(ShutdownScriptImpl::Bolt2(Script::new_v0_wsh(script_hash))) + } + + /// Generates a P2WSH script pubkey from the given segwit version and program. + /// + /// # Errors + /// + /// This function may return an error if `program` is invalid for the segwit `version`. + pub fn new_witness_program(version: NonZeroU8, program: &[u8]) -> Result { + let script = Builder::new() + .push_int(version.get().into()) + .push_slice(&program) + .into_script(); + Self::try_from(script) + } + + /// Converts the shutdown script into the underlying [`Script`]. + pub fn into_inner(self) -> Script { + self.into() + } + + /// Returns the [`PublicKey`] used for a P2WPKH shutdown script if constructed directly from it. + pub fn as_legacy_pubkey(&self) -> Option<&PublicKey> { + match &self.0 { + ShutdownScriptImpl::Legacy(pubkey) => Some(pubkey), + ShutdownScriptImpl::Bolt2(_) => None, + } + } + + /// Returns whether the shutdown script is compatible with the features as defined by BOLT #2. + /// + /// Specifically, checks for compliance with feature `option_shutdown_anysegwit`. + pub fn is_compatible(&self, features: &InitFeatures) -> bool { + match &self.0 { + ShutdownScriptImpl::Legacy(_) => true, + ShutdownScriptImpl::Bolt2(script) => is_bolt2_compliant(script, features), + } + } +} + +fn is_bolt2_compliant(script: &Script, features: &InitFeatures) -> bool { + if script.is_p2pkh() || script.is_p2sh() || script.is_v0_p2wpkh() || script.is_v0_p2wsh() { + true + } else if features.supports_shutdown_anysegwit() { + script.is_witness_program() && script.as_bytes()[0] != SEGWIT_V0.into_u8() + } else { + false + } +} + +impl TryFrom