public channels:
- send node and channel announcements. - store channel_flags in constraints - store signatures in local and remote configs
This commit is contained in:
@@ -1137,11 +1137,15 @@ class Commands:
|
||||
} for p in lnworker.peers.values()]
|
||||
|
||||
@command('wpnl')
|
||||
async def open_channel(self, connection_string, amount, push_amount=0, password=None, wallet: Abstract_Wallet = None):
|
||||
async def open_channel(self, connection_string, amount, push_amount=0, public=False, password=None, wallet: Abstract_Wallet = None):
|
||||
funding_sat = satoshis(amount)
|
||||
push_sat = satoshis(push_amount)
|
||||
peer = await wallet.lnworker.add_peer(connection_string)
|
||||
chan, funding_tx = await wallet.lnworker.open_channel_with_peer(peer, funding_sat, push_sat, password)
|
||||
chan, funding_tx = await wallet.lnworker.open_channel_with_peer(
|
||||
peer, funding_sat,
|
||||
push_sat=push_sat,
|
||||
public=public,
|
||||
password=password)
|
||||
return chan.funding_outpoint.to_str()
|
||||
|
||||
@command('')
|
||||
@@ -1460,7 +1464,8 @@ command_options = {
|
||||
'from_amount': (None, "Amount to convert (default: 1)"),
|
||||
'from_ccy': (None, "Currency to convert from"),
|
||||
'to_ccy': (None, "Currency to convert to"),
|
||||
'unlock': (None, "Unlock the wallet (store the password in memory).")
|
||||
'unlock': (None, "Unlock the wallet (store the password in memory)."),
|
||||
'public': (None, 'Channel will be announced'),
|
||||
}
|
||||
|
||||
|
||||
|
||||
+28
-14
@@ -69,6 +69,8 @@ if TYPE_CHECKING:
|
||||
from .json_db import StoredDict
|
||||
from .lnrouter import RouteEdge
|
||||
|
||||
# channel flags
|
||||
CF_ANNOUNCE_CHANNEL = 0x01
|
||||
|
||||
# lightning channel states
|
||||
# Note: these states are persisted by name (for a given channel) in the wallet file,
|
||||
@@ -392,6 +394,10 @@ class AbstractChannel(Logger, ABC):
|
||||
def is_initiator(self) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_public(self) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_funding_tx_mined(self, funding_height: TxMinedInfo) -> bool:
|
||||
pass
|
||||
@@ -507,11 +513,13 @@ class ChannelBackup(AbstractChannel):
|
||||
initial_msat=None,
|
||||
reserve_sat=None,
|
||||
funding_locked_received=False,
|
||||
was_announced=False,
|
||||
current_commitment_signature=None,
|
||||
current_htlc_signatures=b'',
|
||||
htlc_minimum_msat=1,
|
||||
upfront_shutdown_script='')
|
||||
upfront_shutdown_script='',
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
)
|
||||
self.config[REMOTE] = RemoteConfig(
|
||||
# payment_basepoint needed to deobfuscate ctn in our_ctx
|
||||
payment_basepoint=OnlyPubkeyKeypair(cb.remote_payment_pubkey),
|
||||
@@ -530,7 +538,10 @@ class ChannelBackup(AbstractChannel):
|
||||
htlc_minimum_msat=None,
|
||||
next_per_commitment_point=None,
|
||||
current_per_commitment_point=None,
|
||||
upfront_shutdown_script='')
|
||||
upfront_shutdown_script='',
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
)
|
||||
|
||||
def can_be_deleted(self):
|
||||
return self.is_imported or self.is_redeemed()
|
||||
@@ -567,6 +578,9 @@ class ChannelBackup(AbstractChannel):
|
||||
def is_initiator(self):
|
||||
return self.cb.is_initiator
|
||||
|
||||
def is_public(self):
|
||||
return False
|
||||
|
||||
def get_oldest_unrevoked_ctn(self, who):
|
||||
return -1
|
||||
|
||||
@@ -647,13 +661,13 @@ class Channel(AbstractChannel):
|
||||
self.peer_state = PeerState.DISCONNECTED
|
||||
self._sweep_info = {}
|
||||
self._outgoing_channel_update = None # type: Optional[bytes]
|
||||
self._chan_ann_without_sigs = None # type: Optional[bytes]
|
||||
self.revocation_store = RevocationStore(state["revocation_store"])
|
||||
self._can_send_ctx_updates = True # type: bool
|
||||
self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailure)]
|
||||
self.should_request_force_close = False
|
||||
self.unconfirmed_closing_txid = None # not a state, only for GUI
|
||||
self.sent_channel_ready = False # no need to persist this, because channel_ready is re-sent in channel_reestablish
|
||||
self.sent_announcement_signatures = False
|
||||
|
||||
def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]:
|
||||
"""Get scid_alias to be used for *outgoing* HTLCs.
|
||||
@@ -688,6 +702,9 @@ class Channel(AbstractChannel):
|
||||
def get_capacity(self):
|
||||
return self.constraints.capacity
|
||||
|
||||
def is_public(self):
|
||||
return bool(self.constraints.flags & CF_ANNOUNCE_CHANNEL)
|
||||
|
||||
def is_initiator(self):
|
||||
return self.constraints.is_initiator
|
||||
|
||||
@@ -785,19 +802,14 @@ class Channel(AbstractChannel):
|
||||
return chan_upd
|
||||
|
||||
def construct_channel_announcement_without_sigs(self) -> bytes:
|
||||
if self._chan_ann_without_sigs is not None:
|
||||
return self._chan_ann_without_sigs
|
||||
if not self.lnworker:
|
||||
raise Exception('lnworker not set for channel!')
|
||||
|
||||
bitcoin_keys = [self.config[REMOTE].multisig_key.pubkey,
|
||||
self.config[LOCAL].multisig_key.pubkey]
|
||||
bitcoin_keys = [
|
||||
self.config[REMOTE].multisig_key.pubkey,
|
||||
self.config[LOCAL].multisig_key.pubkey]
|
||||
node_ids = [self.node_id, self.get_local_pubkey()]
|
||||
sorted_node_ids = list(sorted(node_ids))
|
||||
if sorted_node_ids != node_ids:
|
||||
node_ids = sorted_node_ids
|
||||
bitcoin_keys.reverse()
|
||||
|
||||
chan_ann = encode_msg(
|
||||
"channel_announcement",
|
||||
len=0,
|
||||
@@ -809,10 +821,12 @@ class Channel(AbstractChannel):
|
||||
bitcoin_key_1=bitcoin_keys[0],
|
||||
bitcoin_key_2=bitcoin_keys[1],
|
||||
)
|
||||
|
||||
self._chan_ann_without_sigs = chan_ann
|
||||
return chan_ann
|
||||
|
||||
def get_channel_announcement_hash(self):
|
||||
chan_ann = self.construct_channel_announcement_without_sigs()
|
||||
return sha256d(chan_ann[256+2:])
|
||||
|
||||
def is_static_remotekey_enabled(self) -> bool:
|
||||
channel_type = ChannelType(self.storage.get('channel_type'))
|
||||
return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY)
|
||||
|
||||
+103
-68
@@ -31,7 +31,7 @@ from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_pay
|
||||
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
|
||||
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
|
||||
OnionFailureCodeMetaFlag)
|
||||
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption
|
||||
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption, CF_ANNOUNCE_CHANNEL
|
||||
from . import lnutil
|
||||
from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConfig,
|
||||
RemoteConfig, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
|
||||
@@ -107,7 +107,6 @@ class Peer(Logger):
|
||||
self.funding_created_sent = set() # for channels in PREOPENING
|
||||
self.funding_signed_sent = set() # for channels in PREOPENING
|
||||
self.shutdown_received = {} # chan_id -> asyncio.Future()
|
||||
self.announcement_signatures = defaultdict(asyncio.Queue)
|
||||
self.channel_reestablish_msg = defaultdict(self.asyncio_loop.create_future)
|
||||
self.orphan_channel_updates = OrderedDict() # type: OrderedDict[ShortChannelID, dict]
|
||||
Logger.__init__(self)
|
||||
@@ -406,10 +405,17 @@ class Peer(Logger):
|
||||
self.orphan_channel_updates.popitem(last=False)
|
||||
|
||||
def on_announcement_signatures(self, chan: Channel, payload):
|
||||
if chan.config[LOCAL].was_announced:
|
||||
h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan)
|
||||
else:
|
||||
self.announcement_signatures[chan.channel_id].put_nowait(payload)
|
||||
h = chan.get_channel_announcement_hash()
|
||||
node_signature = payload["node_signature"]
|
||||
bitcoin_signature = payload["bitcoin_signature"]
|
||||
if not ecc.verify_signature(chan.config[REMOTE].multisig_key.pubkey, bitcoin_signature, h):
|
||||
raise Exception("bitcoin_sig invalid in announcement_signatures")
|
||||
if not ecc.verify_signature(self.pubkey, node_signature, h):
|
||||
raise Exception("node_sig invalid in announcement_signatures")
|
||||
chan.config[REMOTE].announcement_node_sig = node_signature
|
||||
chan.config[REMOTE].announcement_bitcoin_sig = bitcoin_signature
|
||||
self.lnworker.save_channel(chan)
|
||||
self.maybe_send_announcement_signatures(chan, is_reply=True)
|
||||
|
||||
def handle_disconnect(func):
|
||||
@functools.wraps(func)
|
||||
@@ -434,6 +440,7 @@ class Peer(Logger):
|
||||
await group.spawn(self.htlc_switch())
|
||||
await group.spawn(self.query_gossip())
|
||||
await group.spawn(self.process_gossip())
|
||||
await group.spawn(self.send_own_gossip())
|
||||
|
||||
async def process_gossip(self):
|
||||
while True:
|
||||
@@ -458,6 +465,20 @@ class Peer(Logger):
|
||||
if self.network.lngossip:
|
||||
await self.network.lngossip.process_gossip(chan_anns, node_anns, chan_upds)
|
||||
|
||||
async def send_own_gossip(self):
|
||||
if self.lnworker == self.lnworker.network.lngossip:
|
||||
return
|
||||
await asyncio.sleep(10)
|
||||
while True:
|
||||
public_channels = [chan for chan in self.lnworker.channels.values() if chan.is_public()]
|
||||
if public_channels:
|
||||
alias = self.lnworker.config.LIGHTNING_NODE_ALIAS
|
||||
self.send_node_announcement(alias)
|
||||
for chan in public_channels:
|
||||
if chan.is_open() and chan.peer_state == PeerState.GOOD:
|
||||
self.send_channel_announcement(chan)
|
||||
await asyncio.sleep(600)
|
||||
|
||||
async def query_gossip(self):
|
||||
try:
|
||||
await util.wait_for2(self.initialized, LN_P2P_NETWORK_TIMEOUT)
|
||||
@@ -654,10 +675,11 @@ class Peer(Logger):
|
||||
initial_msat=initial_msat,
|
||||
reserve_sat=reserve_sat,
|
||||
funding_locked_received=False,
|
||||
was_announced=False,
|
||||
current_commitment_signature=None,
|
||||
current_htlc_signatures=b'',
|
||||
htlc_minimum_msat=1,
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
)
|
||||
local_config.validate_params(funding_sat=funding_sat, config=self.network.config, peer_features=self.features)
|
||||
return local_config
|
||||
@@ -687,6 +709,7 @@ class Peer(Logger):
|
||||
funding_tx: 'PartialTransaction',
|
||||
funding_sat: int,
|
||||
push_msat: int,
|
||||
public: bool,
|
||||
temp_channel_id: bytes
|
||||
) -> Tuple[Channel, 'PartialTransaction']:
|
||||
"""Implements the channel opening flow.
|
||||
@@ -704,6 +727,10 @@ class Peer(Logger):
|
||||
if self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.pubkey):
|
||||
raise Exception('Not a trampoline node: ' + str(self.their_features))
|
||||
|
||||
if public and not self.lnworker.config.EXPERIMENTAL_LN_FORWARD_PAYMENTS:
|
||||
raise Exception('Cannot create public channels')
|
||||
|
||||
channel_flags = CF_ANNOUNCE_CHANNEL if public else 0
|
||||
feerate = self.lnworker.current_feerate_per_kw()
|
||||
# we set a channel type for internal bookkeeping
|
||||
open_channel_tlvs = {}
|
||||
@@ -753,7 +780,7 @@ class Peer(Logger):
|
||||
first_per_commitment_point=per_commitment_point_first,
|
||||
to_self_delay=local_config.to_self_delay,
|
||||
max_htlc_value_in_flight_msat=local_config.max_htlc_value_in_flight_msat,
|
||||
channel_flags=0x00, # not willing to announce channel
|
||||
channel_flags=channel_flags,
|
||||
channel_reserve_satoshis=local_config.reserve_sat,
|
||||
htlc_minimum_msat=local_config.htlc_minimum_msat,
|
||||
open_channel_tlvs=open_channel_tlvs,
|
||||
@@ -797,6 +824,8 @@ class Peer(Logger):
|
||||
next_per_commitment_point=remote_per_commitment_point,
|
||||
current_per_commitment_point=None,
|
||||
upfront_shutdown_script=upfront_shutdown_script,
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
)
|
||||
ChannelConfig.cross_validate_params(
|
||||
local_config=local_config,
|
||||
@@ -838,6 +867,7 @@ class Peer(Logger):
|
||||
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_index)
|
||||
outpoint = Outpoint(funding_txid, funding_index)
|
||||
constraints = ChannelConstraints(
|
||||
flags=channel_flags,
|
||||
capacity=funding_sat,
|
||||
is_initiator=True,
|
||||
funding_txn_minimum_depth=funding_txn_minimum_depth
|
||||
@@ -956,6 +986,8 @@ class Peer(Logger):
|
||||
next_per_commitment_point=payload['first_per_commitment_point'],
|
||||
current_per_commitment_point=None,
|
||||
upfront_shutdown_script=upfront_shutdown_script,
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
)
|
||||
ChannelConfig.cross_validate_params(
|
||||
local_config=local_config,
|
||||
@@ -967,9 +999,7 @@ class Peer(Logger):
|
||||
peer_features=self.features,
|
||||
)
|
||||
|
||||
# note: we ignore payload['channel_flags'], which e.g. contains 'announce_channel'.
|
||||
# Notably, if the remote sets 'announce_channel' to True, we will ignore that too,
|
||||
# but we will not play along with actually announcing the channel (so we keep it private).
|
||||
channel_flags = ord(payload['channel_flags'])
|
||||
|
||||
# -> accept channel
|
||||
# for the first commitment transaction
|
||||
@@ -1018,9 +1048,10 @@ class Peer(Logger):
|
||||
funding_txid = funding_created['funding_txid'][::-1].hex()
|
||||
channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_idx)
|
||||
constraints = ChannelConstraints(
|
||||
flags=channel_flags,
|
||||
capacity=funding_sat,
|
||||
is_initiator=False,
|
||||
funding_txn_minimum_depth=min_depth
|
||||
funding_txn_minimum_depth=min_depth,
|
||||
)
|
||||
outpoint = Outpoint(funding_txid, funding_idx)
|
||||
chan_dict = self.create_channel_storage(
|
||||
@@ -1277,6 +1308,8 @@ class Peer(Logger):
|
||||
chan_just_became_ready = (their_next_local_ctn == next_local_ctn == 1)
|
||||
if chan_just_became_ready or self.features.supports(LnFeatures.OPTION_SCID_ALIAS_OPT):
|
||||
self.send_channel_ready(chan)
|
||||
|
||||
self.maybe_send_announcement_signatures(chan)
|
||||
# checks done
|
||||
util.trigger_callback('channel', self.lnworker.wallet, chan)
|
||||
# if we have sent a previous shutdown, it must be retransmitted (Bolt2)
|
||||
@@ -1319,58 +1352,52 @@ class Peer(Logger):
|
||||
self.lnworker.save_channel(chan)
|
||||
self.maybe_mark_open(chan)
|
||||
|
||||
def on_network_update(self, chan: Channel, funding_tx_depth: int):
|
||||
"""
|
||||
Only called when the channel is OPEN.
|
||||
def send_node_announcement(self, alias:str):
|
||||
timestamp = int(time.time())
|
||||
node_id = privkey_to_pubkey(self.privkey)
|
||||
features = self.features.for_node_announcement()
|
||||
b = int.bit_length(features)
|
||||
flen = b // 8 + int(bool(b % 8))
|
||||
rgb_color = bytes.fromhex('000000')
|
||||
alias = bytes(alias, 'utf8')
|
||||
alias += bytes(32 - len(alias))
|
||||
addresses = b''
|
||||
raw_msg = encode_msg(
|
||||
"node_announcement",
|
||||
flen=flen,
|
||||
features=features,
|
||||
timestamp=timestamp,
|
||||
rgb_color=rgb_color,
|
||||
node_id=node_id,
|
||||
alias=alias,
|
||||
addrlen=len(addresses),
|
||||
addresses=addresses)
|
||||
h = sha256d(raw_msg[64+2:])
|
||||
signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s)
|
||||
message_type, payload = decode_msg(raw_msg)
|
||||
payload['signature'] = signature
|
||||
raw_msg = encode_msg(message_type, **payload)
|
||||
self.transport.send_bytes(raw_msg)
|
||||
|
||||
Runs on the Network thread.
|
||||
"""
|
||||
if not chan.config[LOCAL].was_announced and funding_tx_depth >= 6:
|
||||
# don't announce our channels
|
||||
# FIXME should this be a field in chan.local_state maybe?
|
||||
return
|
||||
chan.config[LOCAL].was_announced = True
|
||||
self.lnworker.save_channel(chan)
|
||||
coro = self.handle_announcements(chan)
|
||||
asyncio.run_coroutine_threadsafe(coro, self.asyncio_loop)
|
||||
|
||||
@log_exceptions
|
||||
async def handle_announcements(self, chan: Channel):
|
||||
h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan)
|
||||
announcement_signatures_msg = await self.announcement_signatures[chan.channel_id].get()
|
||||
remote_node_sig = announcement_signatures_msg["node_signature"]
|
||||
remote_bitcoin_sig = announcement_signatures_msg["bitcoin_signature"]
|
||||
if not ecc.verify_signature(chan.config[REMOTE].multisig_key.pubkey, remote_bitcoin_sig, h):
|
||||
raise Exception("bitcoin_sig invalid in announcement_signatures")
|
||||
if not ecc.verify_signature(self.pubkey, remote_node_sig, h):
|
||||
raise Exception("node_sig invalid in announcement_signatures")
|
||||
|
||||
node_sigs = [remote_node_sig, local_node_sig]
|
||||
bitcoin_sigs = [remote_bitcoin_sig, local_bitcoin_sig]
|
||||
def send_channel_announcement(self, chan: Channel):
|
||||
node_ids = [chan.node_id, chan.get_local_pubkey()]
|
||||
node_sigs = [chan.config[REMOTE].announcement_node_sig, chan.config[LOCAL].announcement_node_sig]
|
||||
bitcoin_sigs = [chan.config[REMOTE].announcement_bitcoin_sig, chan.config[LOCAL].announcement_bitcoin_sig]
|
||||
bitcoin_keys = [chan.config[REMOTE].multisig_key.pubkey, chan.config[LOCAL].multisig_key.pubkey]
|
||||
|
||||
if self.node_ids[0] > self.node_ids[1]:
|
||||
sorted_node_ids = list(sorted(node_ids))
|
||||
if sorted_node_ids != node_ids:
|
||||
node_sigs.reverse()
|
||||
bitcoin_sigs.reverse()
|
||||
node_ids = list(reversed(self.node_ids))
|
||||
node_ids.reverse()
|
||||
bitcoin_keys.reverse()
|
||||
else:
|
||||
node_ids = self.node_ids
|
||||
|
||||
self.send_message("channel_announcement",
|
||||
node_signatures_1=node_sigs[0],
|
||||
node_signatures_2=node_sigs[1],
|
||||
bitcoin_signature_1=bitcoin_sigs[0],
|
||||
bitcoin_signature_2=bitcoin_sigs[1],
|
||||
len=0,
|
||||
#features not set (defaults to zeros)
|
||||
chain_hash=constants.net.rev_genesis_bytes(),
|
||||
short_channel_id=chan.short_channel_id,
|
||||
node_id_1=node_ids[0],
|
||||
node_id_2=node_ids[1],
|
||||
bitcoin_key_1=bitcoin_keys[0],
|
||||
bitcoin_key_2=bitcoin_keys[1]
|
||||
)
|
||||
raw_msg = chan.construct_channel_announcement_without_sigs()
|
||||
message_type, payload = decode_msg(raw_msg)
|
||||
payload['node_signature_1'] = node_sigs[0]
|
||||
payload['node_signature_2'] = node_sigs[1]
|
||||
payload['bitcoin_signature_1'] = bitcoin_sigs[0]
|
||||
payload['bitcoin_signature_2'] = bitcoin_sigs[1]
|
||||
raw_msg = encode_msg(message_type, **payload)
|
||||
self.transport.send_bytes(raw_msg)
|
||||
|
||||
def maybe_mark_open(self, chan: Channel):
|
||||
if not chan.sent_channel_ready:
|
||||
@@ -1404,19 +1431,27 @@ class Peer(Logger):
|
||||
chan_upd = chan.get_outgoing_gossip_channel_update()
|
||||
self.transport.send_bytes(chan_upd)
|
||||
|
||||
def send_announcement_signatures(self, chan: Channel):
|
||||
chan_ann = chan.construct_channel_announcement_without_sigs()
|
||||
preimage = chan_ann[256+2:]
|
||||
msg_hash = sha256d(preimage)
|
||||
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(msg_hash, sig_string_from_r_and_s)
|
||||
node_signature = ecc.ECPrivkey(self.privkey).sign(msg_hash, sig_string_from_r_and_s)
|
||||
self.send_message("announcement_signatures",
|
||||
def maybe_send_announcement_signatures(self, chan: Channel, is_reply=False):
|
||||
if not chan.is_public():
|
||||
return
|
||||
if chan.sent_announcement_signatures:
|
||||
return
|
||||
if not is_reply and chan.config[REMOTE].announcement_node_sig:
|
||||
return
|
||||
h = chan.get_channel_announcement_hash()
|
||||
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s)
|
||||
node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s)
|
||||
self.send_message(
|
||||
"announcement_signatures",
|
||||
channel_id=chan.channel_id,
|
||||
short_channel_id=chan.short_channel_id,
|
||||
node_signature=node_signature,
|
||||
bitcoin_signature=bitcoin_signature
|
||||
)
|
||||
return msg_hash, node_signature, bitcoin_signature
|
||||
chan.config[LOCAL].announcement_node_sig = node_signature
|
||||
chan.config[LOCAL].announcement_bitcoin_sig = bitcoin_signature
|
||||
self.lnworker.save_channel(chan)
|
||||
chan.sent_announcement_signatures = True
|
||||
|
||||
def on_update_fail_htlc(self, chan: Channel, payload):
|
||||
htlc_id = payload["id"]
|
||||
@@ -1982,7 +2017,7 @@ class Peer(Logger):
|
||||
feerate = payload["feerate_per_kw"]
|
||||
chan.update_fee(feerate, False)
|
||||
|
||||
async def maybe_update_fee(self, chan: Channel):
|
||||
def maybe_update_fee(self, chan: Channel):
|
||||
"""
|
||||
called when our fee estimates change
|
||||
"""
|
||||
|
||||
+3
-1
@@ -90,6 +90,8 @@ class ChannelConfig(StoredObject):
|
||||
reserve_sat = attr.ib(type=int) # applies to OTHER ctx
|
||||
htlc_minimum_msat = attr.ib(type=int) # smallest value for INCOMING htlc
|
||||
upfront_shutdown_script = attr.ib(type=bytes, converter=hex_to_bytes)
|
||||
announcement_node_sig = attr.ib(type=bytes, converter=hex_to_bytes)
|
||||
announcement_bitcoin_sig = attr.ib(type=bytes, converter=hex_to_bytes)
|
||||
|
||||
def validate_params(self, *, funding_sat: int, config: 'SimpleConfig', peer_features: 'LnFeatures') -> None:
|
||||
conf_name = type(self).__name__
|
||||
@@ -193,7 +195,6 @@ class ChannelConfig(StoredObject):
|
||||
class LocalConfig(ChannelConfig):
|
||||
channel_seed = attr.ib(type=bytes, converter=hex_to_bytes) # type: Optional[bytes]
|
||||
funding_locked_received = attr.ib(type=bool)
|
||||
was_announced = attr.ib(type=bool)
|
||||
current_commitment_signature = attr.ib(type=bytes, converter=hex_to_bytes)
|
||||
current_htlc_signatures = attr.ib(type=bytes, converter=hex_to_bytes)
|
||||
per_commitment_secret_seed = attr.ib(type=bytes, converter=hex_to_bytes)
|
||||
@@ -242,6 +243,7 @@ class FeeUpdate(StoredObject):
|
||||
@stored_as('constraints')
|
||||
@attr.s
|
||||
class ChannelConstraints(StoredObject):
|
||||
flags = attr.ib(type=int, converter=int)
|
||||
capacity = attr.ib(type=int) # in sat
|
||||
is_initiator = attr.ib(type=bool) # note: sometimes also called "funder"
|
||||
funding_txn_minimum_depth = attr.ib(type=int)
|
||||
|
||||
+26
-9
@@ -1204,10 +1204,9 @@ class LNWallet(LNWorker):
|
||||
|
||||
elif chan.get_state() == ChannelState.OPEN:
|
||||
peer = self._peers.get(chan.node_id)
|
||||
if peer:
|
||||
await peer.maybe_update_fee(chan)
|
||||
conf = self.lnwatcher.adb.get_tx_height(chan.funding_outpoint.txid).conf
|
||||
peer.on_network_update(chan, conf)
|
||||
if peer and peer.is_initialized() and chan.peer_state == PeerState.GOOD:
|
||||
peer.maybe_update_fee(chan)
|
||||
peer.maybe_send_announcement_signatures(chan)
|
||||
|
||||
elif chan.get_state() == ChannelState.FORCE_CLOSING:
|
||||
force_close_tx = chan.force_close_tx()
|
||||
@@ -1218,7 +1217,11 @@ class LNWallet(LNWorker):
|
||||
await self.network.try_broadcasting(force_close_tx, 'force-close')
|
||||
|
||||
@log_exceptions
|
||||
async def open_channel_with_peer(self, peer, funding_sat, push_sat, password):
|
||||
async def open_channel_with_peer(
|
||||
self, peer, funding_sat, *,
|
||||
push_sat: int = 0,
|
||||
public: bool = False,
|
||||
password=None):
|
||||
coins = self.wallet.get_spendable_coins(None)
|
||||
node_id = peer.pubkey
|
||||
funding_tx = self.mktx_for_open_channel(
|
||||
@@ -1231,6 +1234,7 @@ class LNWallet(LNWorker):
|
||||
funding_tx=funding_tx,
|
||||
funding_sat=funding_sat,
|
||||
push_sat=push_sat,
|
||||
public=public,
|
||||
password=password)
|
||||
return chan, funding_tx
|
||||
|
||||
@@ -1241,12 +1245,14 @@ class LNWallet(LNWorker):
|
||||
funding_tx: PartialTransaction,
|
||||
funding_sat: int,
|
||||
push_sat: int,
|
||||
public: bool,
|
||||
password: Optional[str]) -> Tuple[Channel, PartialTransaction]:
|
||||
|
||||
coro = peer.channel_establishment_flow(
|
||||
funding_tx=funding_tx,
|
||||
funding_sat=funding_sat,
|
||||
push_msat=push_sat * 1000,
|
||||
public=public,
|
||||
temp_channel_id=os.urandom(32))
|
||||
chan, funding_tx = await util.wait_for2(coro, LN_P2P_NETWORK_TIMEOUT)
|
||||
util.trigger_callback('channels_updated', self.wallet)
|
||||
@@ -1329,8 +1335,15 @@ class LNWallet(LNWorker):
|
||||
pass
|
||||
return funding_sat, min_funding_sat
|
||||
|
||||
def open_channel(self, *, connect_str: str, funding_tx: PartialTransaction,
|
||||
funding_sat: int, push_amt_sat: int, password: str = None) -> Tuple[Channel, PartialTransaction]:
|
||||
def open_channel(
|
||||
self, *,
|
||||
connect_str: str,
|
||||
funding_tx: PartialTransaction,
|
||||
funding_sat: int,
|
||||
push_amt_sat: int,
|
||||
public: bool = False,
|
||||
password: str = None) -> Tuple[Channel, PartialTransaction]:
|
||||
|
||||
if funding_sat > self.config.LIGHTNING_MAX_FUNDING_SAT:
|
||||
raise Exception(_("Requested channel capacity is over maximum."))
|
||||
|
||||
@@ -1340,8 +1353,12 @@ class LNWallet(LNWorker):
|
||||
except concurrent.futures.TimeoutError:
|
||||
raise Exception(_("add peer timed out"))
|
||||
coro = self._open_channel_coroutine(
|
||||
peer=peer, funding_tx=funding_tx, funding_sat=funding_sat,
|
||||
push_sat=push_amt_sat, password=password)
|
||||
peer=peer,
|
||||
funding_tx=funding_tx,
|
||||
funding_sat=funding_sat,
|
||||
push_sat=push_amt_sat,
|
||||
public=public,
|
||||
password=password)
|
||||
fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
|
||||
try:
|
||||
chan, funding_tx = fut.result()
|
||||
|
||||
@@ -1024,6 +1024,7 @@ This will result in longer routes; it might increase your fees and decrease the
|
||||
)
|
||||
INITIAL_TRAMPOLINE_FEE_LEVEL = ConfigVar('initial_trampoline_fee_level', default=1, type_=int)
|
||||
|
||||
LIGHTNING_NODE_ALIAS = ConfigVar('lightning_node_alias', default='', type_=str)
|
||||
EXPERIMENTAL_LN_FORWARD_PAYMENTS = ConfigVar('lightning_forward_payments', default=False, type_=bool)
|
||||
EXPERIMENTAL_LN_FORWARD_TRAMPOLINE_PAYMENTS = ConfigVar('lightning_forward_trampoline_payments', default=False, type_=bool)
|
||||
TEST_FAIL_HTLCS_WITH_TEMP_NODE_FAILURE = ConfigVar('test_fail_htlcs_with_temp_node_failure', default=False, type_=bool)
|
||||
|
||||
@@ -72,6 +72,8 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
|
||||
next_per_commitment_point=nex,
|
||||
current_per_commitment_point=cur,
|
||||
upfront_shutdown_script=b'',
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
),
|
||||
"local_config":lnpeer.LocalConfig(
|
||||
channel_seed = None,
|
||||
@@ -88,13 +90,15 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
|
||||
reserve_sat=0,
|
||||
per_commitment_secret_seed=seed,
|
||||
funding_locked_received=True,
|
||||
was_announced=False,
|
||||
current_commitment_signature=None,
|
||||
current_htlc_signatures=None,
|
||||
htlc_minimum_msat=1,
|
||||
upfront_shutdown_script=b'',
|
||||
announcement_node_sig=b'',
|
||||
announcement_bitcoin_sig=b'',
|
||||
),
|
||||
"constraints":lnpeer.ChannelConstraints(
|
||||
flags=0,
|
||||
capacity=funding_sat,
|
||||
is_initiator=is_initiator,
|
||||
funding_txn_minimum_depth=3,
|
||||
|
||||
+14
-1
@@ -67,7 +67,7 @@ class WalletUnfinished(WalletFileException):
|
||||
# seed_version is now used for the version of the wallet file
|
||||
OLD_SEED_VERSION = 4 # electrum versions < 2.0
|
||||
NEW_SEED_VERSION = 11 # electrum versions >= 2.0
|
||||
FINAL_SEED_VERSION = 55 # electrum >= 2.7 will set this to prevent
|
||||
FINAL_SEED_VERSION = 56 # electrum >= 2.7 will set this to prevent
|
||||
# old versions from overwriting new format
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ class WalletDBUpgrader(Logger):
|
||||
self._convert_version_53()
|
||||
self._convert_version_54()
|
||||
self._convert_version_55()
|
||||
self._convert_version_56()
|
||||
self.put('seed_version', FINAL_SEED_VERSION) # just to be sure
|
||||
|
||||
def _convert_wallet_type(self):
|
||||
@@ -1082,6 +1083,18 @@ class WalletDBUpgrader(Logger):
|
||||
self.data[key[:-1]] = self.data.pop(key)
|
||||
self.data['seed_version'] = 55
|
||||
|
||||
def _convert_version_56(self):
|
||||
if not self._is_upgrade_method_needed(55, 55):
|
||||
return
|
||||
channels = self.data.get('channels', {})
|
||||
for key, item in channels.items():
|
||||
item['constraints']['flags'] = 0
|
||||
for c in ['local_config', 'remote_config']:
|
||||
item[c]['announcement_node_sig'] = ''
|
||||
item[c]['announcement_bitcoin_sig'] = ''
|
||||
item['local_config'].pop('was_announced')
|
||||
self.data['seed_version'] = 56
|
||||
|
||||
def _convert_imported(self):
|
||||
if not self._is_upgrade_method_needed(0, 13):
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user