2020 RevocationStore , extract_ctn_from_tx_and_chan , UnableToDeriveSecret , SENT , RECEIVED ,
2121 map_htlcs_to_ctx_output_idxs , Direction , make_commitment_output_to_remote_witness_script ,
2222 derive_payment_basepoint , ctx_has_anchors , SCRIPT_TEMPLATE_FUNDING , Keypair ,
23- derive_multisig_funding_key_if_we_opened , derive_multisig_funding_key_if_they_opened )
23+ derive_multisig_funding_key_if_we_opened , derive_multisig_funding_key_if_they_opened , LocalConfig )
2424from .transaction import (Transaction , TxInput , PartialTxInput ,
2525 PartialTxOutput , TxOutpoint , script_GetOp , match_script_against_template )
2626from .logging import get_logger , Logger
@@ -571,55 +571,70 @@ def sweep_their_ctx_to_remote_backup(
571571 * , chan : 'ChannelBackup' ,
572572 ctx : Transaction ,
573573 funding_tx : Transaction ,
574- ) -> Optional [Dict [str , SweepInfo ]]:
575- txs = {} # type: Dict[str, SweepInfo]
574+ ) -> Dict [str , SweepInfo ]:
576575 """If we only have a backup, and the remote force-closed with their ctx,
577576 and anchors are enabled, we need to sweep to_remote."""
578577
578+ txs = {} # type: Dict[str, SweepInfo]
579+ local_config = chan .config .get (LOCAL ) # type: Optional[LocalConfig]
580+ fp_idx = None # type: Optional[int]
579581 if ctx_has_anchors (ctx ):
580- # for anchors we need to sweep to_remote
581582 funding_pubkeys = extract_funding_pubkeys_from_ctx (ctx .inputs ()[0 ])
582- _logger . debug ( f'checking their ctx for funding pubkeys: { [ pk . hex () for pk in funding_pubkeys ] } ' )
583- # check which of the pubkey was ours
584- for fp_idx , pubkey in enumerate ( funding_pubkeys ):
585- candidate_basepoint = derive_payment_basepoint ( chan . lnworker . static_payment_key . privkey , funding_pubkey = pubkey )
586- candidate_to_remote_address = make_commitment_output_to_remote_address ( candidate_basepoint . pubkey , has_anchors = True )
587- if ctx . get_output_idxs_from_address ( candidate_to_remote_address ):
588- our_payment_pubkey = candidate_basepoint
589- to_remote_address = candidate_to_remote_address
590- _logger .debug (f' found funding pubkey' )
591- break
583+ # for anchors we need the payment_basepoint to spend the to_remote
584+ if local_config and isinstance ( local_config . payment_basepoint , Keypair ):
585+ _logger . debug ( "using payment_basepoint key from channel backup" )
586+ # if we have a channel backup v3+ the imported payment_basepoint is a private key for anchor channels
587+ # so non-deterministic LNWallets can recover their to_remote outputs
588+ our_payment_keypair = local_config . payment_basepoint
589+ to_remote_address = make_commitment_output_to_remote_address ( our_payment_keypair . pubkey , has_anchors = True )
590+ if not ctx . get_output_idxs_from_address ( to_remote_address ):
591+ _logger .debug (f"no to_remote output found for { to_remote_address = } from backup" )
592+ return {}
592593 else :
593- return
594+ # check which of the pubkey was ours
595+ # might be from a channel backup < v3, if LNWallet got seeded deterministically from an Electrum-type seed
596+ # the basepoint derivation is deterministic too. If they used a nondeterministic seed their funds are lost.
597+ _logger .debug (f'checking their ctx for funding pubkeys: { [pk .hex () for pk in funding_pubkeys ]} ' )
598+ for fp_idx , pubkey in enumerate (funding_pubkeys ):
599+ candidate_basepoint = derive_payment_basepoint (chan .lnworker .static_payment_key .privkey , funding_pubkey = pubkey )
600+ candidate_to_remote_address = make_commitment_output_to_remote_address (candidate_basepoint .pubkey , has_anchors = True )
601+ if ctx .get_output_idxs_from_address (candidate_to_remote_address ):
602+ our_payment_keypair = candidate_basepoint
603+ to_remote_address = candidate_to_remote_address
604+ _logger .debug (f'found funding pubkey' )
605+ break
606+ else :
607+ return {}
594608 else :
595609 # we are dealing with static_remotekey which is locked to a wallet address
596610 return {}
597611
598- # remote anchor
599- # derive funding_privkey ("multisig_key")
612+ # get remote anchor funding_privkey ("multisig_key")
600613 # note: for imported backups, we already have this as 'local_config.multisig_key'
601614 # but for on-chain backups, we need to derive it.
602- # For symmetry, we derive it now regardless of type
603- our_funding_pubkey = funding_pubkeys [fp_idx ]
604- their_funding_pubkey = funding_pubkeys [1 - fp_idx ]
605- remote_node_id = chan .node_id # for onchain backups, this is only the prefix
606- if chan .is_initiator ():
607- funding_kp_cand = derive_multisig_funding_key_if_we_opened (
608- funding_root_secret = chan .lnworker .funding_root_keypair .privkey ,
609- remote_node_id_or_prefix = remote_node_id ,
610- nlocktime = funding_tx .locktime ,
611- )
612- else :
613- funding_kp_cand = derive_multisig_funding_key_if_they_opened (
614- funding_root_secret = chan .lnworker .funding_root_keypair .privkey ,
615- remote_node_id_or_prefix = remote_node_id ,
616- remote_funding_pubkey = their_funding_pubkey ,
617- )
618- assert funding_kp_cand .pubkey == our_funding_pubkey , f"funding pubkey mismatch1. { chan .is_initiator ()= } "
619- our_ms_funding_keypair = funding_kp_cand
620- # sanity check funding_privkey, if we had it already (if backup is imported):
621- if local_config := chan .config .get (LOCAL ):
622- assert our_ms_funding_keypair == local_config .multisig_key , f"funding pubkey mismatch2. { chan .is_initiator ()= } "
615+ our_ms_funding_keypair = None
616+ if local_config and local_config .multisig_key .pubkey in funding_pubkeys :
617+ _logger .debug ("using multisig_key from channel backup to spend remote anchor" )
618+ our_ms_funding_keypair = local_config .multisig_key
619+ elif fp_idx is not None :
620+ _logger .debug ("found no multisig_key for remote anchor in channel backup, deriving key" )
621+ our_funding_pubkey = funding_pubkeys [fp_idx ]
622+ their_funding_pubkey = funding_pubkeys [1 - fp_idx ]
623+ remote_node_id = chan .node_id # for onchain backups, this is only the prefix
624+ if chan .is_initiator ():
625+ funding_kp_cand = derive_multisig_funding_key_if_we_opened (
626+ funding_root_secret = chan .lnworker .funding_root_keypair .privkey ,
627+ remote_node_id_or_prefix = remote_node_id ,
628+ nlocktime = funding_tx .locktime ,
629+ )
630+ else :
631+ funding_kp_cand = derive_multisig_funding_key_if_they_opened (
632+ funding_root_secret = chan .lnworker .funding_root_keypair .privkey ,
633+ remote_node_id_or_prefix = remote_node_id ,
634+ remote_funding_pubkey = their_funding_pubkey ,
635+ )
636+ assert funding_kp_cand .pubkey == our_funding_pubkey , f"funding pubkey mismatch1. { chan .is_initiator ()= } "
637+ our_ms_funding_keypair = funding_kp_cand
623638
624639 if our_ms_funding_keypair :
625640 if txin := sweep_ctx_anchor (ctx = ctx , multisig_key = our_ms_funding_keypair ):
@@ -633,7 +648,7 @@ def sweep_their_ctx_to_remote_backup(
633648 )
634649
635650 # to_remote
636- our_payment_privkey = ecc .ECPrivkey (our_payment_pubkey .privkey )
651+ our_payment_privkey = ecc .ECPrivkey (our_payment_keypair .privkey )
637652 output_idxs = ctx .get_output_idxs_from_address (to_remote_address )
638653 if output_idxs :
639654 output_idx = output_idxs .pop ()
0 commit comments