@@ -105,6 +105,26 @@ impl ShutdownScript {
105
105
ShutdownScriptImpl :: Bolt2 ( _) => None ,
106
106
}
107
107
}
108
+
109
+ /// Returns whether the shutdown script is compatible with the features as defined by BOLT #2.
110
+ ///
111
+ /// Specifically, checks for compliance with feature `option_shutdown_anysegwit`.
112
+ pub fn is_compatible ( & self , features : & InitFeatures ) -> bool {
113
+ match & self . 0 {
114
+ ShutdownScriptImpl :: Legacy ( _) => true ,
115
+ ShutdownScriptImpl :: Bolt2 ( script) => is_bolt2_compliant ( script, features) ,
116
+ }
117
+ }
118
+ }
119
+
120
+ fn is_bolt2_compliant ( script : & Script , features : & InitFeatures ) -> bool {
121
+ if script. is_p2pkh ( ) || script. is_p2sh ( ) || script. is_v0_p2wpkh ( ) || script. is_v0_p2wsh ( ) {
122
+ true
123
+ } else if features. supports_shutdown_anysegwit ( ) {
124
+ script. is_witness_program ( ) && script. as_bytes ( ) [ 0 ] != SEGWIT_V0 . into_u8 ( )
125
+ } else {
126
+ false
127
+ }
108
128
}
109
129
110
130
impl TryFrom < Script > for ShutdownScript {
@@ -119,10 +139,8 @@ impl TryFrom<(Script, &InitFeatures)> for ShutdownScript {
119
139
type Error = InvalidShutdownScript ;
120
140
121
141
fn try_from ( ( script, features) : ( Script , & InitFeatures ) ) -> Result < Self , Self :: Error > {
122
- if script . is_p2pkh ( ) || script. is_p2sh ( ) || script . is_v0_p2wpkh ( ) || script . is_v0_p2wsh ( ) {
142
+ if is_bolt2_compliant ( & script, features ) {
123
143
Ok ( Self ( ShutdownScriptImpl :: Bolt2 ( script) ) )
124
- } else if features. supports_shutdown_anysegwit ( ) && script. is_witness_program ( ) && script. as_bytes ( ) [ 0 ] != SEGWIT_V0 . into_u8 ( ) {
125
- Ok ( Self ( ShutdownScriptImpl :: Bolt2 ( script) ) ) // option_shutdown_anysegwit
126
144
} else {
127
145
Err ( InvalidShutdownScript ( script) )
128
146
}
@@ -147,7 +165,9 @@ mod shutdown_script_tests {
147
165
use bitcoin:: blockdata:: script:: { Builder , Script } ;
148
166
use bitcoin:: secp256k1:: Secp256k1 ;
149
167
use bitcoin:: secp256k1:: key:: { PublicKey , SecretKey } ;
168
+ use ln:: features:: InitFeatures ;
150
169
use std:: convert:: TryFrom ;
170
+ use core:: num:: NonZeroU8 ;
151
171
152
172
fn pubkey ( ) -> bitcoin:: util:: ecdsa:: PublicKey {
153
173
let secp_ctx = Secp256k1 :: signing_only ( ) ;
@@ -173,6 +193,8 @@ mod shutdown_script_tests {
173
193
let p2wpkh_script = Script :: new_v0_wpkh ( & pubkey_hash) ;
174
194
175
195
let shutdown_script = ShutdownScript :: new_p2wpkh_from_pubkey ( pubkey. key ) ;
196
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) ) ) ;
197
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) . clear_shutdown_anysegwit( ) ) ) ;
176
198
assert_eq ! ( shutdown_script. into_inner( ) , p2wpkh_script) ;
177
199
}
178
200
@@ -182,6 +204,8 @@ mod shutdown_script_tests {
182
204
let p2pkh_script = Script :: new_p2pkh ( & pubkey_hash) ;
183
205
184
206
let shutdown_script = ShutdownScript :: new_p2pkh ( & pubkey_hash) ;
207
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) ) ) ;
208
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) . clear_shutdown_anysegwit( ) ) ) ;
185
209
assert_eq ! ( shutdown_script. into_inner( ) , p2pkh_script) ;
186
210
assert ! ( ShutdownScript :: try_from( p2pkh_script) . is_ok( ) ) ;
187
211
}
@@ -192,6 +216,8 @@ mod shutdown_script_tests {
192
216
let p2sh_script = Script :: new_p2sh ( & script_hash) ;
193
217
194
218
let shutdown_script = ShutdownScript :: new_p2sh ( & script_hash) ;
219
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) ) ) ;
220
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) . clear_shutdown_anysegwit( ) ) ) ;
195
221
assert_eq ! ( shutdown_script. into_inner( ) , p2sh_script) ;
196
222
assert ! ( ShutdownScript :: try_from( p2sh_script) . is_ok( ) ) ;
197
223
}
@@ -202,6 +228,8 @@ mod shutdown_script_tests {
202
228
let p2wpkh_script = Script :: new_v0_wpkh ( & pubkey_hash) ;
203
229
204
230
let shutdown_script = ShutdownScript :: new_p2wpkh ( & pubkey_hash) ;
231
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) ) ) ;
232
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) . clear_shutdown_anysegwit( ) ) ) ;
205
233
assert_eq ! ( shutdown_script. into_inner( ) , p2wpkh_script) ;
206
234
assert ! ( ShutdownScript :: try_from( p2wpkh_script) . is_ok( ) ) ;
207
235
}
@@ -212,19 +240,39 @@ mod shutdown_script_tests {
212
240
let p2wsh_script = Script :: new_v0_wsh ( & script_hash) ;
213
241
214
242
let shutdown_script = ShutdownScript :: new_p2wsh ( & script_hash) ;
243
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) ) ) ;
244
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) . clear_shutdown_anysegwit( ) ) ) ;
215
245
assert_eq ! ( shutdown_script. into_inner( ) , p2wsh_script) ;
216
246
assert ! ( ShutdownScript :: try_from( p2wsh_script) . is_ok( ) ) ;
217
247
}
218
248
249
+ #[ test]
250
+ fn generates_segwit_from_non_v0_witness_program ( ) {
251
+ let version = u5:: try_from_u8 ( 16 ) . unwrap ( ) ;
252
+ let witness_program = Script :: new_witness_program ( version, & [ 0 ; 40 ] ) ;
253
+
254
+ let version = NonZeroU8 :: new ( version. to_u8 ( ) ) . unwrap ( ) ;
255
+ let shutdown_script = ShutdownScript :: new_witness_program ( version, & [ 0 ; 40 ] ) ;
256
+ assert ! ( shutdown_script. is_compatible( & InitFeatures :: known( ) ) ) ;
257
+ assert ! ( !shutdown_script. is_compatible( & InitFeatures :: known( ) . clear_shutdown_anysegwit( ) ) ) ;
258
+ assert_eq ! ( shutdown_script. into_inner( ) , witness_program) ;
259
+ }
260
+
219
261
#[ test]
220
262
fn fails_from_unsupported_script ( ) {
221
263
let op_return = Script :: new_op_return ( & [ 0 ; 42 ] ) ;
222
264
assert ! ( ShutdownScript :: try_from( op_return) . is_err( ) ) ;
223
265
}
224
266
225
267
#[ test]
226
- fn fails_from_invalid_segwit_v0_program ( ) {
268
+ fn fails_from_invalid_segwit_v0_witness_program ( ) {
227
269
let witness_program = Script :: new_witness_program ( u5:: try_from_u8 ( 0 ) . unwrap ( ) , & [ 0 ; 2 ] ) ;
228
270
assert ! ( ShutdownScript :: try_from( witness_program) . is_err( ) ) ;
229
271
}
272
+
273
+ #[ test]
274
+ fn fails_from_invalid_segwit_non_v0_witness_program ( ) {
275
+ let witness_program = Script :: new_witness_program ( u5:: try_from_u8 ( 16 ) . unwrap ( ) , & [ 0 ; 42 ] ) ;
276
+ assert ! ( ShutdownScript :: try_from( witness_program) . is_err( ) ) ;
277
+ }
230
278
}
0 commit comments