commit 2255b0715704bf9e32a4e5b9d1fda511be7b5938
parent 47d14c579b0e49cd1c29c416a6b3f69f5bd9fd9e
Author: ThomasV <thomasv@electrum.org>
Date: Fri, 13 Dec 2019 14:07:11 +0100
support option_static_remotekey
Diffstat:
5 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
@@ -230,6 +230,9 @@ class Channel(Logger):
self._chan_ann_without_sigs = chan_ann
return chan_ann
+ def is_static_remotekey_enabled(self):
+ return self.storage.get('static_remotekey_enabled')
+
def set_short_channel_id(self, short_id):
self.short_channel_id = short_id
self.storage["short_channel_id"] = short_id
@@ -766,7 +769,11 @@ class Channel(Logger):
feerate,
self.constraints.is_initiator == (subject == LOCAL),
)
- payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point)
+ if self.is_static_remotekey_enabled():
+ payment_pubkey = other_config.payment_basepoint.pubkey
+ else:
+ payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point)
+
return make_commitment(
ctn,
this_config.multisig_key.pubkey,
diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
@@ -119,7 +119,7 @@ class Peer(Logger):
async def initialize(self):
if isinstance(self.transport, LNTransport):
await self.transport.handshake()
- self.send_message("init", gflen=0, lflen=1, localfeatures=self.localfeatures)
+ self.send_message("init", gflen=0, lflen=2, localfeatures=self.localfeatures)
self._sent_init = True
@property
@@ -461,6 +461,9 @@ class Peer(Logger):
pass
self.lnworker.peer_closed(self)
+ def is_static_remotekey(self):
+ return bool(self.localfeatures & LnLocalFeatures.OPTION_STATIC_REMOTEKEY_OPT)
+
def make_local_config(self, funding_sat: int, push_msat: int, initiator: HTLCOwner) -> LocalConfig:
# key derivation
channel_counter = self.lnworker.get_and_inc_counter_for_channel_keys()
@@ -469,8 +472,16 @@ class Peer(Logger):
initial_msat = funding_sat * 1000 - push_msat
else:
initial_msat = push_msat
+
+ if self.is_static_remotekey():
+ addr = self.lnworker.wallet.get_unused_address()
+ static_key = self.lnworker.wallet.get_public_key(addr) # just a pubkey
+ payment_basepoint = OnlyPubkeyKeypair(bfh(static_key))
+ else:
+ payment_basepoint = keypair_generator(LnKeyFamily.PAYMENT_BASE)
+
local_config=LocalConfig(
- payment_basepoint=keypair_generator(LnKeyFamily.PAYMENT_BASE),
+ payment_basepoint=payment_basepoint,
multisig_key=keypair_generator(LnKeyFamily.MULTISIG),
htlc_basepoint=keypair_generator(LnKeyFamily.HTLC_BASE),
delayed_basepoint=keypair_generator(LnKeyFamily.DELAY_BASE),
@@ -615,6 +626,7 @@ class Peer(Logger):
'data_loss_protect_remote_pcp': {},
"log": {},
"revocation_store": {},
+ "static_remotekey_enabled": self.is_static_remotekey(), # stored because it cannot be "downgraded", per BOLT2
}
channel_id = chan_dict.get('channel_id')
channels = self.lnworker.db.get_dict('channels')
@@ -729,12 +741,16 @@ class Peer(Logger):
next_remote_ctn = chan.get_next_ctn(REMOTE)
assert self.localfeatures & LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
# send message
+ srk_enabled = chan.is_static_remotekey_enabled()
+ if srk_enabled:
+ latest_secret, latest_point = chan.get_secret_and_point(LOCAL, 0)
+ else:
+ latest_secret, latest_point = chan.get_secret_and_point(LOCAL, latest_local_ctn)
if oldest_unrevoked_remote_ctn == 0:
last_rev_secret = 0
else:
last_rev_index = oldest_unrevoked_remote_ctn - 1
last_rev_secret = chan.revocation_store.retrieve_secret(RevocationStore.START_INDEX - last_rev_index)
- latest_secret, latest_point = chan.get_secret_and_point(LOCAL, latest_local_ctn)
self.send_message(
"channel_reestablish",
channel_id=chan_id,
@@ -824,6 +840,8 @@ class Peer(Logger):
if our_pcs != their_claim_of_our_last_per_commitment_secret:
self.logger.error(f"channel_reestablish: (DLP) local PCS mismatch: {bh2u(our_pcs)} != {bh2u(their_claim_of_our_last_per_commitment_secret)}")
return False
+ if chan.is_static_remotekey_enabled():
+ return True
try:
__, our_remote_pcp = chan.get_secret_and_point(REMOTE, their_next_local_ctn - 1)
except RemoteCtnTooFarInFuture:
diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py
@@ -324,7 +324,9 @@ def create_sweeptxs_for_their_ctx(*, chan: 'Channel', ctx: Transaction,
witness_script = bh2u(make_commitment_output_to_local_witness_script(
our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey))
to_local_address = redeem_script_to_address('p2wsh', witness_script)
- our_payment_pubkey = derive_pubkey(our_conf.payment_basepoint.pubkey, their_pcp)
+ # to remote address
+ bpk = our_conf.payment_basepoint.pubkey
+ our_payment_pubkey = bpk if chan.is_static_remotekey_enabled() else derive_pubkey(bpk, their_pcp)
to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey)
# test if this is their ctx
_logger.debug(f'testing their ctx: {to_local_address} {to_remote_address}')
@@ -345,26 +347,27 @@ def create_sweeptxs_for_their_ctx(*, chan: 'Channel', ctx: Transaction,
our_htlc_privkey = derive_privkey(secret=int.from_bytes(our_conf.htlc_basepoint.privkey, 'big'), per_commitment_point=their_pcp)
our_htlc_privkey = ecc.ECPrivkey.from_secret_scalar(our_htlc_privkey)
their_htlc_pubkey = derive_pubkey(their_conf.htlc_basepoint.pubkey, their_pcp)
- our_payment_bp_privkey = ecc.ECPrivkey(our_conf.payment_basepoint.privkey)
- our_payment_privkey = derive_privkey(our_payment_bp_privkey.secret_scalar, their_pcp)
- our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey)
- assert our_payment_pubkey == our_payment_privkey.get_public_key_bytes(compressed=True)
# to_local is handled by lnwatcher
# to_remote
- output_idxs = ctx.get_output_idxs_from_address(to_remote_address)
- if output_idxs:
- output_idx = output_idxs.pop()
- prevout = ctx.txid() + ':%d'%output_idx
- sweep_tx = lambda: create_sweeptx_their_ctx_to_remote(
- sweep_address=sweep_address,
- ctx=ctx,
- output_idx=output_idx,
- our_payment_privkey=our_payment_privkey,
- config=chan.lnworker.config)
- txs[prevout] = SweepInfo(name='their_ctx_to_remote',
- csv_delay=0,
- cltv_expiry=0,
- gen_tx=sweep_tx)
+ if not chan.is_static_remotekey_enabled():
+ our_payment_bp_privkey = ecc.ECPrivkey(our_conf.payment_basepoint.privkey)
+ our_payment_privkey = derive_privkey(our_payment_bp_privkey.secret_scalar, their_pcp)
+ our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey)
+ assert our_payment_pubkey == our_payment_privkey.get_public_key_bytes(compressed=True)
+ output_idxs = ctx.get_output_idxs_from_address(to_remote_address)
+ if output_idxs:
+ output_idx = output_idxs.pop()
+ prevout = ctx.txid() + ':%d'%output_idx
+ sweep_tx = lambda: create_sweeptx_their_ctx_to_remote(
+ sweep_address=sweep_address,
+ ctx=ctx,
+ output_idx=output_idx,
+ our_payment_privkey=our_payment_privkey,
+ config=chan.lnworker.config)
+ txs[prevout] = SweepInfo(name='their_ctx_to_remote',
+ csv_delay=0,
+ cltv_expiry=0,
+ gen_tx=sweep_tx)
# HTLCs
def create_sweeptx_for_htlc(htlc: 'UpdateAddHtlc', is_received_htlc: bool,
ctx_output_idx: int) -> None:
diff --git a/electrum/lnutil.py b/electrum/lnutil.py
@@ -630,6 +630,8 @@ class LnLocalFeatures(IntFlag):
OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT = 1 << 5
GOSSIP_QUERIES_REQ = 1 << 6
GOSSIP_QUERIES_OPT = 1 << 7
+ OPTION_STATIC_REMOTEKEY_REQ = 1 << 12
+ OPTION_STATIC_REMOTEKEY_OPT = 1 << 13
# note that these are powers of two, not the bits themselves
LN_LOCAL_FEATURES_KNOWN_SET = set(LnLocalFeatures)
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
@@ -130,6 +130,7 @@ class LNWorker(Logger):
# note that e.g. DATA_LOSS_PROTECT is needed for LNGossip as many peers require it
self.localfeatures = LnLocalFeatures(0)
self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
+ self.localfeatures |= LnLocalFeatures.OPTION_STATIC_REMOTEKEY_OPT
def channels_for_peer(self, node_id):
return {}