Browse Source

lnbase: organize channel data

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
Janus 7 years ago
committed by ThomasV
parent
commit
39dcc24133
  1. 250
      lib/lnbase.py
  2. 5
      lib/tests/test_lnbase_online.py

250
lib/lnbase.py

@ -32,14 +32,6 @@ from .util import PrintError, bh2u, print_error, bfh, profiler
from .transaction import opcodes, Transaction from .transaction import opcodes, Transaction
from collections import namedtuple, defaultdict from collections import namedtuple, defaultdict
LocalCtxArgs = namedtuple("LocalCtxArgs",
["ctn",
"funding_pubkey", "remote_funding_pubkey", "remotepubkey",
"base_point", "remote_payment_basepoint",
"remote_revocation_pubkey", "local_delayedpubkey", "to_self_delay",
"funding_txid", "funding_index", "funding_satoshis",
"local_amount", "remote_amount", "dust_limit_satoshis", "local_feerate",
"commitment_owner"])
# hardcoded nodes # hardcoded nodes
node_list = [ node_list = [
@ -289,6 +281,17 @@ def create_ephemeral_key(privkey):
pub = privkey_to_pubkey(privkey) pub = privkey_to_pubkey(privkey)
return (privkey[:32], pub) return (privkey[:32], pub)
Keypair = namedtuple("Keypair", ["pubkey", "privkey"])
Outpoint = namedtuple("Outpoint", ["txid", "output_index"])
ChannelConfig = namedtuple("ChannelConfig", [
"payment_key", "multisig_key", "htlc_key", "delayed_key", "revocation_key",
"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"])
LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_sat"])
ChannelConstraints = namedtuple("ChannelConstraints", ["feerate", "capacity", "is_initiator"])
OpenChannel = namedtuple("OpenChannel", ["channel_id", "funding_outpoint", "local_config", "remote_config", "remote_state", "local_state", "constraints"])
class KeypairGenerator: class KeypairGenerator:
def __init__(self, seed): def __init__(self, seed):
self.xprv, xpub = bitcoin.bip32_root(seed, "p2wpkh") self.xprv, xpub = bitcoin.bip32_root(seed, "p2wpkh")
@ -297,7 +300,7 @@ class KeypairGenerator:
_, _, _, _, child_c, child_cK = bitcoin.deserialize_xpub(childxpub) _, _, _, _, child_c, child_cK = bitcoin.deserialize_xpub(childxpub)
_, _, _, _, _, k = bitcoin.deserialize_xprv(childxprv) _, _, _, _, _, k = bitcoin.deserialize_xprv(childxprv)
assert len(k) == 32 assert len(k) == 32
return child_cK, k return Keypair(pubkey=child_cK, privkey=k)
def aiosafe(f): def aiosafe(f):
async def f2(*args, **kwargs): async def f2(*args, **kwargs):
@ -446,12 +449,12 @@ def make_received_htlc(revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, p
+ bytes([opcodes.OP_CLTV, opcodes.OP_DROP, opcodes.OP_CHECKSIG, opcodes.OP_ENDIF, opcodes.OP_ENDIF]) + bytes([opcodes.OP_CLTV, opcodes.OP_DROP, opcodes.OP_CHECKSIG, opcodes.OP_ENDIF, opcodes.OP_ENDIF])
def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey, remotepubkey, def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey, remote_payment_pubkey,
payment_basepoint, remote_payment_basepoint, payment_basepoint, remote_payment_basepoint,
revocation_pubkey, delayed_pubkey, to_self_delay, revocation_pubkey, delayed_pubkey, to_self_delay,
funding_txid, funding_pos, funding_satoshis, funding_txid, funding_pos, funding_sat,
local_amount, remote_amount, local_amount, remote_amount,
dust_limit_satoshis, local_feerate, for_us, htlcs): dust_limit_sat, local_feerate, for_us, htlcs):
pubkeys = sorted([bh2u(local_funding_pubkey), bh2u(remote_funding_pubkey)]) pubkeys = sorted([bh2u(local_funding_pubkey), bh2u(remote_funding_pubkey)])
obs = get_obscured_ctn(ctn, payment_basepoint, remote_payment_basepoint) obs = get_obscured_ctn(ctn, payment_basepoint, remote_payment_basepoint)
@ -466,7 +469,7 @@ def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey, remotepubk
'num_sig': 2, 'num_sig': 2,
'prevout_n': funding_pos, 'prevout_n': funding_pos,
'prevout_hash': funding_txid, 'prevout_hash': funding_txid,
'value': funding_satoshis, 'value': funding_sat,
'coinbase': False, 'coinbase': False,
'sequence':sequence 'sequence':sequence
}] }]
@ -474,7 +477,7 @@ def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey, remotepubk
local_script = bytes([opcodes.OP_IF]) + bfh(push_script(bh2u(revocation_pubkey))) + bytes([opcodes.OP_ELSE]) + add_number_to_script(to_self_delay) \ local_script = bytes([opcodes.OP_IF]) + bfh(push_script(bh2u(revocation_pubkey))) + bytes([opcodes.OP_ELSE]) + add_number_to_script(to_self_delay) \
+ bytes([opcodes.OP_CSV, opcodes.OP_DROP]) + bfh(push_script(bh2u(delayed_pubkey))) + bytes([opcodes.OP_ENDIF, opcodes.OP_CHECKSIG]) + bytes([opcodes.OP_CSV, opcodes.OP_DROP]) + bfh(push_script(bh2u(delayed_pubkey))) + bytes([opcodes.OP_ENDIF, opcodes.OP_CHECKSIG])
local_address = bitcoin.redeem_script_to_address('p2wsh', bh2u(local_script)) local_address = bitcoin.redeem_script_to_address('p2wsh', bh2u(local_script))
remote_address = bitcoin.pubkey_to_address('p2wpkh', bh2u(remotepubkey)) remote_address = bitcoin.pubkey_to_address('p2wpkh', bh2u(remote_payment_pubkey))
# TODO trim htlc outputs here while also considering 2nd stage htlc transactions # TODO trim htlc outputs here while also considering 2nd stage htlc transactions
fee = local_feerate * overall_weight(len(htlcs)) // 1000 # TODO incorrect if anything is trimmed fee = local_feerate * overall_weight(len(htlcs)) // 1000 # TODO incorrect if anything is trimmed
to_local = (bitcoin.TYPE_ADDRESS, local_address, local_amount - (fee if for_us else 0)) to_local = (bitcoin.TYPE_ADDRESS, local_address, local_amount - (fee if for_us else 0))
@ -483,7 +486,8 @@ def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey, remotepubk
for script, msat_amount in htlcs: for script, msat_amount in htlcs:
c_outputs += [(bitcoin.TYPE_ADDRESS, bitcoin.redeem_script_to_address('p2wsh', bh2u(script)), msat_amount // 1000)] c_outputs += [(bitcoin.TYPE_ADDRESS, bitcoin.redeem_script_to_address('p2wsh', bh2u(script)), msat_amount // 1000)]
# trim outputs # trim outputs
c_outputs = list(filter(lambda x:x[2]>= dust_limit_satoshis, c_outputs)) c_outputs = list(filter(lambda x:x[2]>= dust_limit_sat, c_outputs))
assert sum(x[2] for x in c_outputs) <= funding_sat
# create commitment tx # create commitment tx
tx = Transaction.from_io(c_inputs, c_outputs, locktime=locktime, version=2) tx = Transaction.from_io(c_inputs, c_outputs, locktime=locktime, version=2)
@ -687,7 +691,7 @@ class Peer(PrintError):
self.channel_db.on_channel_announcement(payload) self.channel_db.on_channel_announcement(payload)
#def open_channel(self, funding_sat, push_msat): #def open_channel(self, funding_sat, push_msat):
# self.send_message(gen_msg('open_channel', funding_satoshis=funding_sat, push_msat=push_msat)) # self.send_message(gen_msg('open_channel', funding_sat=funding_sat, push_msat=push_msat))
@aiosafe @aiosafe
async def main_loop(self): async def main_loop(self):
@ -710,7 +714,7 @@ class Peer(PrintError):
self.writer.close() self.writer.close()
@aiosafe @aiosafe
async def channel_establishment_flow(self, wallet, config, funding_satoshis, push_msat, temp_channel_id, keypair_generator=None): async def channel_establishment_flow(self, wallet, config, funding_sat, push_msat, temp_channel_id, keypair_generator=None):
await self.initialized await self.initialized
if keypair_generator is None: if keypair_generator is None:
@ -725,40 +729,41 @@ class Peer(PrintError):
keyfamilyrevocationroot = 5 keyfamilyrevocationroot = 5
keyfamilynodekey = 6 # TODO currently unused keyfamilynodekey = 6 # TODO currently unused
funding_pubkey, funding_privkey = keypair_generator.get(keyfamilymultisig, 0) funding_key = keypair_generator.get(keyfamilymultisig, 0)
revocation_basepoint, _ = keypair_generator.get(keyfamilyrevocationbase, 0) revocation_key = keypair_generator.get(keyfamilyrevocationbase, 0)
htlc_basepoint, htlc_privkey = keypair_generator.get(keyfamilyhtlcbase, 0) htlc_key = keypair_generator.get(keyfamilyhtlcbase, 0)
delayed_payment_basepoint, delayed_privkey = keypair_generator.get(keyfamilydelaybase, 0) delayed_key = keypair_generator.get(keyfamilydelaybase, 0)
base_point, _ = keypair_generator.get(keyfamilypaymentbase, 0) base_point = keypair_generator.get(keyfamilypaymentbase, 0)
per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(32, "big") per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(32, "big")
per_commitment_secret_index = 2**48 - 1 per_commitment_secret_index = 2**48 - 1
# amounts # amounts
local_feerate = 20000 local_feerate = 20000
dust_limit_satoshis = 10
to_self_delay = 144 to_self_delay = 144
ctn = 0 dust_limit_sat = 10
# #
per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, per_commitment_secret_index) per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, per_commitment_secret_index)
per_commitment_point_first = secret_to_pubkey(int.from_bytes( per_commitment_point_first = secret_to_pubkey(int.from_bytes(
per_commitment_secret_first, per_commitment_secret_first,
byteorder="big")) byteorder="big"))
local_max_htlc_value_in_flight_msat = 500000 * 1000
local_max_accepted_htlcs = 5
msg = gen_msg( msg = gen_msg(
"open_channel", "open_channel",
temporary_channel_id=temp_channel_id, temporary_channel_id=temp_channel_id,
chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)), chain_hash=bytes.fromhex(rev_hex(constants.net.GENESIS)),
funding_satoshis=funding_satoshis, funding_satoshis=funding_sat,
push_msat=push_msat, push_msat=push_msat,
dust_limit_satoshis=dust_limit_satoshis, dust_limit_satoshis=dust_limit_sat,
feerate_per_kw=local_feerate, feerate_per_kw=local_feerate,
max_accepted_htlcs=5, max_accepted_htlcs=local_max_accepted_htlcs,
funding_pubkey=funding_pubkey, funding_pubkey=funding_key.pubkey,
revocation_basepoint=revocation_basepoint, revocation_basepoint=revocation_key.pubkey,
htlc_basepoint=htlc_basepoint, htlc_basepoint=htlc_key.pubkey,
payment_basepoint=base_point, payment_basepoint=base_point.pubkey,
delayed_payment_basepoint=delayed_payment_basepoint, delayed_payment_basepoint=delayed_key.pubkey,
first_per_commitment_point=per_commitment_point_first, first_per_commitment_point=per_commitment_point_first,
to_self_delay=to_self_delay, to_self_delay=to_self_delay,
max_htlc_value_in_flight_msat=500000 * 1000 max_htlc_value_in_flight_msat=local_max_htlc_value_in_flight_msat
) )
#self.channel_accepted[temp_channel_id] = asyncio.Future() #self.channel_accepted[temp_channel_id] = asyncio.Future()
self.send_message(msg) self.send_message(msg)
@ -766,10 +771,11 @@ class Peer(PrintError):
payload = await self.channel_accepted[temp_channel_id] payload = await self.channel_accepted[temp_channel_id]
finally: finally:
del self.channel_accepted[temp_channel_id] del self.channel_accepted[temp_channel_id]
remote_max_accepted_htlcs = payload["max_accepted_htlcs"]
remote_per_commitment_point = payload['first_per_commitment_point'] remote_per_commitment_point = payload['first_per_commitment_point']
remote_funding_pubkey = payload["funding_pubkey"] remote_funding_pubkey = payload["funding_pubkey"]
remote_delay = int.from_bytes(payload['to_self_delay'], byteorder="big") remote_delay = int.from_bytes(payload['to_self_delay'], byteorder="big")
remote_dust_limit_satoshis = int.from_bytes(payload['dust_limit_satoshis'], byteorder="big") remote_dust_limit_sat = int.from_bytes(payload['dust_limit_satoshis'], byteorder="big")
remote_revocation_basepoint = payload['revocation_basepoint'] remote_revocation_basepoint = payload['revocation_basepoint']
remote_payment_basepoint = payload['payment_basepoint'] remote_payment_basepoint = payload['payment_basepoint']
remote_delayed_payment_basepoint = payload['delayed_payment_basepoint'] remote_delayed_payment_basepoint = payload['delayed_payment_basepoint']
@ -777,44 +783,44 @@ class Peer(PrintError):
remote_htlc_minimum_msat = int.from_bytes(payload['htlc_minimum_msat'], "big") remote_htlc_minimum_msat = int.from_bytes(payload['htlc_minimum_msat'], "big")
remote_max_htlc_value_in_flight_msat = int.from_bytes(payload['max_htlc_value_in_flight_msat'], "big") remote_max_htlc_value_in_flight_msat = int.from_bytes(payload['max_htlc_value_in_flight_msat'], "big")
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], byteorder="big") funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], byteorder="big")
print('remote dust limit', remote_dust_limit_satoshis) print('remote dust limit', remote_dust_limit_sat)
assert remote_dust_limit_satoshis < 600 assert remote_dust_limit_sat < 600
assert remote_htlc_minimum_msat < 600 * 1000 assert remote_htlc_minimum_msat < 600 * 1000
assert remote_max_htlc_value_in_flight_msat >= 500 * 1000 * 1000, remote_max_htlc_value_in_flight_msat assert remote_max_htlc_value_in_flight_msat >= 500 * 1000 * 1000, remote_max_htlc_value_in_flight_msat
self.print_error('remote delay', remote_delay) self.print_error('remote delay', remote_delay)
self.print_error('funding_txn_minimum_depth', funding_txn_minimum_depth) self.print_error('funding_txn_minimum_depth', funding_txn_minimum_depth)
# create funding tx # create funding tx
pubkeys = sorted([bh2u(funding_pubkey), bh2u(remote_funding_pubkey)]) pubkeys = sorted([bh2u(funding_key.pubkey), bh2u(remote_funding_pubkey)])
redeem_script = transaction.multisig_script(pubkeys, 2) redeem_script = transaction.multisig_script(pubkeys, 2)
funding_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script) funding_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script)
funding_output = (bitcoin.TYPE_ADDRESS, funding_address, funding_satoshis) funding_output = (bitcoin.TYPE_ADDRESS, funding_address, funding_sat)
funding_tx = wallet.mktx([funding_output], None, config, 1000) funding_tx = wallet.mktx([funding_output], None, config, 1000)
funding_txid = funding_tx.txid() funding_txid = funding_tx.txid()
funding_index = funding_tx.outputs().index(funding_output) funding_index = funding_tx.outputs().index(funding_output)
# derive keys # derive keys
localpubkey = derive_pubkey(base_point, remote_per_commitment_point) #local_payment_pubkey local_payment_pubkey = derive_pubkey(base_point.pubkey, remote_per_commitment_point)
#localprivkey = derive_privkey(base_secret, remote_per_commitment_point) #local_payment_privkey = derive_privkey(base_secret, remote_per_commitment_point)
remotepubkey = derive_pubkey(remote_payment_basepoint, per_commitment_point_first) remote_payment_pubkey = derive_pubkey(remote_payment_basepoint, per_commitment_point_first)
revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_per_commitment_point) revocation_pubkey = derive_blinded_pubkey(revocation_key.pubkey, remote_per_commitment_point)
remote_revocation_pubkey = derive_blinded_pubkey(remote_revocation_basepoint, per_commitment_point_first) remote_revocation_pubkey = derive_blinded_pubkey(remote_revocation_basepoint, per_commitment_point_first)
local_delayedpubkey = derive_pubkey(delayed_payment_basepoint, per_commitment_point_first) local_delayedpubkey = derive_pubkey(delayed_key.pubkey, per_commitment_point_first)
remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_per_commitment_point) remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_per_commitment_point)
# compute amounts # compute amounts
htlcs = [] htlcs = []
to_local_msat = funding_satoshis*1000 - push_msat to_local_msat = funding_sat*1000 - push_msat
to_remote_msat = push_msat to_remote_msat = push_msat
local_amount = to_local_msat // 1000 local_amount = to_local_msat // 1000
remote_amount = to_remote_msat // 1000 remote_amount = to_remote_msat // 1000
# remote commitment transaction # remote commitment transaction
remote_ctx = make_commitment( remote_ctx = make_commitment(
ctn, 0,
remote_funding_pubkey, funding_pubkey, localpubkey, remote_funding_pubkey, funding_key.pubkey, local_payment_pubkey,
base_point, remote_payment_basepoint, base_point.pubkey, remote_payment_basepoint,
revocation_pubkey, remote_delayedpubkey, remote_delay, revocation_pubkey, remote_delayedpubkey, remote_delay,
funding_txid, funding_index, funding_satoshis, funding_txid, funding_index, funding_sat,
remote_amount, local_amount, remote_dust_limit_satoshis, local_feerate, False, htlcs=[]) remote_amount, local_amount, remote_dust_limit_sat, local_feerate, False, htlcs=[])
remote_ctx.sign({bh2u(funding_pubkey): (funding_privkey, True)}) remote_ctx.sign({bh2u(funding_key.pubkey): (funding_key.privkey, True)})
sig_index = pubkeys.index(bh2u(funding_pubkey)) sig_index = pubkeys.index(bh2u(funding_key.pubkey))
sig = bytes.fromhex(remote_ctx.inputs()[0]["signatures"][sig_index]) sig = bytes.fromhex(remote_ctx.inputs()[0]["signatures"][sig_index])
r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order()) r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order()) sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())
@ -829,14 +835,13 @@ class Peer(PrintError):
self.print_error('received funding_signed') self.print_error('received funding_signed')
remote_sig = payload['signature'] remote_sig = payload['signature']
# verify remote signature # verify remote signature
local_ctx_args = LocalCtxArgs( local_ctx = make_commitment(
ctn, 0,
funding_pubkey, remote_funding_pubkey, remotepubkey, funding_key.pubkey, remote_funding_pubkey, remote_payment_pubkey,
base_point, remote_payment_basepoint, base_point.pubkey, remote_payment_basepoint,
remote_revocation_pubkey, local_delayedpubkey, to_self_delay, remote_revocation_pubkey, local_delayedpubkey, to_self_delay,
funding_txid, funding_index, funding_satoshis, funding_txid, funding_index, funding_sat,
local_amount, remote_amount, dust_limit_satoshis, local_feerate, True) local_amount, remote_amount, dust_limit_sat, local_feerate, True, htlcs=[])
local_ctx = make_commitment(*local_ctx_args, htlcs=[])
pre_hash = bitcoin.Hash(bfh(local_ctx.serialize_preimage(0))) pre_hash = bitcoin.Hash(bfh(local_ctx.serialize_preimage(0)))
if not bitcoin.verify_signature(remote_funding_pubkey, remote_sig, pre_hash): if not bitcoin.verify_signature(remote_funding_pubkey, remote_sig, pre_hash):
raise Exception('verifying remote signature failed.') raise Exception('verifying remote signature failed.')
@ -876,18 +881,68 @@ class Peer(PrintError):
del self.remote_funding_locked[channel_id] del self.remote_funding_locked[channel_id]
self.print_error('Done waiting for remote_funding_locked', remote_funding_locked_msg) self.print_error('Done waiting for remote_funding_locked', remote_funding_locked_msg)
#self.commitment_signed[channel_id] = asyncio.Future() #self.commitment_signed[channel_id] = asyncio.Future()
return channel_id, per_commitment_secret_seed, local_ctx_args, remote_funding_pubkey, remote_funding_locked_msg, remote_revocation_basepoint, remote_htlc_basepoint, htlc_basepoint, delayed_payment_basepoint, revocation_basepoint, remote_delayed_payment_basepoint, remote_delay, remote_dust_limit_satoshis, funding_privkey, htlc_privkey
async def receive_commitment_revoke_ack(self, channel_id, local_per_commitment_secret_seed, local_last_pcs_index, local_ctx_args, expected_received_sat, remote_funding_pubkey, local_next_commitment_number, remote_next_commitment_point, remote_revocation_basepoint, remote_htlc_basepoint, local_htlc_basepoint, delayed_payment_basepoint, revocation_basepoint, remote_delayed_payment_basepoint, remote_delay, remote_dust_limit_satoshis, funding_privkey, payment_preimage, htlc_privkey): return OpenChannel(
channel_id=channel_id,
funding_outpoint=Outpoint(funding_txid, funding_index),
local_config=ChannelConfig(
payment_key=base_point,
multisig_key=funding_key,
htlc_key=htlc_key,
delayed_key=delayed_key,
revocation_key=revocation_key,
to_self_delay=to_self_delay,
dust_limit_sat=dust_limit_sat,
max_htlc_value_in_flight_msat=local_max_htlc_value_in_flight_msat,
max_accepted_htlcs=local_max_accepted_htlcs
),
remote_config=ChannelConfig(
payment_key=OnlyPubkeyKeypair(remote_payment_basepoint),
multisig_key=OnlyPubkeyKeypair(remote_funding_pubkey),
htlc_key=OnlyPubkeyKeypair(remote_htlc_basepoint),
delayed_key=OnlyPubkeyKeypair(remote_delayed_payment_basepoint),
revocation_key=OnlyPubkeyKeypair(remote_revocation_basepoint),
to_self_delay=remote_delay,
dust_limit_sat=remote_dust_limit_sat,
max_htlc_value_in_flight_msat=remote_max_htlc_value_in_flight_msat,
max_accepted_htlcs=remote_max_accepted_htlcs
),
remote_state=RemoteState(
ctn = 0,
next_per_commitment_point=remote_funding_locked_msg["next_per_commitment_point"],
amount_sat=remote_amount
),
local_state=LocalState(
ctn = 0,
per_commitment_secret_seed=per_commitment_secret_seed,
amount_sat=local_amount
),
constraints=ChannelConstraints(capacity=funding_sat, feerate=local_feerate, is_initiator=True)
)
async def receive_commitment_revoke_ack(self, openchannel, expected_received_sat, payment_preimage):
channel_id = openchannel.channel_id
local_per_commitment_secret_seed = openchannel.local_state.per_commitment_secret_seed
assert openchannel.local_state.ctn == 0
local_next_pcs_index = 2**48 - 2
remote_funding_pubkey = openchannel.remote_config.multisig_key.pubkey
remote_next_commitment_point = openchannel.remote_state.next_per_commitment_point
remote_revocation_basepoint = openchannel.remote_config.revocation_key.pubkey
remote_htlc_basepoint = openchannel.remote_config.htlc_key.pubkey
local_htlc_basepoint = openchannel.local_config.htlc_key.pubkey
delayed_payment_basepoint = openchannel.local_config.delayed_key.pubkey
revocation_basepoint = openchannel.local_config.revocation_key.pubkey
remote_delayed_payment_basepoint = openchannel.remote_config.delayed_key.pubkey
remote_delay = openchannel.remote_config.to_self_delay
remote_dust_limit_sat = openchannel.remote_config.dust_limit_sat
funding_privkey = openchannel.local_config.multisig_key.privkey
htlc_privkey = openchannel.local_config.htlc_key.privkey
try: try:
commitment_signed_msg = await self.commitment_signed[channel_id] commitment_signed_msg = await self.commitment_signed[channel_id]
finally: finally:
del self.commitment_signed[channel_id] del self.commitment_signed[channel_id]
# TODO make new future? (there could be more updates)
local_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index) local_next_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_next_pcs_index)
local_next_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index - 1)
local_next_per_commitment_point = secret_to_pubkey(int.from_bytes( local_next_per_commitment_point = secret_to_pubkey(int.from_bytes(
local_next_per_commitment_secret, local_next_per_commitment_secret,
byteorder="big")) byteorder="big"))
@ -902,11 +957,6 @@ class Peer(PrintError):
amount_msat = int.from_bytes(self.unfulfilled_htlcs[0]["amount_msat"], "big") amount_msat = int.from_bytes(self.unfulfilled_htlcs[0]["amount_msat"], "big")
remote_revocation_pubkey = derive_blinded_pubkey(remote_revocation_basepoint, local_next_per_commitment_point) remote_revocation_pubkey = derive_blinded_pubkey(remote_revocation_basepoint, local_next_per_commitment_point)
local_ctx_args = local_ctx_args._replace(remote_amount = local_ctx_args.remote_amount - expected_received_sat)
local_ctx_args = local_ctx_args._replace(ctn = local_next_commitment_number)
local_ctx_args = local_ctx_args._replace(remote_revocation_pubkey = remote_revocation_pubkey)
local_ctx_args = local_ctx_args._replace(remotepubkey = derive_pubkey(local_ctx_args.remote_payment_basepoint, local_next_per_commitment_point))
local_ctx_args = local_ctx_args._replace(local_delayedpubkey = derive_pubkey(delayed_payment_basepoint, local_next_per_commitment_point))
htlcs_in_local = [ htlcs_in_local = [
( (
@ -915,7 +965,23 @@ class Peer(PrintError):
) )
] ]
new_commitment = make_commitment(*local_ctx_args, htlcs=htlcs_in_local) new_commitment = make_commitment(
1,
openchannel.local_config.multisig_key.pubkey,
openchannel.remote_config.multisig_key.pubkey,
derive_pubkey(openchannel.remote_config.payment_key.pubkey, local_next_per_commitment_point),
openchannel.local_config.payment_key.pubkey,
openchannel.remote_config.payment_key.pubkey,
remote_revocation_pubkey,
derive_pubkey(delayed_payment_basepoint, local_next_per_commitment_point),
openchannel.local_config.to_self_delay,
*openchannel.funding_outpoint,
openchannel.constraints.capacity,
openchannel.local_state.amount_sat,
openchannel.remote_state.amount_sat - expected_received_sat,
openchannel.local_config.dust_limit_sat,
openchannel.constraints.feerate,
True, htlcs=htlcs_in_local)
preimage_hex = new_commitment.serialize_preimage(0) preimage_hex = new_commitment.serialize_preimage(0)
pre_hash = bitcoin.Hash(bfh(preimage_hex)) pre_hash = bitcoin.Hash(bfh(preimage_hex))
if not bitcoin.verify_signature(remote_funding_pubkey, commitment_signed_msg["signature"], pre_hash): if not bitcoin.verify_signature(remote_funding_pubkey, commitment_signed_msg["signature"], pre_hash):
@ -927,7 +993,13 @@ class Peer(PrintError):
# TODO verify htlc_signature # TODO verify htlc_signature
pubkeys = sorted([bh2u(local_ctx_args.funding_pubkey), bh2u(remote_funding_pubkey)]) local_last_pcs_index = 2**48 - openchannel.local_state.ctn - 1
local_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index)
self.send_message(gen_msg("revoke_and_ack", channel_id=channel_id, per_commitment_secret=local_last_per_commitment_secret, next_per_commitment_point=local_next_per_commitment_point))
funding_pubkey = openchannel.local_config.multisig_key.pubkey
pubkeys = sorted([bh2u(funding_pubkey), bh2u(remote_funding_pubkey)])
revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_next_commitment_point) revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_next_commitment_point)
remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point) remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point)
their_local_htlc_pubkey = derive_pubkey(remote_htlc_basepoint, remote_next_commitment_point) their_local_htlc_pubkey = derive_pubkey(remote_htlc_basepoint, remote_next_commitment_point)
@ -936,25 +1008,24 @@ class Peer(PrintError):
their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, "big") their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, "big")
# TODO check payment_hash # TODO check payment_hash
htlcs_in_remote = [(make_offered_htlc(revocation_pubkey, their_remote_htlc_pubkey, their_local_htlc_pubkey, payment_hash), amount_msat)] htlcs_in_remote = [(make_offered_htlc(revocation_pubkey, their_remote_htlc_pubkey, their_local_htlc_pubkey, payment_hash), amount_msat)]
local_payment_pubkey = derive_pubkey(local_ctx_args.base_point, remote_next_commitment_point) local_payment_pubkey = derive_pubkey(openchannel.local_config.payment_key.pubkey, remote_next_commitment_point)
remote_ctx = make_commitment( remote_ctx = make_commitment(
1, 1,
remote_funding_pubkey, local_ctx_args.funding_pubkey, local_payment_pubkey, remote_funding_pubkey, funding_pubkey, local_payment_pubkey,
local_ctx_args.base_point, local_ctx_args.remote_payment_basepoint, openchannel.local_config.payment_key.pubkey, openchannel.remote_config.payment_key.pubkey,
revocation_pubkey, remote_delayedpubkey, remote_delay, revocation_pubkey, remote_delayedpubkey, remote_delay,
local_ctx_args.funding_txid, local_ctx_args.funding_index, local_ctx_args.funding_satoshis, *openchannel.funding_outpoint, openchannel.constraints.capacity,
local_ctx_args.remote_amount, local_ctx_args.local_amount, remote_dust_limit_satoshis, local_ctx_args.local_feerate, False, htlcs=htlcs_in_remote) openchannel.remote_state.amount_sat - expected_received_sat, openchannel.local_state.amount_sat, remote_dust_limit_sat,
remote_ctx.sign({bh2u(local_ctx_args.funding_pubkey): (funding_privkey, True)}) openchannel.constraints.feerate, False, htlcs=htlcs_in_remote)
sig_index = pubkeys.index(bh2u(local_ctx_args.funding_pubkey)) remote_ctx.sign({bh2u(funding_pubkey): (funding_privkey, True)})
sig_index = pubkeys.index(bh2u(funding_pubkey))
sig = bytes.fromhex(remote_ctx.inputs()[0]["signatures"][sig_index]) sig = bytes.fromhex(remote_ctx.inputs()[0]["signatures"][sig_index])
r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order()) r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order()) sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())
self.send_message(gen_msg("revoke_and_ack", channel_id=channel_id, per_commitment_secret=local_last_per_commitment_secret, next_per_commitment_point=local_next_per_commitment_point))
htlc_tx_output = make_htlc_tx_output( htlc_tx_output = make_htlc_tx_output(
amount_msat = amount_msat, amount_msat = amount_msat,
local_feerate = local_ctx_args.local_feerate, local_feerate = openchannel.constraints.feerate,
revocationpubkey=revocation_pubkey, revocationpubkey=revocation_pubkey,
local_delayedpubkey=remote_delayedpubkey, local_delayedpubkey=remote_delayedpubkey,
success = False) # timeout for the one offering an HTLC success = False) # timeout for the one offering an HTLC
@ -990,18 +1061,19 @@ class Peer(PrintError):
revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_next_commitment_point) revocation_pubkey = derive_blinded_pubkey(revocation_basepoint, remote_next_commitment_point)
remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point) remote_delayedpubkey = derive_pubkey(remote_delayed_payment_basepoint, remote_next_commitment_point)
local_payment_pubkey = derive_pubkey(local_ctx_args.base_point, remote_next_commitment_point) local_payment_pubkey = derive_pubkey(openchannel.local_config.payment_key.pubkey, remote_next_commitment_point)
# local commitment transaction without htlcs # remote commitment transaction without htlcs
bare_ctx = make_commitment( bare_ctx = make_commitment(
2, 2,
remote_funding_pubkey, local_ctx_args.funding_pubkey, local_payment_pubkey, remote_funding_pubkey, funding_pubkey, local_payment_pubkey,
local_ctx_args.base_point, local_ctx_args.remote_payment_basepoint, openchannel.local_config.payment_key.pubkey, openchannel.remote_config.payment_key.pubkey,
revocation_pubkey, remote_delayedpubkey, remote_delay, revocation_pubkey, remote_delayedpubkey, remote_delay,
local_ctx_args.funding_txid, local_ctx_args.funding_index, local_ctx_args.funding_satoshis, *openchannel.funding_outpoint, openchannel.constraints.capacity,
local_ctx_args.remote_amount, local_ctx_args.local_amount + expected_received_sat, remote_dust_limit_satoshis, local_ctx_args.local_feerate, False, htlcs=[]) openchannel.remote_state.amount_sat - expected_received_sat, openchannel.local_state.amount_sat + expected_received_sat,
remote_dust_limit_sat, openchannel.constraints.feerate, False, htlcs=[])
bare_ctx.sign({bh2u(local_ctx_args.funding_pubkey): (funding_privkey, True)}) bare_ctx.sign({bh2u(funding_pubkey): (funding_privkey, True)})
sig_index = pubkeys.index(bh2u(local_ctx_args.funding_pubkey)) sig_index = pubkeys.index(bh2u(funding_pubkey))
sig = bytes.fromhex(bare_ctx.inputs()[0]["signatures"][sig_index]) sig = bytes.fromhex(bare_ctx.inputs()[0]["signatures"][sig_index])
r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order()) r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order()) sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())

