Replace wallet backup with channel backups
- channels can be backed up individually - backups are added to lnwatcher - AbstractChannel ancestor class
This commit is contained in:
@@ -64,6 +64,9 @@ from .lnrouter import RouteEdge, LNPaymentRoute, is_route_sane_to_use
|
||||
from .address_synchronizer import TX_HEIGHT_LOCAL
|
||||
from . import lnsweep
|
||||
from .lnwatcher import LNWalletWatcher
|
||||
from .crypto import pw_encode_bytes, pw_decode_bytes, PW_HASH_VERSION_LATEST
|
||||
from .lnutil import ChannelBackupStorage
|
||||
from .lnchannel import ChannelBackup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .network import Network
|
||||
@@ -219,7 +222,8 @@ class LNWorker(Logger):
|
||||
return peer
|
||||
|
||||
def peer_closed(self, peer: Peer) -> None:
|
||||
self.peers.pop(peer.pubkey)
|
||||
if peer.pubkey in self.peers:
|
||||
self.peers.pop(peer.pubkey)
|
||||
|
||||
def num_peers(self) -> int:
|
||||
return sum([p.is_initialized() for p in self.peers.values()])
|
||||
@@ -492,7 +496,8 @@ class LNWallet(LNWorker):
|
||||
self.lnwatcher = LNWalletWatcher(self, network)
|
||||
self.lnwatcher.start_network(network)
|
||||
self.network = network
|
||||
for chan_id, chan in self.channels.items():
|
||||
|
||||
for chan in self.channels.values():
|
||||
self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address())
|
||||
|
||||
super().start_network(network)
|
||||
@@ -763,8 +768,6 @@ class LNWallet(LNWorker):
|
||||
def open_channel(self, *, connect_str: str, funding_tx: PartialTransaction,
|
||||
funding_sat: int, push_amt_sat: int, password: str = None,
|
||||
timeout: Optional[int] = 20) -> Tuple[Channel, PartialTransaction]:
|
||||
if self.wallet.is_lightning_backup():
|
||||
raise Exception(_('Cannot create channel: this is a backup file'))
|
||||
if funding_sat > LN_MAX_FUNDING_SAT:
|
||||
raise Exception(_("Requested channel capacity is over protocol allowed maximum."))
|
||||
coro = self._open_channel_coroutine(connect_str=connect_str, funding_tx=funding_tx, funding_sat=funding_sat,
|
||||
@@ -1319,3 +1322,100 @@ class LNWallet(LNWorker):
|
||||
if feerate_per_kvbyte is None:
|
||||
feerate_per_kvbyte = FEERATE_FALLBACK_STATIC_FEE
|
||||
return max(253, feerate_per_kvbyte // 4)
|
||||
|
||||
def create_channel_backup(self, channel_id):
|
||||
chan = self.channels[channel_id]
|
||||
peer_addresses = list(chan.get_peer_addresses())
|
||||
peer_addr = peer_addresses[0]
|
||||
return ChannelBackupStorage(
|
||||
node_id = chan.node_id,
|
||||
privkey = self.node_keypair.privkey,
|
||||
funding_txid = chan.funding_outpoint.txid,
|
||||
funding_index = chan.funding_outpoint.output_index,
|
||||
funding_address = chan.get_funding_address(),
|
||||
host = peer_addr.host,
|
||||
port = peer_addr.port,
|
||||
is_initiator = chan.constraints.is_initiator,
|
||||
channel_seed = chan.config[LOCAL].channel_seed,
|
||||
local_delay = chan.config[LOCAL].to_self_delay,
|
||||
remote_delay = chan.config[REMOTE].to_self_delay,
|
||||
remote_revocation_pubkey = chan.config[REMOTE].revocation_basepoint.pubkey,
|
||||
remote_payment_pubkey = chan.config[REMOTE].payment_basepoint.pubkey)
|
||||
|
||||
def export_channel_backup(self, channel_id):
|
||||
xpub = self.wallet.get_fingerprint()
|
||||
backup_bytes = self.create_channel_backup(channel_id).to_bytes()
|
||||
assert backup_bytes == ChannelBackupStorage.from_bytes(backup_bytes).to_bytes(), "roundtrip failed"
|
||||
encrypted = pw_encode_bytes(backup_bytes, xpub, version=PW_HASH_VERSION_LATEST)
|
||||
assert backup_bytes == pw_decode_bytes(encrypted, xpub, version=PW_HASH_VERSION_LATEST), "encrypt failed"
|
||||
return encrypted
|
||||
|
||||
|
||||
class LNBackups(Logger):
|
||||
|
||||
def __init__(self, wallet: 'Abstract_Wallet'):
|
||||
Logger.__init__(self)
|
||||
self.features = LnFeatures(0)
|
||||
self.features |= LnFeatures.OPTION_DATA_LOSS_PROTECT_OPT
|
||||
self.features |= LnFeatures.OPTION_STATIC_REMOTEKEY_OPT
|
||||
self.taskgroup = SilentTaskGroup()
|
||||
self.lock = threading.RLock()
|
||||
self.wallet = wallet
|
||||
self.db = wallet.db
|
||||
self.sweep_address = wallet.get_receiving_address()
|
||||
self.channel_backups = {}
|
||||
for channel_id, cb in self.db.get_dict("channel_backups").items():
|
||||
self.channel_backups[bfh(channel_id)] = ChannelBackup(cb, sweep_address=self.sweep_address, lnworker=self)
|
||||
|
||||
def peer_closed(self, chan):
|
||||
pass
|
||||
|
||||
async def on_channel_update(self, chan):
|
||||
pass
|
||||
|
||||
def channel_by_txo(self, txo):
|
||||
with self.lock:
|
||||
channel_backups = list(self.channel_backups.values())
|
||||
for chan in channel_backups:
|
||||
if chan.funding_outpoint.to_str() == txo:
|
||||
return chan
|
||||
|
||||
def start_network(self, network: 'Network'):
|
||||
assert network
|
||||
self.lnwatcher = LNWalletWatcher(self, network)
|
||||
self.lnwatcher.start_network(network)
|
||||
self.network = network
|
||||
for cb in self.channel_backups.values():
|
||||
self.lnwatcher.add_channel(cb.funding_outpoint.to_str(), cb.get_funding_address())
|
||||
|
||||
def import_channel_backup(self, encrypted):
|
||||
xpub = self.wallet.get_fingerprint()
|
||||
x = pw_decode_bytes(encrypted, xpub, version=PW_HASH_VERSION_LATEST)
|
||||
cb = ChannelBackupStorage.from_bytes(x)
|
||||
channel_id = cb.channel_id().hex()
|
||||
d = self.db.get_dict("channel_backups")
|
||||
if channel_id in d:
|
||||
raise Exception('Channel already in wallet')
|
||||
d[channel_id] = cb
|
||||
self.channel_backups[bfh(channel_id)] = ChannelBackup(cb, sweep_address=self.sweep_address, lnworker=self)
|
||||
self.wallet.save_db()
|
||||
self.network.trigger_callback('channels_updated', self.wallet)
|
||||
|
||||
def remove_channel_backup(self, channel_id):
|
||||
d = self.db.get_dict("channel_backups")
|
||||
if channel_id.hex() not in d:
|
||||
raise Exception('Channel not found')
|
||||
d.pop(channel_id.hex())
|
||||
self.channel_backups.pop(channel_id)
|
||||
self.wallet.save_db()
|
||||
self.network.trigger_callback('channels_updated', self.wallet)
|
||||
|
||||
@log_exceptions
|
||||
async def request_force_close(self, channel_id):
|
||||
cb = self.channel_backups[channel_id].cb
|
||||
peer_addr = LNPeerAddr(cb.host, cb.port, cb.node_id)
|
||||
transport = LNTransport(cb.privkey, peer_addr)
|
||||
peer = Peer(self, cb.node_id, transport)
|
||||
await self.taskgroup.spawn(peer._message_loop())
|
||||
await peer.initialized
|
||||
await self.taskgroup.spawn(peer.trigger_force_close(channel_id))
|
||||
|
||||
Reference in New Issue
Block a user