1
0

lightning: change derivation of funding_pubkey

Ideally, given an on-chain backup, after the remote force-closes, we should be able to spend our anchor output,
to CPFP the remote commitment tx (assuming the channel used OPTION_ANCHORS).
To spend the anchor output, we need to be able to sign with the local funding_privkey.

Previously we derived the funding_key from the channel_seed (which comes from os.urandom).
Prior to anchors, there was no use case for signing with the funding_key given a channel backup.
Now with anchors, we should make its derivation deterministic somehow, in a way so that it can
be derived given just an on-chain backup.
- one way would be to put some more data into the existing OP_RETURN
  - uses block space
  - the OP_RETURNs can be disabled via "use_recoverable_channels"
  - only the initiator can use OP_RETURNs (so what if channel is in incoming dir?)
- instead, new scheme for our funding_key:
  - we derive the funding_privkey from the lnworker root secret (derived from our bip32 seed)
  - for outgoing channels:
    - lnworker_root_secret + remote_node_id + funding_tx_nlocktime
  - for incoming channels:
    - lnworker_root_secret + remote_node_id + remote_funding_pubkey
  - a check is added to avoid reusing the same key between channels:
      not letting to user open more than one channel with the same peer in a single block
  - only the first 16 bytes of the remote_node_id are used, as the onchain backup OP_RETURNs only contain that
- as the funding_privkey cannot be derived from the channel_seed anymore, it is included in the
imported channel backups, which in turn need a new version defined
  - a wallet db upgrade is used to update already stored imported cbs
  - alternatively we could keep the imported cbs as-is, so no new version, no new funding_privkey field, as it is clearly somewhat redundant given on-chain backups can reconstruct it
    - however adding the field seems easier
      - otherwise the existing code would try to derive the funding_privkey from the channel_seed
      - also note: atm there is no field in the imported backups to distinguish anchor channels vs static-remotekey channels
This commit is contained in:
SomberNight
2025-01-14 16:14:01 +00:00
parent 8f5b395ddc
commit cba073dfd1
10 changed files with 190 additions and 24 deletions

View File

@@ -415,6 +415,10 @@ class AbstractChannel(Logger, ABC):
def get_funding_address(self) -> str:
pass
def get_funding_tx(self) -> Optional[Transaction]:
funding_txid = self.funding_outpoint.txid
return self.lnworker.lnwatcher.adb.get_transaction(funding_txid)
@abstractmethod
def get_sweep_address(self) -> str:
"""Returns a wallet address we can use to sweep coins to.
@@ -534,6 +538,12 @@ class ChannelBackup(AbstractChannel):
self.logger.warning(
f"local_payment_pubkey missing from (old-type) channel backup. "
f"You should export and re-import a newer backup.")
multisig_funding_keypair = None
if multisig_funding_secret := cb.multisig_funding_privkey:
multisig_funding_keypair = Keypair(
privkey=multisig_funding_secret,
pubkey=ecc.ECPrivkey(multisig_funding_secret).get_public_key_bytes(),
)
self.config[LOCAL] = LocalConfig.from_seed(
channel_seed=cb.channel_seed,
to_self_delay=cb.local_delay,
@@ -542,6 +552,7 @@ class ChannelBackup(AbstractChannel):
# 2. static_remotekey: to_remote sweep not necessary due to wallet address
# 3. anchor outputs: sweep to_remote by deriving the key from the funding pubkeys
static_remotekey=local_payment_pubkey,
multisig_key=multisig_funding_keypair,
# dummy values
static_payment_key=None,
dust_limit_sat=None,
@@ -594,7 +605,9 @@ class ChannelBackup(AbstractChannel):
return True
def create_sweeptxs_for_their_ctx(self, ctx):
return sweep_their_ctx_to_remote_backup(chan=self, ctx=ctx)
funding_tx = self.get_funding_tx()
assert funding_tx
return sweep_their_ctx_to_remote_backup(chan=self, ctx=ctx, funding_tx=funding_tx)
def create_sweeptxs_for_our_ctx(self, ctx):
if self.is_imported: