support option_static_remotekey
This commit is contained in:
@@ -230,6 +230,9 @@ class Channel(Logger):
|
|||||||
self._chan_ann_without_sigs = chan_ann
|
self._chan_ann_without_sigs = chan_ann
|
||||||
return 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):
|
def set_short_channel_id(self, short_id):
|
||||||
self.short_channel_id = short_id
|
self.short_channel_id = short_id
|
||||||
self.storage["short_channel_id"] = short_id
|
self.storage["short_channel_id"] = short_id
|
||||||
@@ -766,7 +769,11 @@ class Channel(Logger):
|
|||||||
feerate,
|
feerate,
|
||||||
self.constraints.is_initiator == (subject == LOCAL),
|
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(
|
return make_commitment(
|
||||||
ctn,
|
ctn,
|
||||||
this_config.multisig_key.pubkey,
|
this_config.multisig_key.pubkey,
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class Peer(Logger):
|
|||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
if isinstance(self.transport, LNTransport):
|
if isinstance(self.transport, LNTransport):
|
||||||
await self.transport.handshake()
|
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
|
self._sent_init = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -461,6 +461,9 @@ class Peer(Logger):
|
|||||||
pass
|
pass
|
||||||
self.lnworker.peer_closed(self)
|
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:
|
def make_local_config(self, funding_sat: int, push_msat: int, initiator: HTLCOwner) -> LocalConfig:
|
||||||
# key derivation
|
# key derivation
|
||||||
channel_counter = self.lnworker.get_and_inc_counter_for_channel_keys()
|
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
|
initial_msat = funding_sat * 1000 - push_msat
|
||||||
else:
|
else:
|
||||||
initial_msat = push_msat
|
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(
|
local_config=LocalConfig(
|
||||||
payment_basepoint=keypair_generator(LnKeyFamily.PAYMENT_BASE),
|
payment_basepoint=payment_basepoint,
|
||||||
multisig_key=keypair_generator(LnKeyFamily.MULTISIG),
|
multisig_key=keypair_generator(LnKeyFamily.MULTISIG),
|
||||||
htlc_basepoint=keypair_generator(LnKeyFamily.HTLC_BASE),
|
htlc_basepoint=keypair_generator(LnKeyFamily.HTLC_BASE),
|
||||||
delayed_basepoint=keypair_generator(LnKeyFamily.DELAY_BASE),
|
delayed_basepoint=keypair_generator(LnKeyFamily.DELAY_BASE),
|
||||||
@@ -615,6 +626,7 @@ class Peer(Logger):
|
|||||||
'data_loss_protect_remote_pcp': {},
|
'data_loss_protect_remote_pcp': {},
|
||||||
"log": {},
|
"log": {},
|
||||||
"revocation_store": {},
|
"revocation_store": {},
|
||||||
|
"static_remotekey_enabled": self.is_static_remotekey(), # stored because it cannot be "downgraded", per BOLT2
|
||||||
}
|
}
|
||||||
channel_id = chan_dict.get('channel_id')
|
channel_id = chan_dict.get('channel_id')
|
||||||
channels = self.lnworker.db.get_dict('channels')
|
channels = self.lnworker.db.get_dict('channels')
|
||||||
@@ -729,12 +741,16 @@ class Peer(Logger):
|
|||||||
next_remote_ctn = chan.get_next_ctn(REMOTE)
|
next_remote_ctn = chan.get_next_ctn(REMOTE)
|
||||||
assert self.localfeatures & LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
|
assert self.localfeatures & LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
|
||||||
# send message
|
# 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:
|
if oldest_unrevoked_remote_ctn == 0:
|
||||||
last_rev_secret = 0
|
last_rev_secret = 0
|
||||||
else:
|
else:
|
||||||
last_rev_index = oldest_unrevoked_remote_ctn - 1
|
last_rev_index = oldest_unrevoked_remote_ctn - 1
|
||||||
last_rev_secret = chan.revocation_store.retrieve_secret(RevocationStore.START_INDEX - last_rev_index)
|
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(
|
self.send_message(
|
||||||
"channel_reestablish",
|
"channel_reestablish",
|
||||||
channel_id=chan_id,
|
channel_id=chan_id,
|
||||||
@@ -824,6 +840,8 @@ class Peer(Logger):
|
|||||||
if our_pcs != their_claim_of_our_last_per_commitment_secret:
|
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)}")
|
self.logger.error(f"channel_reestablish: (DLP) local PCS mismatch: {bh2u(our_pcs)} != {bh2u(their_claim_of_our_last_per_commitment_secret)}")
|
||||||
return False
|
return False
|
||||||
|
if chan.is_static_remotekey_enabled():
|
||||||
|
return True
|
||||||
try:
|
try:
|
||||||
__, our_remote_pcp = chan.get_secret_and_point(REMOTE, their_next_local_ctn - 1)
|
__, our_remote_pcp = chan.get_secret_and_point(REMOTE, their_next_local_ctn - 1)
|
||||||
except RemoteCtnTooFarInFuture:
|
except RemoteCtnTooFarInFuture:
|
||||||
|
|||||||
@@ -324,7 +324,9 @@ def create_sweeptxs_for_their_ctx(*, chan: 'Channel', ctx: Transaction,
|
|||||||
witness_script = bh2u(make_commitment_output_to_local_witness_script(
|
witness_script = bh2u(make_commitment_output_to_local_witness_script(
|
||||||
our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey))
|
our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey))
|
||||||
to_local_address = redeem_script_to_address('p2wsh', witness_script)
|
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)
|
to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey)
|
||||||
# test if this is their ctx
|
# test if this is their ctx
|
||||||
_logger.debug(f'testing their ctx: {to_local_address} {to_remote_address}')
|
_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 = 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)
|
our_htlc_privkey = ecc.ECPrivkey.from_secret_scalar(our_htlc_privkey)
|
||||||
their_htlc_pubkey = derive_pubkey(their_conf.htlc_basepoint.pubkey, their_pcp)
|
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_local is handled by lnwatcher
|
||||||
# to_remote
|
# to_remote
|
||||||
output_idxs = ctx.get_output_idxs_from_address(to_remote_address)
|
if not chan.is_static_remotekey_enabled():
|
||||||
if output_idxs:
|
our_payment_bp_privkey = ecc.ECPrivkey(our_conf.payment_basepoint.privkey)
|
||||||
output_idx = output_idxs.pop()
|
our_payment_privkey = derive_privkey(our_payment_bp_privkey.secret_scalar, their_pcp)
|
||||||
prevout = ctx.txid() + ':%d'%output_idx
|
our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey)
|
||||||
sweep_tx = lambda: create_sweeptx_their_ctx_to_remote(
|
assert our_payment_pubkey == our_payment_privkey.get_public_key_bytes(compressed=True)
|
||||||
sweep_address=sweep_address,
|
output_idxs = ctx.get_output_idxs_from_address(to_remote_address)
|
||||||
ctx=ctx,
|
if output_idxs:
|
||||||
output_idx=output_idx,
|
output_idx = output_idxs.pop()
|
||||||
our_payment_privkey=our_payment_privkey,
|
prevout = ctx.txid() + ':%d'%output_idx
|
||||||
config=chan.lnworker.config)
|
sweep_tx = lambda: create_sweeptx_their_ctx_to_remote(
|
||||||
txs[prevout] = SweepInfo(name='their_ctx_to_remote',
|
sweep_address=sweep_address,
|
||||||
csv_delay=0,
|
ctx=ctx,
|
||||||
cltv_expiry=0,
|
output_idx=output_idx,
|
||||||
gen_tx=sweep_tx)
|
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
|
# HTLCs
|
||||||
def create_sweeptx_for_htlc(htlc: 'UpdateAddHtlc', is_received_htlc: bool,
|
def create_sweeptx_for_htlc(htlc: 'UpdateAddHtlc', is_received_htlc: bool,
|
||||||
ctx_output_idx: int) -> None:
|
ctx_output_idx: int) -> None:
|
||||||
|
|||||||
@@ -630,6 +630,8 @@ class LnLocalFeatures(IntFlag):
|
|||||||
OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT = 1 << 5
|
OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT = 1 << 5
|
||||||
GOSSIP_QUERIES_REQ = 1 << 6
|
GOSSIP_QUERIES_REQ = 1 << 6
|
||||||
GOSSIP_QUERIES_OPT = 1 << 7
|
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
|
# note that these are powers of two, not the bits themselves
|
||||||
LN_LOCAL_FEATURES_KNOWN_SET = set(LnLocalFeatures)
|
LN_LOCAL_FEATURES_KNOWN_SET = set(LnLocalFeatures)
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ class LNWorker(Logger):
|
|||||||
# note that e.g. DATA_LOSS_PROTECT is needed for LNGossip as many peers require it
|
# note that e.g. DATA_LOSS_PROTECT is needed for LNGossip as many peers require it
|
||||||
self.localfeatures = LnLocalFeatures(0)
|
self.localfeatures = LnLocalFeatures(0)
|
||||||
self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
|
self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_OPT
|
||||||
|
self.localfeatures |= LnLocalFeatures.OPTION_STATIC_REMOTEKEY_OPT
|
||||||
|
|
||||||
def channels_for_peer(self, node_id):
|
def channels_for_peer(self, node_id):
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
Reference in New Issue
Block a user