5
lib/tests/test_lnbase_online.py

@ -4,6 +4,7 @@ import json
import binascii import binascii
import asyncio import asyncio
import time import time
import os
from lib.bitcoin import sha256 from lib.bitcoin import sha256
from decimal import Decimal from decimal import Decimal
@ -50,12 +51,12 @@ 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)
channel_id, per_commitment_secret_seed, local_ctx_args, remote_funding_pubkey, remote_funding_locked_msg, remote_revocation_basepoint, remote_htlc_basepoint, local_htlc_basepoint, delayed_payment_basepoint, revocation_basepoint, remote_delayed_payment_basepoint, remote_delay, remote_dust_limit_satoshis, funding_privkey, htlc_privkey = await peer.channel_establishment_flow(wallet, config, funding_satoshis, push_msat) openchannel = await peer.channel_establishment_flow(wallet, config, funding_satoshis, push_msat, temp_channel_id=os.urandom(32))
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])
print("payment request", pay_req) print("payment request", pay_req)
last_pcs_index = 2**48 - 1 last_pcs_index = 2**48 - 1
await peer.receive_commitment_revoke_ack(channel_id, per_commitment_secret_seed, last_pcs_index, local_ctx_args, expected_received_sat, remote_funding_pubkey, local_next_commitment_number=1, remote_next_commitment_point=remote_funding_locked_msg["next_per_commitment_point"], remote_revocation_basepoint=remote_revocation_basepoint, remote_htlc_basepoint=remote_htlc_basepoint, local_htlc_basepoint=local_htlc_basepoint, delayed_payment_basepoint=delayed_payment_basepoint, revocation_basepoint=revocation_basepoint, remote_delayed_payment_basepoint=remote_delayed_payment_basepoint, remote_delay=remote_delay, remote_dust_limit_satoshis=remote_dust_limit_satoshis, funding_privkey=funding_privkey, htlc_privkey=htlc_privkey, payment_preimage=payment_preimage) await peer.receive_commitment_revoke_ack(openchannel, expected_received_sat, payment_preimage)
fut = asyncio.run_coroutine_threadsafe(async_test(), network.asyncio_loop) fut = asyncio.run_coroutine_threadsafe(async_test(), network.asyncio_loop)
while not fut.done(): while not fut.done():
time.sleep(1) time.sleep(1)

Loading…
Cancel
Save