commit c2bbc1ec6050963db654b18013ce39616729a0f9
parent 9f8d6625ecc6e957f5b02ffce5db7b139b454158
Author: Janus <ysangkok@gmail.com>
Date: Mon, 30 Apr 2018 17:07:52 +0200
lnbase: allow passing KeypairGenerator to channel_establishment_flow, fix derive_privkey
Diffstat:
M | lib/lnbase.py | | | 80 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
1 file changed, 48 insertions(+), 32 deletions(-)
diff --git a/lib/lnbase.py b/lib/lnbase.py
@@ -31,7 +31,7 @@ from . import transaction
from .util import PrintError, bh2u, print_error, bfh, profiler
from .transaction import opcodes, Transaction
-from collections import namedtuple
+from collections import namedtuple, defaultdict
LocalCtxArgs = namedtuple("LocalCtxArgs",
["ctn",
"funding_pubkey", "remote_funding_pubkey", "remotepubkey",
@@ -289,14 +289,15 @@ def create_ephemeral_key(privkey):
pub = privkey_to_pubkey(privkey)
return (privkey[:32], pub)
-def get_unused_keys():
- xprv, xpub = bitcoin.bip32_root(b"testseed", "p2wpkh")
- for i in itertools.count():
- childxprv, childxpub = bitcoin.bip32_private_derivation(xprv, "m/", "m/42/"+str(i))
+class KeypairGenerator:
+ def __init__(self, seed):
+ self.xprv, xpub = bitcoin.bip32_root(seed, "p2wpkh")
+ def get(self, keyfamily, index):
+ childxprv, childxpub = bitcoin.bip32_private_derivation(self.xprv, "m/", "m/42'/{keyfamily}'/{index}'".format(keyfamily=keyfamily, index=index))
_, _, _, _, child_c, child_cK = bitcoin.deserialize_xpub(childxpub)
_, _, _, _, _, k = bitcoin.deserialize_xprv(childxprv)
assert len(k) == 32
- yield child_cK, k
+ return child_cK, k
def aiosafe(f):
async def f2(*args, **kwargs):
@@ -326,7 +327,9 @@ def derive_pubkey(basepoint, per_commitment_point):
def derive_privkey(secret, per_commitment_point):
assert type(secret) is int
basepoint = point_to_ser(SECP256k1.generator * secret)
- return secret + bitcoin.string_to_number(bitcoin.sha256(per_commitment_point + basepoint))
+ basepoint = secret + bitcoin.string_to_number(bitcoin.sha256(per_commitment_point + basepoint))
+ basepoint %= SECP256k1.order
+ return basepoint
def derive_blinded_pubkey(basepoint, per_commitment_point):
k1 = ser_to_point(basepoint) * bitcoin.string_to_number(bitcoin.sha256(basepoint + per_commitment_point))
@@ -503,12 +506,12 @@ class Peer(PrintError):
"remote_funding_locked",
"revoke_and_ack",
"commitment_signed"]
- self.channel_accepted = {}
- self.funding_signed = {}
- self.local_funding_locked = {}
- self.remote_funding_locked = {}
- self.revoke_and_ack = {}
- self.commitment_signed = {}
+ self.channel_accepted = defaultdict(asyncio.Future)
+ self.funding_signed = defaultdict(asyncio.Future)
+ self.local_funding_locked = defaultdict(asyncio.Future)
+ self.remote_funding_locked = defaultdict(asyncio.Future)
+ self.revoke_and_ack = defaultdict(asyncio.Future)
+ self.commitment_signed = defaultdict(asyncio.Future)
self.initialized = asyncio.Future()
self.localfeatures = (0x08 if request_initial_sync else 0)
# view of the network
@@ -707,16 +710,27 @@ class Peer(PrintError):
self.writer.close()
@aiosafe
- async def channel_establishment_flow(self, wallet, config, funding_satoshis, push_msat):
+ async def channel_establishment_flow(self, wallet, config, funding_satoshis, push_msat, temp_channel_id, keypair_generator=None):
await self.initialized
- temp_channel_id = os.urandom(32)
- keys = get_unused_keys()
- funding_pubkey, funding_privkey = next(keys)
- revocation_basepoint, revocation_privkey = next(keys)
- htlc_basepoint, htlc_privkey = next(keys)
- delayed_payment_basepoint, delayed_privkey = next(keys)
- base_secret = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
- per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(length=32, byteorder="big")
+
+ if keypair_generator is None:
+ keypair_generator = KeypairGenerator(b"testseed")
+
+ # see lnd/keychain/derivation.go
+ keyfamilymultisig = 0
+ keyfamilyrevocationbase = 1
+ keyfamilyhtlcbase = 2
+ keyfamilypaymentbase = 3
+ keyfamilydelaybase = 4
+ keyfamilyrevocationroot = 5
+ keyfamilynodekey = 6 # TODO currently unused
+
+ funding_pubkey, funding_privkey = keypair_generator.get(keyfamilymultisig, 0)
+ revocation_basepoint, _ = keypair_generator.get(keyfamilyrevocationbase, 0)
+ htlc_basepoint, htlc_privkey = keypair_generator.get(keyfamilyhtlcbase, 0)
+ delayed_payment_basepoint, delayed_privkey = keypair_generator.get(keyfamilydelaybase, 0)
+ base_point, _ = keypair_generator.get(keyfamilypaymentbase, 0)
+ per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(32, "big")
per_commitment_secret_index = 2**48 - 1
# amounts
local_feerate = 20000
@@ -724,7 +738,6 @@ class Peer(PrintError):
to_self_delay = 144
ctn = 0
#
- base_point = secret_to_pubkey(base_secret)
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_secret_first,
@@ -747,7 +760,7 @@ class Peer(PrintError):
to_self_delay=to_self_delay,
max_htlc_value_in_flight_msat=500000 * 1000
)
- self.channel_accepted[temp_channel_id] = asyncio.Future()
+ #self.channel_accepted[temp_channel_id] = asyncio.Future()
self.send_message(msg)
try:
payload = await self.channel_accepted[temp_channel_id]
@@ -808,7 +821,7 @@ class Peer(PrintError):
funding_txid_bytes = bytes.fromhex(funding_txid)[::-1]
channel_id = int.from_bytes(funding_txid_bytes, byteorder="big") ^ funding_index
self.send_message(gen_msg("funding_created", temporary_channel_id=temp_channel_id, funding_txid=funding_txid_bytes, funding_output_index=funding_index, signature=sig_64))
- self.funding_signed[channel_id] = asyncio.Future()
+ #self.funding_signed[channel_id] = asyncio.Future()
try:
payload = await self.funding_signed[channel_id]
finally:
@@ -828,8 +841,8 @@ class Peer(PrintError):
if not bitcoin.verify_signature(remote_funding_pubkey, remote_sig, pre_hash):
raise Exception('verifying remote signature failed.')
# broadcast funding tx
- self.local_funding_locked[channel_id] = asyncio.Future()
- self.remote_funding_locked[channel_id] = asyncio.Future()
+ #self.local_funding_locked[channel_id] = asyncio.Future()
+ #self.remote_funding_locked[channel_id] = asyncio.Future()
success, _txid = self.network.broadcast(funding_tx)
assert success, success
# wait until we see confirmations
@@ -862,7 +875,7 @@ class Peer(PrintError):
finally:
del self.remote_funding_locked[channel_id]
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):
@@ -919,7 +932,8 @@ class Peer(PrintError):
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_remote_htlc_pubkey = derive_pubkey(local_htlc_basepoint, remote_next_commitment_point)
- their_remote_htlc_privkey = derive_privkey(int.from_bytes(htlc_privkey, "big"), remote_next_commitment_point).to_bytes(32, "big")
+ their_remote_htlc_privkey_number = derive_privkey(int.from_bytes(htlc_privkey, "big"), remote_next_commitment_point)
+ their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, "big")
# TODO check payment_hash
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)
@@ -960,7 +974,7 @@ class Peer(PrintError):
r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
htlc_sig = sigencode_string_canonize(r, s, SECP256k1.generator.order())
- self.revoke_and_ack[channel_id] = asyncio.Future()
+ #self.revoke_and_ack[channel_id] = asyncio.Future()
self.send_message(gen_msg("commitment_signed", channel_id=channel_id, signature=sig_64, num_htlcs=1, htlc_signature=htlc_sig))
try:
@@ -992,8 +1006,8 @@ class Peer(PrintError):
r, s = sigdecode_der(sig[:-1], SECP256k1.generator.order())
sig_64 = sigencode_string_canonize(r, s, SECP256k1.generator.order())
- self.revoke_and_ack[channel_id] = asyncio.Future()
- self.commitment_signed[channel_id] = asyncio.Future() # we will also receive a new commitment transaction shortly after sending ours
+ #self.revoke_and_ack[channel_id] = asyncio.Future()
+ #self.commitment_signed[channel_id] = asyncio.Future() # we will also receive a new commitment transaction shortly after sending ours
self.send_message(gen_msg("commitment_signed", channel_id=channel_id, signature=sig_64, num_htlcs=0))
try:
revoke_and_ack_msg = await self.revoke_and_ack[channel_id]
@@ -1007,6 +1021,8 @@ class Peer(PrintError):
finally:
del self.commitment_signed[channel_id]
+ # TODO check commitment_signed results
+
local_last_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index - 1)
local_next_per_commitment_secret = get_per_commitment_secret_from_seed(local_per_commitment_secret_seed, local_last_pcs_index - 2)