Browse Source

lnbase: save channel details in wallet, enable running online test with reestablishment_mode

regtest_lnd
Janus 7 years ago
committed by SomberNight
parent
commit
ef477d8a0e
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 54
      lib/lnbase.py
  2. 78
      lib/tests/test_lnbase_online.py

54
lib/lnbase.py

@ -558,6 +558,7 @@ class Peer(PrintError):
"local_funding_locked", "local_funding_locked",
"remote_funding_locked", "remote_funding_locked",
"revoke_and_ack", "revoke_and_ack",
"channel_reestablish",
"commitment_signed"] "commitment_signed"]
self.channel_accepted = defaultdict(asyncio.Future) self.channel_accepted = defaultdict(asyncio.Future)
self.funding_signed = defaultdict(asyncio.Future) self.funding_signed = defaultdict(asyncio.Future)
@ -565,6 +566,7 @@ class Peer(PrintError):
self.remote_funding_locked = defaultdict(asyncio.Future) self.remote_funding_locked = defaultdict(asyncio.Future)
self.revoke_and_ack = defaultdict(asyncio.Future) self.revoke_and_ack = defaultdict(asyncio.Future)
self.commitment_signed = defaultdict(asyncio.Future) self.commitment_signed = defaultdict(asyncio.Future)
self.channel_reestablish = defaultdict(asyncio.Future)
self.initialized = asyncio.Future() self.initialized = asyncio.Future()
self.localfeatures = (0x08 if request_initial_sync else 0) self.localfeatures = (0x08 if request_initial_sync else 0)
# view of the network # view of the network
@ -685,6 +687,9 @@ class Peer(PrintError):
l = int.from_bytes(payload['num_pong_bytes'], 'big') l = int.from_bytes(payload['num_pong_bytes'], 'big')
self.send_message(gen_msg('pong', byteslen=l)) self.send_message(gen_msg('pong', byteslen=l))
def on_channel_reestablish(self, payload):
self.channel_reestablish[payload["channel_id"]].set_result(payload)
def on_accept_channel(self, payload): def on_accept_channel(self, payload):
self.channel_accepted[payload["temporary_channel_id"]].set_result(payload) self.channel_accepted[payload["temporary_channel_id"]].set_result(payload)
@ -826,7 +831,7 @@ class Peer(PrintError):
to_self_delay=int.from_bytes(payload['to_self_delay'], byteorder='big'), to_self_delay=int.from_bytes(payload['to_self_delay'], byteorder='big'),
dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], byteorder='big'), dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], byteorder='big'),
max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'), max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'),
max_accepted_htlcs=payload["max_accepted_htlcs"] max_accepted_htlcs=int.from_bytes(payload["max_accepted_htlcs"], 'big')
) )
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big') funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
print('remote dust limit', remote_config.dust_limit_sat) print('remote dust limit', remote_config.dust_limit_sat)
@ -912,27 +917,36 @@ class Peer(PrintError):
) )
return chan return chan
async def reestablish_channel(self, chan, m, n):
await self.initialized
self.send_message(gen_msg("channel_reestablish", channel_id=chan.channel_id, next_local_commitment_number=m, next_remote_revocation_number=n))
channel_reestablish_msg = await self.channel_reestablish[chan.channel_id]
print(channel_reestablish_msg)
async def wait_for_funding_locked(self, chan, wallet): async def wait_for_funding_locked(self, chan, wallet):
channel_id = chan.channel_id channel_id = chan.channel_id
# wait until we see confirmations conf = wallet.get_tx_height(chan.funding_outpoint.txid)[1]
def on_network_update(event, *args): if conf < chan.constraints.funding_txn_minimum_depth:
conf = wallet.get_tx_height(chan.funding_outpoint.txid)[1] # wait until we see confirmations
if conf >= chan.constraints.funding_txn_minimum_depth: def on_network_update(event, *args):
async def set_local_funding_locked_result(): conf = wallet.get_tx_height(chan.funding_outpoint.txid)[1]
try: if conf >= chan.constraints.funding_txn_minimum_depth:
self.local_funding_locked[channel_id].set_result(1) async def set_local_funding_locked_result():
except (asyncio.InvalidStateError, KeyError) as e: try:
# FIXME race condition if updates come in quickly, set_result might be called multiple times self.local_funding_locked[channel_id].set_result(1)
# or self.local_funding_locked[channel_id] might be deleted already except (asyncio.InvalidStateError, KeyError) as e:
self.print_error('local_funding_locked.set_result error for channel {}: {}'.format(channel_id, e)) # FIXME race condition if updates come in quickly, set_result might be called multiple times
asyncio.run_coroutine_threadsafe(set_local_funding_locked_result(), asyncio.get_event_loop()) # or self.local_funding_locked[channel_id] might be deleted already
self.network.unregister_callback(on_network_update) self.print_error('local_funding_locked.set_result error for channel {}: {}'.format(channel_id, e))
self.network.register_callback(on_network_update, ['updated']) # thread safe asyncio.run_coroutine_threadsafe(set_local_funding_locked_result(), asyncio.get_event_loop())
self.network.unregister_callback(on_network_update)
self.network.register_callback(on_network_update, ['updated']) # thread safe
try:
await self.local_funding_locked[channel_id]
finally:
del self.local_funding_locked[channel_id]
try:
await self.local_funding_locked[channel_id]
finally:
del self.local_funding_locked[channel_id]
per_commitment_secret_index = 2**48 - (chan.local_state.ctn + 1) - 1 per_commitment_secret_index = 2**48 - (chan.local_state.ctn + 1) - 1
per_commitment_point_second = secret_to_pubkey(int.from_bytes( per_commitment_point_second = secret_to_pubkey(int.from_bytes(
get_per_commitment_secret_from_seed(chan.local_state.per_commitment_secret_seed, per_commitment_secret_index), 'big')) get_per_commitment_secret_from_seed(chan.local_state.per_commitment_secret_seed, per_commitment_secret_index), 'big'))
@ -1094,7 +1108,7 @@ class Peer(PrintError):
class LNWorker: class LNWorker:
def __init__(self, wallet, network): def __init__(self, wallet, network):
self.privkey = H256(str(time.time()).encode("ascii")) self.privkey = H256(b"0123456789")
self.wallet = wallet self.wallet = wallet
self.network = network self.network = network
self.config = network.config self.config = network.config

