add static payment key
* in order to be able to sweep to_remote in an onchain backup scenario we need to retain the private key for the payment_basepoint * to facilitate the above, we open a channel derived from a static secret (tied to the wallet seed), the static_payment_key combined with the funding pubkey (multisig_key), which we can restore from the channel closing transaction
This commit is contained in:
@@ -399,7 +399,6 @@ class AbstractChannel(Logger, ABC):
|
||||
# auto-remove redeemed backups
|
||||
self.lnworker.remove_channel_backup(self.channel_id)
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def is_initiator(self) -> bool:
|
||||
pass
|
||||
@@ -538,8 +537,13 @@ class ChannelBackup(AbstractChannel):
|
||||
self.config[LOCAL] = LocalConfig.from_seed(
|
||||
channel_seed=cb.channel_seed,
|
||||
to_self_delay=cb.local_delay,
|
||||
# there are three cases of backups:
|
||||
# 1. legacy: payment_basepoint will be derived
|
||||
# 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,
|
||||
# dummy values
|
||||
static_payment_key=None,
|
||||
dust_limit_sat=None,
|
||||
max_htlc_value_in_flight_msat=None,
|
||||
max_accepted_htlcs=None,
|
||||
@@ -635,6 +639,9 @@ class ChannelBackup(AbstractChannel):
|
||||
def get_sweep_address(self) -> str:
|
||||
return self.lnworker.wallet.get_new_sweep_address_for_channel()
|
||||
|
||||
def has_anchors(self) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
def get_local_pubkey(self) -> bytes:
|
||||
cb = self.cb
|
||||
assert isinstance(cb, ChannelBackupStorage)
|
||||
|
||||
@@ -681,11 +681,16 @@ class Peer(Logger, EventListener):
|
||||
# flexibility to decide an address at closing time
|
||||
upfront_shutdown_script = b''
|
||||
|
||||
assert channel_type & channel_type.OPTION_STATIC_REMOTEKEY
|
||||
wallet = self.lnworker.wallet
|
||||
assert wallet.txin_type == 'p2wpkh'
|
||||
addr = wallet.get_new_sweep_address_for_channel()
|
||||
static_remotekey = bytes.fromhex(wallet.get_public_key(addr))
|
||||
if self.use_anchors():
|
||||
static_payment_key = self.lnworker.static_payment_key
|
||||
static_remotekey = None
|
||||
else:
|
||||
assert channel_type & channel_type.OPTION_STATIC_REMOTEKEY
|
||||
wallet = self.lnworker.wallet
|
||||
assert wallet.txin_type == 'p2wpkh'
|
||||
addr = wallet.get_new_sweep_address_for_channel()
|
||||
static_payment_key = None
|
||||
static_remotekey = bytes.fromhex(wallet.get_public_key(addr))
|
||||
|
||||
dust_limit_sat = bitcoin.DUST_LIMIT_P2PKH
|
||||
reserve_sat = max(funding_sat // 100, dust_limit_sat)
|
||||
@@ -696,6 +701,7 @@ class Peer(Logger, EventListener):
|
||||
local_config = LocalConfig.from_seed(
|
||||
channel_seed=channel_seed,
|
||||
static_remotekey=static_remotekey,
|
||||
static_payment_key=static_payment_key,
|
||||
upfront_shutdown_script=upfront_shutdown_script,
|
||||
to_self_delay=self.network.config.LIGHTNING_TO_SELF_DELAY_CSV,
|
||||
dust_limit_sat=dust_limit_sat,
|
||||
|
||||
@@ -212,7 +212,6 @@ class LocalConfig(ChannelConfig):
|
||||
@classmethod
|
||||
def from_seed(cls, **kwargs):
|
||||
channel_seed = kwargs['channel_seed']
|
||||
static_remotekey = kwargs.pop('static_remotekey')
|
||||
node = BIP32Node.from_rootseed(channel_seed, xtype='standard')
|
||||
keypair_generator = lambda family: generate_keypair(node, family)
|
||||
kwargs['per_commitment_secret_seed'] = keypair_generator(LnKeyFamily.REVOCATION_ROOT).privkey
|
||||
@@ -220,11 +219,23 @@ class LocalConfig(ChannelConfig):
|
||||
kwargs['htlc_basepoint'] = keypair_generator(LnKeyFamily.HTLC_BASE)
|
||||
kwargs['delayed_basepoint'] = keypair_generator(LnKeyFamily.DELAY_BASE)
|
||||
kwargs['revocation_basepoint'] = keypair_generator(LnKeyFamily.REVOCATION_BASE)
|
||||
if static_remotekey:
|
||||
static_remotekey = kwargs.pop('static_remotekey')
|
||||
static_payment_key = kwargs.pop('static_payment_key')
|
||||
if static_payment_key:
|
||||
# We derive the payment_basepoint from a static secret (derived from
|
||||
# the wallet seed) and a public nonce that is revealed
|
||||
# when the funding transaction is spent. This way we can restore the
|
||||
# payment_basepoint, needed for sweeping in the event of a force close.
|
||||
kwargs['payment_basepoint'] = derive_payment_basepoint(
|
||||
static_payment_secret=static_payment_key.privkey,
|
||||
funding_pubkey=kwargs['multisig_key'].pubkey
|
||||
)
|
||||
elif static_remotekey: # we automatically sweep to a wallet address
|
||||
kwargs['payment_basepoint'] = OnlyPubkeyKeypair(static_remotekey)
|
||||
else:
|
||||
# we expect all our channels to use option_static_remotekey, so ending up here likely indicates an issue...
|
||||
kwargs['payment_basepoint'] = keypair_generator(LnKeyFamily.PAYMENT_BASE)
|
||||
|
||||
return LocalConfig(**kwargs)
|
||||
|
||||
def validate_params(self, *, funding_sat: int, config: 'SimpleConfig', peer_features: 'LnFeatures') -> None:
|
||||
@@ -586,6 +597,16 @@ def derive_blinded_privkey(basepoint_secret: bytes, per_commitment_secret: bytes
|
||||
return int.to_bytes(sum, length=32, byteorder='big', signed=False)
|
||||
|
||||
|
||||
def derive_payment_basepoint(static_payment_secret: bytes, funding_pubkey: bytes) -> Keypair:
|
||||
assert isinstance(static_payment_secret, bytes)
|
||||
assert isinstance(funding_pubkey, bytes)
|
||||
payment_basepoint = ecc.ECPrivkey(sha256(static_payment_secret + funding_pubkey))
|
||||
return Keypair(
|
||||
pubkey=payment_basepoint.get_public_key_bytes(),
|
||||
privkey=payment_basepoint.get_secret_bytes()
|
||||
)
|
||||
|
||||
|
||||
def make_htlc_tx_output(
|
||||
amount_msat, local_feerate, revocationpubkey, local_delayedpubkey, success, to_self_delay,
|
||||
) -> Tuple[bytes, PartialTxOutput]:
|
||||
|
||||
@@ -829,6 +829,7 @@ class LNWallet(LNWorker):
|
||||
self.db = wallet.db
|
||||
self.node_keypair = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.NODE_KEY)
|
||||
self.backup_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.BACKUP_CIPHER).privkey
|
||||
self.static_payment_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.PAYMENT_BASE)
|
||||
self.payment_secret_key = generate_keypair(BIP32Node.from_xkey(xprv), LnKeyFamily.PAYMENT_SECRET_KEY).privkey
|
||||
Logger.__init__(self)
|
||||
features = LNWALLET_FEATURES
|
||||
|
||||
Reference in New Issue
Block a user