@@ -32,6 +32,7 @@ use crate::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
32
32
use crate :: util:: logger:: { Logger , Level , Record } ;
33
33
use crate :: util:: ser:: { Readable , ReadableArgs , Writer , Writeable } ;
34
34
35
+ use bitcoin:: { TxIn , Witness , EcdsaSighashType } ;
35
36
use bitcoin:: blockdata:: constants:: genesis_block;
36
37
use bitcoin:: blockdata:: transaction:: { Transaction , TxOut } ;
37
38
use bitcoin:: blockdata:: script:: { Builder , Script } ;
@@ -264,6 +265,157 @@ impl<'a> chain::Watch<EnforcingSigner> for TestChainMonitor<'a> {
264
265
}
265
266
}
266
267
268
+ pub enum WatchtowerState {
269
+ /// Upon a new commitment signed, we'll get a
270
+ /// ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTxInfo. We'll store the commitment txid and
271
+ /// revokeable output index to use to form the justice tx once we get a revoke_and_ack with the
272
+ /// commitment secret.
273
+ CounterpartyCommitmentTxSeen {
274
+ commitment_txid : Txid ,
275
+ output_idx : u32 ,
276
+ value : u64 ,
277
+ } ,
278
+ /// After receiving a revoke_and_ack for a commitment number, we'll form and store the justice
279
+ /// tx which would be used to provide the watchtower with the data it needs.
280
+ JusticeTxFormed ( Transaction ) ,
281
+ }
282
+
283
+ pub struct WatchtowerPersister {
284
+ pub persister : TestPersister ,
285
+ pub destination_script : Script ,
286
+ pub channel_watchtower_state : Mutex < HashMap < OutPoint , HashMap < u64 , WatchtowerState > > > ,
287
+ }
288
+
289
+ impl WatchtowerPersister {
290
+ pub ( crate ) fn new ( destination_script : Script ) -> Self {
291
+ WatchtowerPersister {
292
+ persister : TestPersister :: new ( ) ,
293
+ destination_script,
294
+ channel_watchtower_state : Mutex :: new ( HashMap :: new ( ) ) ,
295
+ }
296
+ }
297
+
298
+ fn build_justice_tx ( & self , commitment_txid : Txid , output_idx : u32 , value : u64 ) -> Transaction {
299
+ let mut justice_tx = Transaction {
300
+ version : 2 ,
301
+ lock_time : bitcoin:: PackedLockTime :: ZERO ,
302
+ input : vec ! [ TxIn {
303
+ previous_output: bitcoin:: OutPoint {
304
+ txid: commitment_txid,
305
+ vout: output_idx,
306
+ } ,
307
+ script_sig: Script :: new( ) ,
308
+ sequence: Sequence :: ENABLE_RBF_NO_LOCKTIME ,
309
+ witness: Witness :: new( ) ,
310
+ } ] ,
311
+ output : vec ! [ TxOut {
312
+ script_pubkey: self . destination_script. clone( ) ,
313
+ value,
314
+ } ] ,
315
+ } ;
316
+ // Naive fee calculation to pass tests
317
+ let min_fee = ( justice_tx. weight ( ) as u64 + 3 ) / 4 ;
318
+ justice_tx. output [ 0 ] . value -= min_fee * 2 ;
319
+ justice_tx
320
+ }
321
+
322
+ fn sign_justice_tx < Signer : sign:: WriteableEcdsaChannelSigner > ( & self , mut justice_tx : Transaction , secret : & [ u8 ; 32 ] , value : u64 , data : & channelmonitor:: ChannelMonitor < Signer > ) -> Result < Transaction , ( ) > {
323
+ let secp_ctx = Secp256k1 :: new ( ) ;
324
+ let per_commitment_key = SecretKey :: from_slice ( secret) . unwrap ( ) ;
325
+ let per_commitment_point = PublicKey :: from_secret_key ( & secp_ctx, & per_commitment_key) ;
326
+ let revokeable_redeemscript = data. get_revokeable_redeemscript ( per_commitment_point) ;
327
+
328
+ let input_idx = 0 ;
329
+ let sig = match data. sign_justice_revoked_output ( & justice_tx, input_idx, value, & per_commitment_key) {
330
+ Ok ( sig) => sig,
331
+ Err ( _) => return Err ( ( ) ) ,
332
+ } ;
333
+
334
+ let mut ser_sig = sig. serialize_der ( ) . to_vec ( ) ;
335
+ ser_sig. push ( EcdsaSighashType :: All as u8 ) ;
336
+ justice_tx. input [ 0 ] . witness . push ( ser_sig) ;
337
+ justice_tx. input [ 0 ] . witness . push ( vec ! ( 1 ) ) ;
338
+ justice_tx. input [ 0 ] . witness . push ( revokeable_redeemscript. clone ( ) . into_bytes ( ) ) ;
339
+
340
+ Ok ( justice_tx)
341
+ }
342
+ }
343
+
344
+ impl < Signer : sign:: WriteableEcdsaChannelSigner > chainmonitor:: Persist < Signer > for WatchtowerPersister {
345
+ fn persist_new_channel ( & self , funding_txo : OutPoint , data : & channelmonitor:: ChannelMonitor < Signer > , id : MonitorUpdateId ) -> chain:: ChannelMonitorUpdateStatus {
346
+ assert ! ( self . channel_watchtower_state. lock( ) . unwrap( )
347
+ . insert( funding_txo, HashMap :: new( ) ) . is_none( ) ) ;
348
+
349
+ // No ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTxInfo is persisted on channel creation
350
+ // so we get the same info and update the watchtower
351
+ let commitment_number = ( 1u64 << 48 ) - 1 ;
352
+ let output_idx = 0 ;
353
+ let commitment_txid = data. get_current_counterparty_commitment_txid ( ) . expect ( "Should have at least one counterparty commitment tx" ) ;
354
+ let value = data. initial_to_counterparty_value ( ) ;
355
+ if value != 0 {
356
+ assert ! ( self . channel_watchtower_state. lock( ) . unwrap( ) . get_mut( & funding_txo) . unwrap( )
357
+ . insert( commitment_number, WatchtowerState :: CounterpartyCommitmentTxSeen {
358
+ commitment_txid,
359
+ output_idx,
360
+ value,
361
+ } ) . is_none( ) ) ;
362
+ }
363
+
364
+ self . persister . persist_new_channel ( funding_txo, data, id)
365
+ }
366
+
367
+ fn update_persisted_channel ( & self , funding_txo : OutPoint , update : Option < & channelmonitor:: ChannelMonitorUpdate > , data : & channelmonitor:: ChannelMonitor < Signer > , update_id : MonitorUpdateId ) -> chain:: ChannelMonitorUpdateStatus {
368
+ if let Some ( up) = update {
369
+ for step in up. updates . iter ( ) {
370
+ match step {
371
+ channelmonitor:: ChannelMonitorUpdateStep :: LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs : _, commitment_number, their_per_commitment_point, non_htlc_outputs } => {
372
+ // Find the revokeable output and store the data needed to build the
373
+ // justice tx later
374
+ let revokeable_redeemscript = data. get_revokeable_redeemscript ( * their_per_commitment_point) ;
375
+ let revokeable_p2wsh = revokeable_redeemscript. to_v0_p2wsh ( ) ;
376
+ let ( output_idx, output) = match non_htlc_outputs. iter ( ) . enumerate ( )
377
+ . find ( |( _, output) | output. script_pubkey == revokeable_p2wsh) {
378
+ Some ( ( idx, output) ) => ( idx, output) ,
379
+ None => {
380
+ println ! ( "No revokeable output found, revokeable output must be below dust limit." ) ;
381
+ continue ;
382
+ } ,
383
+ } ;
384
+
385
+ assert ! ( self . channel_watchtower_state. lock( ) . unwrap( ) . get_mut( & funding_txo) . unwrap( )
386
+ . insert( * commitment_number, WatchtowerState :: CounterpartyCommitmentTxSeen {
387
+ commitment_txid: * commitment_txid,
388
+ output_idx: output_idx as u32 ,
389
+ value: output. value,
390
+ } ) . is_none( ) ) ;
391
+ } ,
392
+ channelmonitor:: ChannelMonitorUpdateStep :: CommitmentSecret { idx, secret } => {
393
+ // Build the justice tx and store it
394
+ let mut channel_watchtower_state = self . channel_watchtower_state . lock ( ) . unwrap ( ) ;
395
+ let justice_tx = match channel_watchtower_state. get ( & funding_txo) . unwrap ( ) . get ( idx) {
396
+ Some ( WatchtowerState :: CounterpartyCommitmentTxSeen { commitment_txid, output_idx, value } ) => {
397
+ let justice_tx = self . build_justice_tx ( * commitment_txid, * output_idx, * value) ;
398
+ self . sign_justice_tx ( justice_tx, secret, * value, data) . expect ( "Should be able to sign justice tx" )
399
+ } ,
400
+ _ => {
401
+ println ! ( "No CounterpartyCommitmentTxSeen found, revokeable output must have been below dust limit." ) ;
402
+ continue ;
403
+ } ,
404
+ } ;
405
+
406
+ channel_watchtower_state. get_mut ( & funding_txo) . unwrap ( )
407
+ . insert ( * idx, WatchtowerState :: JusticeTxFormed ( justice_tx) )
408
+ . expect ( "Should only happen after previous update" ) ;
409
+ } ,
410
+ _ => { } ,
411
+ }
412
+ }
413
+ }
414
+
415
+ self . persister . update_persisted_channel ( funding_txo, update, data, update_id)
416
+ }
417
+ }
418
+
267
419
pub struct TestPersister {
268
420
/// The queue of update statuses we'll return. If none are queued, ::Completed will always be
269
421
/// returned.
0 commit comments