lnbase: organize channel data
This commit is contained in:
parent
07624960e5
commit
9a6646c780
250
lib/lnbase.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())
|
||||||
|
|||||||
@ -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…
Reference in New Issue
Block a user