|
@ -34,13 +34,14 @@ from .bip32 import BIP32Node |
|
|
from .util import bh2u, bfh, InvoiceError, resolve_dns_srv, is_ip_address, log_exceptions |
|
|
from .util import bh2u, bfh, InvoiceError, resolve_dns_srv, is_ip_address, log_exceptions |
|
|
from .util import ignore_exceptions, make_aiohttp_session |
|
|
from .util import ignore_exceptions, make_aiohttp_session |
|
|
from .util import timestamp_to_datetime |
|
|
from .util import timestamp_to_datetime |
|
|
|
|
|
from .util import MyEncoder |
|
|
from .logging import Logger |
|
|
from .logging import Logger |
|
|
from .lntransport import LNTransport, LNResponderTransport |
|
|
from .lntransport import LNTransport, LNResponderTransport |
|
|
from .lnpeer import Peer, LN_P2P_NETWORK_TIMEOUT |
|
|
from .lnpeer import Peer, LN_P2P_NETWORK_TIMEOUT |
|
|
from .lnaddr import lnencode, LnAddr, lndecode |
|
|
from .lnaddr import lnencode, LnAddr, lndecode |
|
|
from .ecc import der_sig_from_sig_string |
|
|
from .ecc import der_sig_from_sig_string |
|
|
from .ecc_fast import is_using_fast_ecc |
|
|
from .ecc_fast import is_using_fast_ecc |
|
|
from .lnchannel import Channel, ChannelJsonEncoder |
|
|
from .lnchannel import Channel |
|
|
from .lnchannel import channel_states, peer_states |
|
|
from .lnchannel import channel_states, peer_states |
|
|
from . import lnutil |
|
|
from . import lnutil |
|
|
from .lnutil import funding_output_script |
|
|
from .lnutil import funding_output_script |
|
@ -106,8 +107,6 @@ FALLBACK_NODE_LIST_MAINNET = [ |
|
|
LNPeerAddr(host='3.124.63.44', port=9735, pubkey=bfh('0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3')), |
|
|
LNPeerAddr(host='3.124.63.44', port=9735, pubkey=bfh('0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3')), |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
|
encoder = ChannelJsonEncoder() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from typing import NamedTuple |
|
|
from typing import NamedTuple |
|
|
|
|
|
|
|
@ -347,19 +346,20 @@ class LNWallet(LNWorker): |
|
|
LNWorker.__init__(self, xprv) |
|
|
LNWorker.__init__(self, xprv) |
|
|
self.ln_keystore = keystore.from_xprv(xprv) |
|
|
self.ln_keystore = keystore.from_xprv(xprv) |
|
|
self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_REQ |
|
|
self.localfeatures |= LnLocalFeatures.OPTION_DATA_LOSS_PROTECT_REQ |
|
|
self.payments = self.storage.get('lightning_payments', {}) # RHASH -> amount, direction, is_paid |
|
|
self.payments = self.storage.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid |
|
|
self.preimages = self.storage.get('lightning_preimages', {}) # RHASH -> preimage |
|
|
self.preimages = self.storage.db.get_dict('lightning_preimages') # RHASH -> preimage |
|
|
self.sweep_address = wallet.get_receiving_address() |
|
|
self.sweep_address = wallet.get_receiving_address() |
|
|
self.lock = threading.RLock() |
|
|
self.lock = threading.RLock() |
|
|
self.logs = defaultdict(list) # type: Dict[str, List[PaymentAttemptLog]] # key is RHASH |
|
|
self.logs = defaultdict(list) # type: Dict[str, List[PaymentAttemptLog]] # key is RHASH |
|
|
|
|
|
|
|
|
# note: accessing channels (besides simple lookup) needs self.lock! |
|
|
# note: accessing channels (besides simple lookup) needs self.lock! |
|
|
self.channels = {} # type: Dict[bytes, Channel] |
|
|
self.channels = {} |
|
|
for x in wallet.storage.get("channels", {}).values(): |
|
|
channels = self.storage.db.get_dict("channels") |
|
|
c = Channel(x, sweep_address=self.sweep_address, lnworker=self) |
|
|
for channel_id, c in channels.items(): |
|
|
self.channels[c.channel_id] = c |
|
|
self.channels[bfh(channel_id)] = Channel(c, sweep_address=self.sweep_address, lnworker=self) |
|
|
|
|
|
|
|
|
# timestamps of opening and closing transactions |
|
|
# timestamps of opening and closing transactions |
|
|
self.channel_timestamps = self.storage.get('lightning_channel_timestamps', {}) |
|
|
self.channel_timestamps = self.storage.db.get_dict('lightning_channel_timestamps') |
|
|
self.pending_payments = defaultdict(asyncio.Future) |
|
|
self.pending_payments = defaultdict(asyncio.Future) |
|
|
|
|
|
|
|
|
@ignore_exceptions |
|
|
@ignore_exceptions |
|
@ -610,17 +610,9 @@ class LNWallet(LNWorker): |
|
|
assert type(chan) is Channel |
|
|
assert type(chan) is Channel |
|
|
if chan.config[REMOTE].next_per_commitment_point == chan.config[REMOTE].current_per_commitment_point: |
|
|
if chan.config[REMOTE].next_per_commitment_point == chan.config[REMOTE].current_per_commitment_point: |
|
|
raise Exception("Tried to save channel with next_point == current_point, this should not happen") |
|
|
raise Exception("Tried to save channel with next_point == current_point, this should not happen") |
|
|
with self.lock: |
|
|
self.wallet.storage.write() |
|
|
self.channels[chan.channel_id] = chan |
|
|
|
|
|
self.save_channels() |
|
|
|
|
|
self.network.trigger_callback('channel', chan) |
|
|
self.network.trigger_callback('channel', chan) |
|
|
|
|
|
|
|
|
def save_channels(self): |
|
|
|
|
|
with self.lock: |
|
|
|
|
|
dumped = dict( (k.hex(), c.serialize()) for k, c in self.channels.items() ) |
|
|
|
|
|
self.storage.put("channels", dumped) |
|
|
|
|
|
self.storage.write() |
|
|
|
|
|
|
|
|
|
|
|
def save_short_chan_id(self, chan): |
|
|
def save_short_chan_id(self, chan): |
|
|
""" |
|
|
""" |
|
|
Checks if Funding TX has been mined. If it has, save the short channel ID in chan; |
|
|
Checks if Funding TX has been mined. If it has, save the short channel ID in chan; |
|
@ -648,8 +640,8 @@ class LNWallet(LNWorker): |
|
|
return |
|
|
return |
|
|
block_height, tx_pos = self.lnwatcher.get_txpos(chan.funding_outpoint.txid) |
|
|
block_height, tx_pos = self.lnwatcher.get_txpos(chan.funding_outpoint.txid) |
|
|
assert tx_pos >= 0 |
|
|
assert tx_pos >= 0 |
|
|
chan.short_channel_id = ShortChannelID.from_components( |
|
|
chan.set_short_channel_id(ShortChannelID.from_components( |
|
|
block_height, tx_pos, chan.funding_outpoint.output_index) |
|
|
block_height, tx_pos, chan.funding_outpoint.output_index)) |
|
|
self.logger.info(f"save_short_channel_id: {chan.short_channel_id}") |
|
|
self.logger.info(f"save_short_channel_id: {chan.short_channel_id}") |
|
|
self.save_channel(chan) |
|
|
self.save_channel(chan) |
|
|
|
|
|
|
|
@ -669,7 +661,6 @@ class LNWallet(LNWorker): |
|
|
|
|
|
|
|
|
# save timestamp regardless of state, so that funding tx is returned in get_history |
|
|
# save timestamp regardless of state, so that funding tx is returned in get_history |
|
|
self.channel_timestamps[bh2u(chan.channel_id)] = chan.funding_outpoint.txid, funding_height.height, funding_height.timestamp, None, None, None |
|
|
self.channel_timestamps[bh2u(chan.channel_id)] = chan.funding_outpoint.txid, funding_height.height, funding_height.timestamp, None, None, None |
|
|
self.storage.put('lightning_channel_timestamps', self.channel_timestamps) |
|
|
|
|
|
|
|
|
|
|
|
if chan.get_state() == channel_states.OPEN and self.should_channel_be_closed_due_to_expiring_htlcs(chan): |
|
|
if chan.get_state() == channel_states.OPEN and self.should_channel_be_closed_due_to_expiring_htlcs(chan): |
|
|
self.logger.info(f"force-closing due to expiring htlcs") |
|
|
self.logger.info(f"force-closing due to expiring htlcs") |
|
@ -714,7 +705,6 @@ class LNWallet(LNWorker): |
|
|
|
|
|
|
|
|
# fixme: this is wasteful |
|
|
# fixme: this is wasteful |
|
|
self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, closing_txid, closing_height.height, closing_height.timestamp |
|
|
self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, closing_txid, closing_height.height, closing_height.timestamp |
|
|
self.storage.put('lightning_channel_timestamps', self.channel_timestamps) |
|
|
|
|
|
|
|
|
|
|
|
# remove from channel_db |
|
|
# remove from channel_db |
|
|
if chan.short_channel_id is not None: |
|
|
if chan.short_channel_id is not None: |
|
@ -836,7 +826,7 @@ class LNWallet(LNWorker): |
|
|
funding_sat=funding_sat, |
|
|
funding_sat=funding_sat, |
|
|
push_msat=push_sat * 1000, |
|
|
push_msat=push_sat * 1000, |
|
|
temp_channel_id=os.urandom(32)) |
|
|
temp_channel_id=os.urandom(32)) |
|
|
self.save_channel(chan) |
|
|
self.add_channel(chan) |
|
|
self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address()) |
|
|
self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address()) |
|
|
self.network.trigger_callback('channels_updated', self.wallet) |
|
|
self.network.trigger_callback('channels_updated', self.wallet) |
|
|
self.wallet.add_transaction(funding_tx) # save tx as local into the wallet |
|
|
self.wallet.add_transaction(funding_tx) # save tx as local into the wallet |
|
@ -846,6 +836,10 @@ class LNWallet(LNWorker): |
|
|
await asyncio.wait_for(self.network.broadcast_transaction(funding_tx), LN_P2P_NETWORK_TIMEOUT) |
|
|
await asyncio.wait_for(self.network.broadcast_transaction(funding_tx), LN_P2P_NETWORK_TIMEOUT) |
|
|
return chan, funding_tx |
|
|
return chan, funding_tx |
|
|
|
|
|
|
|
|
|
|
|
def add_channel(self, chan): |
|
|
|
|
|
with self.lock: |
|
|
|
|
|
self.channels[chan.channel_id] = chan |
|
|
|
|
|
|
|
|
@log_exceptions |
|
|
@log_exceptions |
|
|
async def add_peer(self, connect_str: str) -> Peer: |
|
|
async def add_peer(self, connect_str: str) -> Peer: |
|
|
node_id, rest = extract_nodeid(connect_str) |
|
|
node_id, rest = extract_nodeid(connect_str) |
|
@ -1133,7 +1127,6 @@ class LNWallet(LNWorker): |
|
|
def save_preimage(self, payment_hash: bytes, preimage: bytes): |
|
|
def save_preimage(self, payment_hash: bytes, preimage: bytes): |
|
|
assert sha256(preimage) == payment_hash |
|
|
assert sha256(preimage) == payment_hash |
|
|
self.preimages[bh2u(payment_hash)] = bh2u(preimage) |
|
|
self.preimages[bh2u(payment_hash)] = bh2u(preimage) |
|
|
self.storage.put('lightning_preimages', self.preimages) |
|
|
|
|
|
self.storage.write() |
|
|
self.storage.write() |
|
|
|
|
|
|
|
|
def get_preimage(self, payment_hash: bytes) -> bytes: |
|
|
def get_preimage(self, payment_hash: bytes) -> bytes: |
|
@ -1152,7 +1145,6 @@ class LNWallet(LNWorker): |
|
|
assert info.status in [PR_PAID, PR_UNPAID, PR_INFLIGHT] |
|
|
assert info.status in [PR_PAID, PR_UNPAID, PR_INFLIGHT] |
|
|
with self.lock: |
|
|
with self.lock: |
|
|
self.payments[key] = info.amount, info.direction, info.status |
|
|
self.payments[key] = info.amount, info.direction, info.status |
|
|
self.storage.put('lightning_payments', self.payments) |
|
|
|
|
|
self.storage.write() |
|
|
self.storage.write() |
|
|
|
|
|
|
|
|
def get_payment_status(self, payment_hash): |
|
|
def get_payment_status(self, payment_hash): |
|
@ -1238,7 +1230,6 @@ class LNWallet(LNWorker): |
|
|
del self.payments[payment_hash_hex] |
|
|
del self.payments[payment_hash_hex] |
|
|
except KeyError: |
|
|
except KeyError: |
|
|
return |
|
|
return |
|
|
self.storage.put('lightning_payments', self.payments) |
|
|
|
|
|
self.storage.write() |
|
|
self.storage.write() |
|
|
|
|
|
|
|
|
def get_balance(self): |
|
|
def get_balance(self): |
|
@ -1246,6 +1237,7 @@ class LNWallet(LNWorker): |
|
|
return Decimal(sum(chan.balance(LOCAL) if not chan.is_closed() else 0 for chan in self.channels.values()))/1000 |
|
|
return Decimal(sum(chan.balance(LOCAL) if not chan.is_closed() else 0 for chan in self.channels.values()))/1000 |
|
|
|
|
|
|
|
|
def list_channels(self): |
|
|
def list_channels(self): |
|
|
|
|
|
encoder = MyEncoder() |
|
|
with self.lock: |
|
|
with self.lock: |
|
|
# we output the funding_outpoint instead of the channel_id because lnd uses channel_point (funding outpoint) to identify channels |
|
|
# we output the funding_outpoint instead of the channel_id because lnd uses channel_point (funding outpoint) to identify channels |
|
|
for channel_id, chan in self.channels.items(): |
|
|
for channel_id, chan in self.channels.items(): |
|
@ -1283,7 +1275,9 @@ class LNWallet(LNWorker): |
|
|
assert chan.is_closed() |
|
|
assert chan.is_closed() |
|
|
with self.lock: |
|
|
with self.lock: |
|
|
self.channels.pop(chan_id) |
|
|
self.channels.pop(chan_id) |
|
|
self.save_channels() |
|
|
self.channel_timestamps.pop(chan_id.hex()) |
|
|
|
|
|
self.storage.get('channels').pop(chan_id.hex()) |
|
|
|
|
|
|
|
|
self.network.trigger_callback('channels_updated', self.wallet) |
|
|
self.network.trigger_callback('channels_updated', self.wallet) |
|
|
self.network.trigger_callback('wallet_updated', self.wallet) |
|
|
self.network.trigger_callback('wallet_updated', self.wallet) |
|
|
|
|
|
|
|
|