1
0

ln: restore functionality

This commit is contained in:
Janus
2018-05-28 18:22:45 +02:00
committed by ThomasV
parent 4268be9093
commit 6a8e5d5954
4 changed files with 104 additions and 51 deletions

View File

@@ -10,6 +10,7 @@ import binascii
import asyncio
from . import constants
from .bitcoin import sha256, COIN
from .util import bh2u, bfh
from .constants import set_testnet, set_simnet
@@ -17,7 +18,7 @@ from .simple_config import SimpleConfig
from .network import Network
from .storage import WalletStorage
from .wallet import Wallet
from .lnbase import Peer, Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, OpenChannel, ChannelConstraints, RevocationStore, aiosafe
from .lnbase import Peer, Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, OpenChannel, ChannelConstraints, RevocationStore, aiosafe, calc_short_channel_id, privkey_to_pubkey
from .lightning_payencode.lnaddr import lnencode, LnAddr, lndecode
from . import lnrouter
@@ -98,92 +99,119 @@ class LNWorker:
self.channel_db = lnrouter.ChannelDB()
self.path_finder = lnrouter.LNPathFinder(self.channel_db)
self.channels = wallet.storage.get("channels", {})
self.channels = [reconstruct_namedtuples(x) for x in wallet.storage.get("channels", {})]
peer_list = network.config.get('lightning_peers', node_list)
self.channel_state = {}
for host, port, pubkey in peer_list:
self.add_peer(host, port, pubkey)
self.add_peer(host, int(port), pubkey)
# wait until we see confirmations
self.network.register_callback(self.on_network_update, ['updated', 'verified']) # thread safe
self.on_network_update('updated') # shortcut (don't block) if funding tx locked and verified
def add_peer(self, host, port, pubkey):
peer = Peer(host, int(port), binascii.unhexlify(pubkey), self.privkey,
self.network, self.channel_db, self.path_finder)
self.network, self.channel_db, self.path_finder, self.channel_state, self.handle_channel_reestablish)
self.network.futures.append(asyncio.run_coroutine_threadsafe(peer.main_loop(), asyncio.get_event_loop()))
self.peers[pubkey] = peer
self.peers[bfh(pubkey)] = peer
async def handle_channel_reestablish(self, chan_id, payload):
chans = [x for x in self.channels if x.channel_id == chan_id ]
chan = chans[0]
await self.peers[chan.node_id].reestablish_channel(chan)
def save_channel(self, openchannel):
dumped = serialize_channels([openchannel])
self.channels = [openchannel] # TODO multiple channels
dumped = serialize_channels(self.channels)
self.wallet.storage.put("channels", dumped)
self.wallet.storage.write()
def on_network_update(self, event, *args):
for chan in self.channels:
peer = self.peers[chan.node_id]
conf = wallet.get_tx_height(chan.funding_outpoint.txid)[1]
conf = self.wallet.get_tx_height(chan.funding_outpoint.txid)[1]
if conf >= chan.constraints.funding_txn_minimum_depth:
block_height, tx_pos = wallet.get_txpos(chan.funding_outpoint.txid)
block_height, tx_pos = self.wallet.get_txpos(chan.funding_outpoint.txid)
if tx_pos == -1:
self.print_error('funding tx is not yet SPV verified.. but there are '
'already enough confirmations (currently {})'.format(conf))
return
asyncio.run_coroutine_threadsafe(self.set_local_funding_locked_result(peer, chan, block_height, txpos), asyncio.get_event_loop())
if chan.channel_id not in self.channel_state or self.channel_state[chan.channel_id] != "OPENING":
return
asyncio.run_coroutine_threadsafe(self.set_local_funding_locked_result(peer, chan, block_height, tx_pos), asyncio.get_event_loop())
async def set_local_funding_locked_result(self, peer, chan, block_height, txpos):
# aiosafe because we don't wait for result
@aiosafe
async def set_local_funding_locked_result(self, peer, chan, block_height, tx_pos):
channel_id = chan.channel_id
short_channel_id = calc_short_channel_id(block_height, tx_pos, chan.funding_outpoint.output_index)
try:
peer.local_funding_locked[channel_id].set_result(short_channel_id)
except (asyncio.InvalidStateError, KeyError) as e:
# FIXME race condition if updates come in quickly, set_result might be called multiple times
# or self.local_funding_locked[channel_id] might be deleted already
self.print_error('local_funding_locked.set_result error for channel {}: {}'.format(channel_id, e))
short_channel_id = calc_short_channel_id(block_height, tx_pos, chan.funding_outpoint.output_index)
openchannel = await peer.on_funding_locked(openingchannel, self.wallet)
openchannel = await peer.funding_locked(chan)
self.save_channel(openchannel)
print("CHANNEL OPENING COMPLETED")
@aiosafe
# not aiosafe because we call .result() which will propagate an exception
async def _open_channel_coroutine(self, node_id, amount, push_msat, password):
peer = self.peers[node_id]
peer = self.peers[bfh(node_id)]
openingchannel = await peer.channel_establishment_flow(self.wallet, self.config, password, amount, push_msat, temp_channel_id=os.urandom(32))
self.save_channel(openingchannel)
def open_channel(self, node_id, local_amt, push_amt, emit_function, pw):
def open_channel(self, node_id, local_amt, push_amt, pw):
coro = self._open_channel_coroutine(node_id, local_amt, push_amt, None if pw == "" else pw)
asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop).result()
#chan = fut.result()
# https://api.lightning.community/#listchannels
#std_chan = {"chan_id": chan.channel_id}
#emit_function({"channels": [std_chan]})
def list_channels(self):
return self.channels
def get_paid(self):
coro = self._get_paid_coroutine()
return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop).result()
@aiosafe
async def reestablish_channel(self):
def pay(self, invoice):
coro = self._pay_coroutine(invoice)
return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop).result()
def list_channels(self):
return serialize_channels(self.channels)
def reestablish_channels(self):
coro = self._reestablish_channels_coroutine()
return asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop).result()
# not aiosafe because we call .result() which will propagate an exception
async def _reestablish_channels_coroutine(self):
if self.channels is None or len(self.channels) < 1:
raise Exception("Can't reestablish: No channel saved")
openchannel = self.channels[0]
openchannel = reconstruct_namedtuples(openchannel)
openchannel = await peer.reestablish_channel(openchannel)
self.save_channel(openchannel)
peer = self.peers[self.channels[0].node_id]
await peer.reestablish_channel(self.channels[0])
@aiosafe
async def pay(self):
addr = lndecode(sys.argv[6], expected_hrp="sb" if sys.argv[2] == "simnet" else "tb")
# not aiosafe because we call .result() which will propagate an exception
async def _pay_coroutine(self, invoice):
openchannel = self.channels[0]
addr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
payment_hash = addr.paymenthash
pubkey = addr.pubkey.serialize()
msat_amt = int(addr.amount * COIN * 1000)
openchannel = await peer.pay(wallet, openchannel, msat_amt, payment_hash, pubkey, addr.min_final_cltv_expiry)
peer = self.peers[openchannel.node_id]
openchannel = await peer.pay(self.wallet, openchannel, msat_amt, payment_hash, pubkey, addr.min_final_cltv_expiry)
self.save_channel(openchannel)
@aiosafe
async def get_paid(self):
# not aiosafe because we call .result() which will propagate an exception
async def _get_paid_coroutine(self):
openchannel = self.channels[0]
payment_preimage = os.urandom(32)
RHASH = sha256(payment_preimage)
expected_received_sat = 200000
expected_received_msat = expected_received_sat * 1000
pay_req = lnencode(LnAddr(RHASH, amount=1/Decimal(COIN)*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32])
peer = self.peers[openchannel.node_id]
pay_req = lnencode(LnAddr(RHASH, amount=1/Decimal(COIN)*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey)
decoded = lndecode(pay_req, expected_hrp="sb")
assert decoded.pubkey.serialize() == privkey_to_pubkey(self.privkey)
print("payment request", pay_req)
openchannel = await peer.receive_commitment_revoke_ack(openchannel, expected_received_msat, payment_preimage)
self.save_channel(openchannel)