Browse Source

lnbase: fix pay(), save htlc_id's, generate onion packet correctly

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
Janus 7 years ago
committed by ThomasV
parent
commit
b81fb44952
  1. 81
      lib/lnbase.py
  2. 13
      lib/tests/test_lnbase_online.py

81
lib/lnbase.py

@ -275,8 +275,8 @@ ChannelConfig = namedtuple("ChannelConfig", [
"payment_basepoint", "multisig_key", "htlc_basepoint", "delayed_basepoint", "revocation_basepoint",
"to_self_delay", "dust_limit_sat", "max_htlc_value_in_flight_msat", "max_accepted_htlcs"])
OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"])
RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_sat", "revocation_store", "last_per_commitment_point"])
LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_sat"])
RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_sat", "revocation_store", "last_per_commitment_point", "next_htlc_id"])
LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_sat", "next_htlc_id"])
ChannelConstraints = namedtuple("ChannelConstraints", ["feerate", "capacity", "is_initiator", "funding_txn_minimum_depth"])
OpenChannel = namedtuple("OpenChannel", ["channel_id", "short_channel_id", "funding_outpoint", "local_config", "remote_config", "remote_state", "local_state", "constraints"])
@ -563,6 +563,11 @@ def sign_and_get_sig_string(tx, local_config, remote_config):
sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())
return sig_64
def is_synced(network):
local_height, server_height = network.get_status_value("updated")
synced = server_height != 0 and network.is_up_to_date() and local_height >= server_height
return synced
class Peer(PrintError):
def __init__(self, host, port, pubkey, privkey, request_initial_sync=False, network=None):
self.host = host
@ -606,7 +611,7 @@ class Peer(PrintError):
def send_message(self, msg):
message_type, payload = decode_msg(msg)
self.print_error("Sending '%s'"%message_type.upper(), payload)
self.print_error("Sending '%s'"%message_type.upper())
l = len(msg).to_bytes(2, 'big')
lc = aead_encrypt(self.sk, self.sn(), b'', l)
c = aead_encrypt(self.sk, self.sn(), b'', msg)
@ -937,12 +942,14 @@ class Peer(PrintError):
next_per_commitment_point=None,
last_per_commitment_point=remote_per_commitment_point,
amount_sat=remote_amount,
revocation_store=their_revocation_store
revocation_store=their_revocation_store,
next_htlc_id = 0
),
local_state=LocalState(
ctn = 0,
per_commitment_secret_seed=per_commitment_secret_seed,
amount_sat=local_amount
amount_sat=local_amount,
next_htlc_id = 0
),
constraints=ChannelConstraints(capacity=funding_sat, feerate=local_feerate, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth)
)
@ -1040,17 +1047,22 @@ class Peer(PrintError):
)
)
return last_secret, this_point, next_point
their_revstore = chan.remote_state.revocation_store
sat = int(sat)
cltv_expiry = wallet.get_local_height() + 9
await asyncio.sleep(1)
while not is_synced(wallet.network):
await asyncio.sleep(1)
print("sleeping more")
cltv_expiry = wallet.get_local_height() + chan.remote_config.to_self_delay
assert sat > 0, "sat is not positive"
amount_msat = sat * 1000
assert type(self.pubkey) is bytes
hops_data = [OnionHopsDataSingle(OnionPerHop(chan.short_channel_id, amount_msat.to_bytes(8, "big"), cltv_expiry.to_bytes(4, "big")))]
associated_data = b""
onion = new_onion_packet([self.pubkey], self.privkey, hops_data, associated_data)
associated_data = payment_hash
onion = new_onion_packet([self.pubkey], os.urandom(32), hops_data, associated_data)
self.send_message(gen_msg("update_add_htlc", channel_id=chan.channel_id, id=0, cltv_expiry=cltv_expiry, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes()))
self.send_message(gen_msg("update_add_htlc", channel_id=chan.channel_id, id=chan.local_state.next_htlc_id, cltv_expiry=cltv_expiry, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes()))
their_local_htlc_pubkey = derive_pubkey(chan.remote_config.htlc_basepoint.pubkey, chan.remote_state.next_per_commitment_point)
their_remote_htlc_pubkey = derive_pubkey(chan.local_config.htlc_basepoint.pubkey, chan.remote_state.next_per_commitment_point)
@ -1074,18 +1086,25 @@ class Peer(PrintError):
self.send_message(gen_msg("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=1, htlc_signature=htlc_sig))
try:
revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id]
finally:
del self.revoke_and_ack[chan.channel_id]
# TODO check revoke_and_ack results
last_secret, _, next_point = derive_and_incr()
their_revstore.add_next_entry(last_secret)
self.send_message(gen_msg("revoke_and_ack",
channel_id=chan.channel_id,
per_commitment_secret=last_secret,
next_per_commitment_point=next_point))
try:
revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id]
update_fulfill_htlc_msg = await self.update_fulfill_htlc[chan.channel_id]
finally:
del self.revoke_and_ack[chan.channel_id]
del self.update_fulfill_htlc[chan.channel_id]
# TODO check revoke_and_ack results
# TODO use other fields too
next_per_commitment_point = revoke_and_ack_msg["next_per_commitment_point"]
try:
@ -1096,26 +1115,37 @@ class Peer(PrintError):
# TODO check commitment_signed results
last_secret, _, next_point = derive_and_incr()
their_revstore.add_next_entry(last_secret)
self.send_message(gen_msg("revoke_and_ack",
channel_id=chan.channel_id,
per_commitment_secret=last_secret,
next_per_commitment_point=next_point))
try:
update_fulfill_htlc_msg = await self.update_fulfill_htlc[chan.channel_id]
finally:
del self.update_fulfill_htlc[chan.channel_id]
bare_ctx = make_commitment_using_open_channel(chan, chan.remote_state.ctn + 2, False, next_per_commitment_point,
chan.remote_state.amount_sat + sat, chan.local_state.amount_sat - sat)
print("update_fulfill_htlc_msg", update_fulfill_htlc_msg)
sig_64 = sign_and_get_sig_string(bare_ctx, chan.local_config, chan.remote_config)
self.send_message(gen_msg("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=0))
try:
commitment_signed_msg = await self.commitment_signed[chan.channel_id]
revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id]
finally:
del self.commitment_signed[chan.channel_id]
# TODO send revoke_and_ack
del self.revoke_and_ack[chan.channel_id]
# TODO check revoke_and_ack results
# TODO send commitment_signed
return chan._replace(
local_state=chan.local_state._replace(
amount_sat=chan.local_state.amount_sat - sat,
next_htlc_id=chan.local_state.next_htlc_id + 1
),
remote_state=chan.remote_state._replace(
ctn=chan.remote_state.ctn + 2,
revocation_store=their_revstore,
last_per_commitment_point=next_per_commitment_point,
next_per_commitment_point=revoke_and_ack_msg["next_per_commitment_point"],
amount_sat=chan.remote_state.amount_sat + sat
)
)
async def receive_commitment_revoke_ack(self, chan, expected_received_sat, payment_preimage):
def derive_and_incr():
@ -1143,8 +1173,10 @@ class Peer(PrintError):
finally:
del self.commitment_signed[channel_id]
assert len(self.unfulfilled_htlcs) == 1
htlc = self.unfulfilled_htlcs.pop()
htlc_id = int.from_bytes(htlc["id"], 'big')
assert htlc_id == chan.remote_state.next_htlc_id, (htlc_id, chan.remote_state.next_htlc_id)
cltv_expiry = int.from_bytes(htlc["cltv_expiry"], 'big')
# TODO verify sanity of their cltv expiry
amount_msat = int.from_bytes(htlc["amount_msat"], 'big')
@ -1264,7 +1296,8 @@ class Peer(PrintError):
revocation_store=their_revstore,
last_per_commitment_point=remote_next_commitment_point,
next_per_commitment_point=revoke_and_ack_msg["next_per_commitment_point"],
amount_sat=chan.remote_state.amount_sat - expected_received_sat
amount_sat=chan.remote_state.amount_sat - expected_received_sat,
next_htlc_id=htlc_id + 1
)
)
@ -1275,7 +1308,7 @@ class Peer(PrintError):
def on_update_fulfill_htlc(self, payload):
channel_id = int.from_bytes(payload["channel_id"], 'big')
self.update_fulfill_htlc[channel_id].set_reuslt(payload)
self.update_fulfill_htlc[channel_id].set_result(payload)
def on_update_fail_malformed_htlc(self, payload):
self.on_error(payload)

13
lib/tests/test_lnbase_online.py

@ -128,14 +128,13 @@ if __name__ == "__main__":
addr = lndecode(sys.argv[6], expected_hrp="sb" if sys.argv[2] == "simnet" else "tb")
payment_hash = addr.paymenthash
amt = int(addr.amount * COIN)
print("amt", amt)
await peer.pay(wallet, openchannel, amt, payment_hash)
return
advanced_channel = await peer.pay(wallet, openchannel, amt, payment_hash)
else:
expected_received_sat = 200000
pay_req = lnencode(LnAddr(RHASH, amount=1/Decimal(COIN)*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32])
print("payment request", pay_req)
advanced_channel = await peer.receive_commitment_revoke_ack(openchannel, expected_received_sat, payment_preimage)
expected_received_sat = 200000
pay_req = lnencode(LnAddr(RHASH, amount=1/Decimal(COIN)*expected_received_sat, tags=[('d', 'one cup of coffee')]), peer.privkey[:32])
print("payment request", pay_req)
advanced_channel = await peer.receive_commitment_revoke_ack(openchannel, expected_received_sat, payment_preimage)
dumped = serialize_channels([advanced_channel])
wallet.storage.put("channels", dumped)
wallet.storage.write()

Loading…
Cancel
Save