78
lib/tests/test_lnbase_online.py

@ -13,20 +13,71 @@ from lib.simple_config import SimpleConfig
from lib.network import Network from lib.network import Network
from lib.storage import WalletStorage from lib.storage import WalletStorage
from lib.wallet import Wallet from lib.wallet import Wallet
from lib.lnbase import Peer, node_list from lib.lnbase import Peer, node_list, Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, OpenChannel, ChannelConstraints
from lib.lightning_payencode.lnaddr import lnencode, LnAddr from lib.lightning_payencode.lnaddr import lnencode, LnAddr
import lib.constants as constants import lib.constants as constants
is_key = lambda k: k.endswith("_basepoint") or k.endswith("_key")
def maybeDecode(k, v):
if k in ["pubkey", "privkey", "next_per_commitment_point", "per_commitment_secret_seed"] and v is not None:
return binascii.unhexlify(v)
return v
def decodeAll(v):
return {i: maybeDecode(i, j) for i, j in v.items()} if isinstance(v, dict) else v
def typeWrap(k, v, local):
if is_key(k):
if local:
return Keypair(**v)
else:
return OnlyPubkeyKeypair(**v)
return v
def reconstruct_namedtuples(openingchannel):
openingchannel=OpenChannel(**openingchannel)
openingchannel = openingchannel._replace(funding_outpoint=Outpoint(**openingchannel.funding_outpoint))
new_local_config = {k: typeWrap(k, decodeAll(v), True) for k, v in openingchannel.local_config.items()}
openingchannel = openingchannel._replace(local_config=ChannelConfig(**new_local_config))
new_remote_config = {k: typeWrap(k, decodeAll(v), False) for k, v in openingchannel.remote_config.items()}
openingchannel = openingchannel._replace(remote_config=ChannelConfig(**new_remote_config))
new_local_state = decodeAll(openingchannel.local_state)
openingchannel = openingchannel._replace(local_state=LocalState(**new_local_state))
new_remote_state = decodeAll(openingchannel.remote_state)
openingchannel = openingchannel._replace(remote_state=RemoteState(**new_remote_state))
openingchannel = openingchannel._replace(constraints=ChannelConstraints(**openingchannel.constraints))
return openingchannel
def serialize_channels(channels):
serialized_channels = []
for chan in channels:
namedtuples_to_dict = lambda v: {i: j._asdict() if isinstance(j, tuple) else j for i, j in v._asdict().items()}
serialized_channels.append({k: namedtuples_to_dict(v) if isinstance(v, tuple) else v for k, v in chan._asdict().items()})
class MyJsonEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, bytes):
return binascii.hexlify(o).decode("ascii")
return super(MyJsonEncoder, self)
dumped = MyJsonEncoder().encode(serialized_channels)
roundtripped = json.loads(dumped)
reconstructed = [reconstruct_namedtuples(x) for x in roundtripped]
if reconstructed != channels:
raise Exception("Channels did not roundtrip serialization without changes:\n" + repr(reconstructed) + "\n" + repr(channels))
return dumped
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 2: if len(sys.argv) > 3:
host, port, pubkey = sys.argv[2:5] host, port, pubkey = sys.argv[3:6]
else: else:
host, port, pubkey = node_list[0] host, port, pubkey = node_list[0]
pubkey = binascii.unhexlify(pubkey) pubkey = binascii.unhexlify(pubkey)
port = int(port) port = int(port)
if sys.argv[1] not in ["simnet", "testnet"]: if sys.argv[1] not in ["new_channel", "reestablish_channel"]:
raise Exception("first argument must be simnet or testnet") raise Exception("first argument must be new_channel or reestablish_channel")
if sys.argv[1] == "simnet": if sys.argv[2] not in ["simnet", "testnet"]:
raise Exception("second argument must be simnet or testnet")
if sys.argv[2] == "simnet":
set_simnet() set_simnet()
config = SimpleConfig({'lnbase':True, 'simnet':True}) config = SimpleConfig({'lnbase':True, 'simnet':True})
else: else:
@ -41,7 +92,7 @@ if __name__ == "__main__":
wallet = Wallet(storage) wallet = Wallet(storage)
wallet.start_threads(network) wallet.start_threads(network)
# start peer # start peer
privkey = sha256(str(time.time())) privkey = sha256("0123456789")
peer = Peer(host, port, pubkey, privkey, request_initial_sync=False, network=network) peer = Peer(host, port, pubkey, privkey, request_initial_sync=False, network=network)
network.futures.append(asyncio.run_coroutine_threadsafe(peer.main_loop(), network.asyncio_loop)) network.futures.append(asyncio.run_coroutine_threadsafe(peer.main_loop(), network.asyncio_loop))
@ -52,7 +103,18 @@ if __name__ == "__main__":
async def async_test(): async def async_test():
payment_preimage = bytes.fromhex("01"*32) payment_preimage = bytes.fromhex("01"*32)
RHASH = sha256(payment_preimage) RHASH = sha256(payment_preimage)
openingchannel = await peer.channel_establishment_flow(wallet, config, None, funding_satoshis, push_msat, temp_channel_id=os.urandom(32)) channels = wallet.storage.get("channels", None)
if sys.argv[1] == "new_channel":
openingchannel = await peer.channel_establishment_flow(wallet, config, None, funding_satoshis, push_msat, temp_channel_id=os.urandom(32))
dumped = serialize_channels([openingchannel])
wallet.storage.put("channels", dumped)
return
else:
openingchannel = json.loads(channels)[0]
openingchannel = reconstruct_namedtuples(openingchannel)
next_local_commitment_number, next_remote_revocation_number = 1, 1
await peer.reestablish_channel(openingchannel, next_local_commitment_number, next_remote_revocation_number)
openchannel = await peer.wait_for_funding_locked(openingchannel, wallet) openchannel = await peer.wait_for_funding_locked(openingchannel, wallet)
expected_received_sat = 400000 expected_received_sat = 400000
pay_req = lnencode(LnAddr(RHASH, amount=Decimal("0.00000001")*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32]) pay_req = lnencode(LnAddr(RHASH, amount=Decimal("0.00000001")*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32])

Loading…
Cancel
Save