tlnutil.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tlnutil.py (57914B)
---
1 # Copyright (C) 2018 The Electrum developers
2 # Distributed under the MIT software license, see the accompanying
3 # file LICENCE or http://www.opensource.org/licenses/mit-license.php
4
5 from enum import IntFlag, IntEnum
6 import enum
7 import json
8 from collections import namedtuple, defaultdict
9 from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
10 import re
11 import time
12 import attr
13 from aiorpcx import NetAddress
14
15 from .util import bfh, bh2u, inv_dict, UserFacingException
16 from .util import list_enabled_bits
17 from .crypto import sha256
18 from .transaction import (Transaction, PartialTransaction, PartialTxInput, TxOutpoint,
19 PartialTxOutput, opcodes, TxOutput)
20 from .ecc import CURVE_ORDER, sig_string_from_der_sig, ECPubkey, string_to_number
21 from . import ecc, bitcoin, crypto, transaction
22 from .bitcoin import (push_script, redeem_script_to_address, address_to_script,
23 construct_witness, construct_script)
24 from . import segwit_addr
25 from .i18n import _
26 from .lnaddr import lndecode
27 from .bip32 import BIP32Node, BIP32_PRIME
28 from .transaction import BCDataStream
29
30 if TYPE_CHECKING:
31 from .lnchannel import Channel, AbstractChannel
32 from .lnrouter import LNPaymentRoute
33 from .lnonion import OnionRoutingFailureMessage
34
35
36 # defined in BOLT-03:
37 HTLC_TIMEOUT_WEIGHT = 663
38 HTLC_SUCCESS_WEIGHT = 703
39 COMMITMENT_TX_WEIGHT = 724
40 HTLC_OUTPUT_WEIGHT = 172
41
42 LN_MAX_FUNDING_SAT = pow(2, 24) - 1
43 LN_MAX_HTLC_VALUE_MSAT = pow(2, 32) - 1
44
45 # dummy address for fee estimation of funding tx
46 def ln_dummy_address():
47 return redeem_script_to_address('p2wsh', '')
48
49 from .json_db import StoredObject
50
51
52 def channel_id_from_funding_tx(funding_txid: str, funding_index: int) -> Tuple[bytes, bytes]:
53 funding_txid_bytes = bytes.fromhex(funding_txid)[::-1]
54 i = int.from_bytes(funding_txid_bytes, 'big') ^ funding_index
55 return i.to_bytes(32, 'big'), funding_txid_bytes
56
57 hex_to_bytes = lambda v: v if isinstance(v, bytes) else bytes.fromhex(v) if v is not None else None
58 json_to_keypair = lambda v: v if isinstance(v, OnlyPubkeyKeypair) else Keypair(**v) if len(v)==2 else OnlyPubkeyKeypair(**v)
59
60
61 @attr.s
62 class OnlyPubkeyKeypair(StoredObject):
63 pubkey = attr.ib(type=bytes, converter=hex_to_bytes)
64
65 @attr.s
66 class Keypair(OnlyPubkeyKeypair):
67 privkey = attr.ib(type=bytes, converter=hex_to_bytes)
68
69 @attr.s
70 class Config(StoredObject):
71 # shared channel config fields
72 payment_basepoint = attr.ib(type=OnlyPubkeyKeypair, converter=json_to_keypair)
73 multisig_key = attr.ib(type=OnlyPubkeyKeypair, converter=json_to_keypair)
74 htlc_basepoint = attr.ib(type=OnlyPubkeyKeypair, converter=json_to_keypair)
75 delayed_basepoint = attr.ib(type=OnlyPubkeyKeypair, converter=json_to_keypair)
76 revocation_basepoint = attr.ib(type=OnlyPubkeyKeypair, converter=json_to_keypair)
77 to_self_delay = attr.ib(type=int) # applies to OTHER ctx
78 dust_limit_sat = attr.ib(type=int) # applies to SAME ctx
79 max_htlc_value_in_flight_msat = attr.ib(type=int) # max val of INCOMING htlcs
80 max_accepted_htlcs = attr.ib(type=int) # max num of INCOMING htlcs
81 initial_msat = attr.ib(type=int)
82 reserve_sat = attr.ib(type=int) # applies to OTHER ctx
83 htlc_minimum_msat = attr.ib(type=int) # smallest value for INCOMING htlc
84 upfront_shutdown_script = attr.ib(type=bytes, converter=hex_to_bytes)
85
86 def validate_params(self, *, funding_sat: int) -> None:
87 conf_name = type(self).__name__
88 for key in (
89 self.payment_basepoint,
90 self.multisig_key,
91 self.htlc_basepoint,
92 self.delayed_basepoint,
93 self.revocation_basepoint
94 ):
95 if not (len(key.pubkey) == 33 and ecc.ECPubkey.is_pubkey_bytes(key.pubkey)):
96 raise Exception(f"{conf_name}. invalid pubkey in channel config")
97 if self.reserve_sat < self.dust_limit_sat:
98 raise Exception(f"{conf_name}. MUST set channel_reserve_satoshis greater than or equal to dust_limit_satoshis")
99 # technically this could be using the lower DUST_LIMIT_DEFAULT_SAT_SEGWIT
100 # but other implementations are checking against this value too; also let's be conservative
101 if self.dust_limit_sat < bitcoin.DUST_LIMIT_DEFAULT_SAT_LEGACY:
102 raise Exception(f"{conf_name}. dust limit too low: {self.dust_limit_sat} sat")
103 if self.reserve_sat > funding_sat // 100:
104 raise Exception(f"{conf_name}. reserve too high: {self.reserve_sat}, funding_sat: {funding_sat}")
105 if self.htlc_minimum_msat > 1_000:
106 raise Exception(f"{conf_name}. htlc_minimum_msat too high: {self.htlc_minimum_msat} msat")
107 HTLC_MINIMUM_MSAT_MIN = 0 # should be at least 1 really, but apparently some nodes are sending zero...
108 if self.htlc_minimum_msat < HTLC_MINIMUM_MSAT_MIN:
109 raise Exception(f"{conf_name}. htlc_minimum_msat too low: {self.htlc_minimum_msat} msat < {HTLC_MINIMUM_MSAT_MIN}")
110 if self.max_accepted_htlcs < 1:
111 raise Exception(f"{conf_name}. max_accepted_htlcs too low: {self.max_accepted_htlcs}")
112 if self.max_accepted_htlcs > 483:
113 raise Exception(f"{conf_name}. max_accepted_htlcs too high: {self.max_accepted_htlcs}")
114 if self.to_self_delay > MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED:
115 raise Exception(f"{conf_name}. to_self_delay too high: {self.to_self_delay} > {MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED}")
116 if self.max_htlc_value_in_flight_msat < min(1000 * funding_sat, 100_000_000):
117 raise Exception(f"{conf_name}. max_htlc_value_in_flight_msat is too small: {self.max_htlc_value_in_flight_msat}")
118
119
120 @attr.s
121 class LocalConfig(Config):
122 channel_seed = attr.ib(type=bytes, converter=hex_to_bytes) # type: Optional[bytes]
123 funding_locked_received = attr.ib(type=bool)
124 was_announced = attr.ib(type=bool)
125 current_commitment_signature = attr.ib(type=bytes, converter=hex_to_bytes)
126 current_htlc_signatures = attr.ib(type=bytes, converter=hex_to_bytes)
127 per_commitment_secret_seed = attr.ib(type=bytes, converter=hex_to_bytes)
128
129 @classmethod
130 def from_seed(self, **kwargs):
131 channel_seed = kwargs['channel_seed']
132 static_remotekey = kwargs.pop('static_remotekey')
133 node = BIP32Node.from_rootseed(channel_seed, xtype='standard')
134 keypair_generator = lambda family: generate_keypair(node, family)
135 kwargs['per_commitment_secret_seed'] = keypair_generator(LnKeyFamily.REVOCATION_ROOT).privkey
136 kwargs['multisig_key'] = keypair_generator(LnKeyFamily.MULTISIG)
137 kwargs['htlc_basepoint'] = keypair_generator(LnKeyFamily.HTLC_BASE)
138 kwargs['delayed_basepoint'] = keypair_generator(LnKeyFamily.DELAY_BASE)
139 kwargs['revocation_basepoint'] = keypair_generator(LnKeyFamily.REVOCATION_BASE)
140 kwargs['payment_basepoint'] = OnlyPubkeyKeypair(static_remotekey) if static_remotekey else keypair_generator(LnKeyFamily.PAYMENT_BASE)
141 return LocalConfig(**kwargs)
142
143 def validate_params(self, *, funding_sat: int) -> None:
144 conf_name = type(self).__name__
145 # run base checks regardless whether LOCAL/REMOTE config
146 super().validate_params(funding_sat=funding_sat)
147 # run some stricter checks on LOCAL config (make sure we ourselves do the sane thing,
148 # even if we are lenient with REMOTE for compatibility reasons)
149 HTLC_MINIMUM_MSAT_MIN = 1
150 if self.htlc_minimum_msat < HTLC_MINIMUM_MSAT_MIN:
151 raise Exception(f"{conf_name}. htlc_minimum_msat too low: {self.htlc_minimum_msat} msat < {HTLC_MINIMUM_MSAT_MIN}")
152
153 @attr.s
154 class RemoteConfig(Config):
155 next_per_commitment_point = attr.ib(type=bytes, converter=hex_to_bytes)
156 current_per_commitment_point = attr.ib(default=None, type=bytes, converter=hex_to_bytes)
157
158 @attr.s
159 class FeeUpdate(StoredObject):
160 rate = attr.ib(type=int) # in sat/kw
161 ctn_local = attr.ib(default=None, type=int)
162 ctn_remote = attr.ib(default=None, type=int)
163
164 @attr.s
165 class ChannelConstraints(StoredObject):
166 capacity = attr.ib(type=int) # in sat
167 is_initiator = attr.ib(type=bool) # note: sometimes also called "funder"
168 funding_txn_minimum_depth = attr.ib(type=int)
169
170
171 CHANNEL_BACKUP_VERSION = 0
172 @attr.s
173 class ChannelBackupStorage(StoredObject):
174 node_id = attr.ib(type=bytes, converter=hex_to_bytes)
175 privkey = attr.ib(type=bytes, converter=hex_to_bytes)
176 funding_txid = attr.ib(type=str)
177 funding_index = attr.ib(type=int, converter=int)
178 funding_address = attr.ib(type=str)
179 host = attr.ib(type=str)
180 port = attr.ib(type=int, converter=int)
181 is_initiator = attr.ib(type=bool)
182 channel_seed = attr.ib(type=bytes, converter=hex_to_bytes)
183 local_delay = attr.ib(type=int, converter=int)
184 remote_delay = attr.ib(type=int, converter=int)
185 remote_payment_pubkey = attr.ib(type=bytes, converter=hex_to_bytes)
186 remote_revocation_pubkey = attr.ib(type=bytes, converter=hex_to_bytes)
187
188 def funding_outpoint(self):
189 return Outpoint(self.funding_txid, self.funding_index)
190
191 def channel_id(self):
192 chan_id, _ = channel_id_from_funding_tx(self.funding_txid, self.funding_index)
193 return chan_id
194
195 def to_bytes(self) -> bytes:
196 vds = BCDataStream()
197 vds.write_int16(CHANNEL_BACKUP_VERSION)
198 vds.write_boolean(self.is_initiator)
199 vds.write_bytes(self.privkey, 32)
200 vds.write_bytes(self.channel_seed, 32)
201 vds.write_bytes(self.node_id, 33)
202 vds.write_bytes(bfh(self.funding_txid), 32)
203 vds.write_int16(self.funding_index)
204 vds.write_string(self.funding_address)
205 vds.write_bytes(self.remote_payment_pubkey, 33)
206 vds.write_bytes(self.remote_revocation_pubkey, 33)
207 vds.write_int16(self.local_delay)
208 vds.write_int16(self.remote_delay)
209 vds.write_string(self.host)
210 vds.write_int16(self.port)
211 return bytes(vds.input)
212
213 @staticmethod
214 def from_bytes(s):
215 vds = BCDataStream()
216 vds.write(s)
217 version = vds.read_int16()
218 if version != CHANNEL_BACKUP_VERSION:
219 raise Exception(f"unknown version for channel backup: {version}")
220 return ChannelBackupStorage(
221 is_initiator = vds.read_boolean(),
222 privkey = vds.read_bytes(32).hex(),
223 channel_seed = vds.read_bytes(32).hex(),
224 node_id = vds.read_bytes(33).hex(),
225 funding_txid = vds.read_bytes(32).hex(),
226 funding_index = vds.read_int16(),
227 funding_address = vds.read_string(),
228 remote_payment_pubkey = vds.read_bytes(33).hex(),
229 remote_revocation_pubkey = vds.read_bytes(33).hex(),
230 local_delay = vds.read_int16(),
231 remote_delay = vds.read_int16(),
232 host = vds.read_string(),
233 port = vds.read_int16())
234
235
236
237 class ScriptHtlc(NamedTuple):
238 redeem_script: bytes
239 htlc: 'UpdateAddHtlc'
240
241
242 # FIXME duplicate of TxOutpoint in transaction.py??
243 @attr.s
244 class Outpoint(StoredObject):
245 txid = attr.ib(type=str)
246 output_index = attr.ib(type=int)
247
248 def to_str(self):
249 return "{}:{}".format(self.txid, self.output_index)
250
251
252 class HtlcLog(NamedTuple):
253 success: bool
254 amount_msat: int # amount for receiver (e.g. from invoice)
255 route: Optional['LNPaymentRoute'] = None
256 preimage: Optional[bytes] = None
257 error_bytes: Optional[bytes] = None
258 failure_msg: Optional['OnionRoutingFailureMessage'] = None
259 sender_idx: Optional[int] = None
260
261 def formatted_tuple(self):
262 route = self.route
263 route_str = '%d'%len(route)
264 short_channel_id = None
265 if not self.success:
266 sender_idx = self.sender_idx
267 failure_msg = self.failure_msg
268 if sender_idx is not None:
269 try:
270 short_channel_id = route[sender_idx + 1].short_channel_id
271 except IndexError:
272 # payment destination reported error
273 short_channel_id = _("Destination node")
274 message = failure_msg.code_name()
275 else:
276 short_channel_id = route[-1].short_channel_id
277 message = _('Success')
278 chan_str = str(short_channel_id) if short_channel_id else _("Unknown")
279 return route_str, chan_str, message
280
281
282 class LightningError(Exception): pass
283 class LightningPeerConnectionClosed(LightningError): pass
284 class UnableToDeriveSecret(LightningError): pass
285 class HandshakeFailed(LightningError): pass
286 class ConnStringFormatError(LightningError): pass
287 class RemoteMisbehaving(LightningError): pass
288 class UpfrontShutdownScriptViolation(RemoteMisbehaving): pass
289
290 class NotFoundChanAnnouncementForUpdate(Exception): pass
291
292 class PaymentFailure(UserFacingException): pass
293 class NoPathFound(PaymentFailure):
294 def __str__(self):
295 return _('No path found')
296
297 # TODO make some of these values configurable?
298 REDEEM_AFTER_DOUBLE_SPENT_DELAY = 30
299
300 CHANNEL_OPENING_TIMEOUT = 24*60*60
301
302 MIN_FUNDING_SAT = 200_000
303
304 ##### CLTV-expiry-delta-related values
305 # see https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#cltv_expiry_delta-selection
306
307 # the minimum cltv_expiry accepted for newly received HTLCs
308 # note: when changing, consider Blockchain.is_tip_stale()
309 MIN_FINAL_CLTV_EXPIRY_ACCEPTED = 144
310 # set it a tiny bit higher for invoices as blocks could get mined
311 # during forward path of payment
312 MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE = MIN_FINAL_CLTV_EXPIRY_ACCEPTED + 3
313
314 # the deadline for offered HTLCs:
315 # the deadline after which the channel has to be failed and timed out on-chain
316 NBLOCK_DEADLINE_AFTER_EXPIRY_FOR_OFFERED_HTLCS = 1
317
318 # the deadline for received HTLCs this node has fulfilled:
319 # the deadline after which the channel has to be failed and the HTLC fulfilled on-chain before its cltv_expiry
320 NBLOCK_DEADLINE_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS = 72
321
322 NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE = 28 * 144
323
324 MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED = 2016
325
326 class RevocationStore:
327 # closely based on code in lightningnetwork/lnd
328
329 START_INDEX = 2 ** 48 - 1
330
331 def __init__(self, storage):
332 if len(storage) == 0:
333 storage['index'] = self.START_INDEX
334 storage['buckets'] = {}
335 self.storage = storage
336 self.buckets = storage['buckets']
337
338 def add_next_entry(self, hsh):
339 index = self.storage['index']
340 new_element = ShachainElement(index=index, secret=hsh)
341 bucket = count_trailing_zeros(index)
342 for i in range(0, bucket):
343 this_bucket = self.buckets[i]
344 e = shachain_derive(new_element, this_bucket.index)
345 if e != this_bucket:
346 raise Exception("hash is not derivable: {} {} {}".format(bh2u(e.secret), bh2u(this_bucket.secret), this_bucket.index))
347 self.buckets[bucket] = new_element
348 self.storage['index'] = index - 1
349
350 def retrieve_secret(self, index: int) -> bytes:
351 assert index <= self.START_INDEX, index
352 for i in range(0, 49):
353 bucket = self.buckets.get(i)
354 if bucket is None:
355 raise UnableToDeriveSecret()
356 try:
357 element = shachain_derive(bucket, index)
358 except UnableToDeriveSecret:
359 continue
360 return element.secret
361 raise UnableToDeriveSecret()
362
363 def __eq__(self, o):
364 return type(o) is RevocationStore and self.serialize() == o.serialize()
365
366 def __hash__(self):
367 return hash(json.dumps(self.serialize(), sort_keys=True))
368
369
370 def count_trailing_zeros(index):
371 """ BOLT-03 (where_to_put_secret) """
372 try:
373 return list(reversed(bin(index)[2:])).index("1")
374 except ValueError:
375 return 48
376
377 def shachain_derive(element, to_index):
378 def get_prefix(index, pos):
379 mask = (1 << 64) - 1 - ((1 << pos) - 1)
380 return index & mask
381 from_index = element.index
382 zeros = count_trailing_zeros(from_index)
383 if from_index != get_prefix(to_index, zeros):
384 raise UnableToDeriveSecret("prefixes are different; index not derivable")
385 return ShachainElement(
386 get_per_commitment_secret_from_seed(element.secret, to_index, zeros),
387 to_index)
388
389 ShachainElement = namedtuple("ShachainElement", ["secret", "index"])
390 ShachainElement.__str__ = lambda self: "ShachainElement(" + bh2u(self.secret) + "," + str(self.index) + ")"
391
392 def get_per_commitment_secret_from_seed(seed: bytes, i: int, bits: int = 48) -> bytes:
393 """Generate per commitment secret."""
394 per_commitment_secret = bytearray(seed)
395 for bitindex in range(bits - 1, -1, -1):
396 mask = 1 << bitindex
397 if i & mask:
398 per_commitment_secret[bitindex // 8] ^= 1 << (bitindex % 8)
399 per_commitment_secret = bytearray(sha256(per_commitment_secret))
400 bajts = bytes(per_commitment_secret)
401 return bajts
402
403 def secret_to_pubkey(secret: int) -> bytes:
404 assert type(secret) is int
405 return ecc.ECPrivkey.from_secret_scalar(secret).get_public_key_bytes(compressed=True)
406
407 def privkey_to_pubkey(priv: bytes) -> bytes:
408 return ecc.ECPrivkey(priv[:32]).get_public_key_bytes()
409
410 def derive_pubkey(basepoint: bytes, per_commitment_point: bytes) -> bytes:
411 p = ecc.ECPubkey(basepoint) + ecc.GENERATOR * ecc.string_to_number(sha256(per_commitment_point + basepoint))
412 return p.get_public_key_bytes()
413
414 def derive_privkey(secret: int, per_commitment_point: bytes) -> int:
415 assert type(secret) is int
416 basepoint_bytes = secret_to_pubkey(secret)
417 basepoint = secret + ecc.string_to_number(sha256(per_commitment_point + basepoint_bytes))
418 basepoint %= CURVE_ORDER
419 return basepoint
420
421 def derive_blinded_pubkey(basepoint: bytes, per_commitment_point: bytes) -> bytes:
422 k1 = ecc.ECPubkey(basepoint) * ecc.string_to_number(sha256(basepoint + per_commitment_point))
423 k2 = ecc.ECPubkey(per_commitment_point) * ecc.string_to_number(sha256(per_commitment_point + basepoint))
424 return (k1 + k2).get_public_key_bytes()
425
426 def derive_blinded_privkey(basepoint_secret: bytes, per_commitment_secret: bytes) -> bytes:
427 basepoint = ecc.ECPrivkey(basepoint_secret).get_public_key_bytes(compressed=True)
428 per_commitment_point = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
429 k1 = ecc.string_to_number(basepoint_secret) * ecc.string_to_number(sha256(basepoint + per_commitment_point))
430 k2 = ecc.string_to_number(per_commitment_secret) * ecc.string_to_number(sha256(per_commitment_point + basepoint))
431 sum = (k1 + k2) % ecc.CURVE_ORDER
432 return int.to_bytes(sum, length=32, byteorder='big', signed=False)
433
434
435 def make_htlc_tx_output(amount_msat, local_feerate, revocationpubkey, local_delayedpubkey, success, to_self_delay):
436 assert type(amount_msat) is int
437 assert type(local_feerate) is int
438 assert type(revocationpubkey) is bytes
439 assert type(local_delayedpubkey) is bytes
440 script = bfh(construct_script([
441 opcodes.OP_IF,
442 revocationpubkey,
443 opcodes.OP_ELSE,
444 to_self_delay,
445 opcodes.OP_CHECKSEQUENCEVERIFY,
446 opcodes.OP_DROP,
447 local_delayedpubkey,
448 opcodes.OP_ENDIF,
449 opcodes.OP_CHECKSIG,
450 ]))
451
452 p2wsh = bitcoin.redeem_script_to_address('p2wsh', bh2u(script))
453 weight = HTLC_SUCCESS_WEIGHT if success else HTLC_TIMEOUT_WEIGHT
454 fee = local_feerate * weight
455 fee = fee // 1000 * 1000
456 final_amount_sat = (amount_msat - fee) // 1000
457 assert final_amount_sat > 0, final_amount_sat
458 output = PartialTxOutput.from_address_and_value(p2wsh, final_amount_sat)
459 return script, output
460
461 def make_htlc_tx_witness(remotehtlcsig: bytes, localhtlcsig: bytes,
462 payment_preimage: bytes, witness_script: bytes) -> bytes:
463 assert type(remotehtlcsig) is bytes
464 assert type(localhtlcsig) is bytes
465 assert type(payment_preimage) is bytes
466 assert type(witness_script) is bytes
467 return bfh(construct_witness([0, remotehtlcsig, localhtlcsig, payment_preimage, witness_script]))
468
469 def make_htlc_tx_inputs(htlc_output_txid: str, htlc_output_index: int,
470 amount_msat: int, witness_script: str) -> List[PartialTxInput]:
471 assert type(htlc_output_txid) is str
472 assert type(htlc_output_index) is int
473 assert type(amount_msat) is int
474 assert type(witness_script) is str
475 txin = PartialTxInput(prevout=TxOutpoint(txid=bfh(htlc_output_txid), out_idx=htlc_output_index),
476 nsequence=0)
477 txin.witness_script = bfh(witness_script)
478 txin.script_sig = b''
479 txin._trusted_value_sats = amount_msat // 1000
480 c_inputs = [txin]
481 return c_inputs
482
483 def make_htlc_tx(*, cltv_expiry: int, inputs: List[PartialTxInput], output: PartialTxOutput) -> PartialTransaction:
484 assert type(cltv_expiry) is int
485 c_outputs = [output]
486 tx = PartialTransaction.from_io(inputs, c_outputs, locktime=cltv_expiry, version=2)
487 return tx
488
489 def make_offered_htlc(revocation_pubkey: bytes, remote_htlcpubkey: bytes,
490 local_htlcpubkey: bytes, payment_hash: bytes) -> bytes:
491 assert type(revocation_pubkey) is bytes
492 assert type(remote_htlcpubkey) is bytes
493 assert type(local_htlcpubkey) is bytes
494 assert type(payment_hash) is bytes
495 script = bfh(construct_script([
496 opcodes.OP_DUP,
497 opcodes.OP_HASH160,
498 bitcoin.hash_160(revocation_pubkey),
499 opcodes.OP_EQUAL,
500 opcodes.OP_IF,
501 opcodes.OP_CHECKSIG,
502 opcodes.OP_ELSE,
503 remote_htlcpubkey,
504 opcodes.OP_SWAP,
505 opcodes.OP_SIZE,
506 32,
507 opcodes.OP_EQUAL,
508 opcodes.OP_NOTIF,
509 opcodes.OP_DROP,
510 2,
511 opcodes.OP_SWAP,
512 local_htlcpubkey,
513 2,
514 opcodes.OP_CHECKMULTISIG,
515 opcodes.OP_ELSE,
516 opcodes.OP_HASH160,
517 crypto.ripemd(payment_hash),
518 opcodes.OP_EQUALVERIFY,
519 opcodes.OP_CHECKSIG,
520 opcodes.OP_ENDIF,
521 opcodes.OP_ENDIF,
522 ]))
523 return script
524
525 def make_received_htlc(revocation_pubkey: bytes, remote_htlcpubkey: bytes,
526 local_htlcpubkey: bytes, payment_hash: bytes, cltv_expiry: int) -> bytes:
527 for i in [revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, payment_hash]:
528 assert type(i) is bytes
529 assert type(cltv_expiry) is int
530
531 script = bfh(construct_script([
532 opcodes.OP_DUP,
533 opcodes.OP_HASH160,
534 bitcoin.hash_160(revocation_pubkey),
535 opcodes.OP_EQUAL,
536 opcodes.OP_IF,
537 opcodes.OP_CHECKSIG,
538 opcodes.OP_ELSE,
539 remote_htlcpubkey,
540 opcodes.OP_SWAP,
541 opcodes.OP_SIZE,
542 32,
543 opcodes.OP_EQUAL,
544 opcodes.OP_IF,
545 opcodes.OP_HASH160,
546 crypto.ripemd(payment_hash),
547 opcodes.OP_EQUALVERIFY,
548 2,
549 opcodes.OP_SWAP,
550 local_htlcpubkey,
551 2,
552 opcodes.OP_CHECKMULTISIG,
553 opcodes.OP_ELSE,
554 opcodes.OP_DROP,
555 cltv_expiry,
556 opcodes.OP_CHECKLOCKTIMEVERIFY,
557 opcodes.OP_DROP,
558 opcodes.OP_CHECKSIG,
559 opcodes.OP_ENDIF,
560 opcodes.OP_ENDIF,
561 ]))
562 return script
563
564 def make_htlc_output_witness_script(is_received_htlc: bool, remote_revocation_pubkey: bytes, remote_htlc_pubkey: bytes,
565 local_htlc_pubkey: bytes, payment_hash: bytes, cltv_expiry: Optional[int]) -> bytes:
566 if is_received_htlc:
567 return make_received_htlc(revocation_pubkey=remote_revocation_pubkey,
568 remote_htlcpubkey=remote_htlc_pubkey,
569 local_htlcpubkey=local_htlc_pubkey,
570 payment_hash=payment_hash,
571 cltv_expiry=cltv_expiry)
572 else:
573 return make_offered_htlc(revocation_pubkey=remote_revocation_pubkey,
574 remote_htlcpubkey=remote_htlc_pubkey,
575 local_htlcpubkey=local_htlc_pubkey,
576 payment_hash=payment_hash)
577
578
579 def get_ordered_channel_configs(chan: 'AbstractChannel', for_us: bool) -> Tuple[Union[LocalConfig, RemoteConfig],
580 Union[LocalConfig, RemoteConfig]]:
581 conf = chan.config[LOCAL] if for_us else chan.config[REMOTE]
582 other_conf = chan.config[LOCAL] if not for_us else chan.config[REMOTE]
583 return conf, other_conf
584
585
586 def possible_output_idxs_of_htlc_in_ctx(*, chan: 'Channel', pcp: bytes, subject: 'HTLCOwner',
587 htlc_direction: 'Direction', ctx: Transaction,
588 htlc: 'UpdateAddHtlc') -> Set[int]:
589 amount_msat, cltv_expiry, payment_hash = htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash
590 for_us = subject == LOCAL
591 conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=for_us)
592
593 other_revocation_pubkey = derive_blinded_pubkey(other_conf.revocation_basepoint.pubkey, pcp)
594 other_htlc_pubkey = derive_pubkey(other_conf.htlc_basepoint.pubkey, pcp)
595 htlc_pubkey = derive_pubkey(conf.htlc_basepoint.pubkey, pcp)
596 preimage_script = make_htlc_output_witness_script(is_received_htlc=htlc_direction == RECEIVED,
597 remote_revocation_pubkey=other_revocation_pubkey,
598 remote_htlc_pubkey=other_htlc_pubkey,
599 local_htlc_pubkey=htlc_pubkey,
600 payment_hash=payment_hash,
601 cltv_expiry=cltv_expiry)
602 htlc_address = redeem_script_to_address('p2wsh', bh2u(preimage_script))
603 candidates = ctx.get_output_idxs_from_address(htlc_address)
604 return {output_idx for output_idx in candidates
605 if ctx.outputs()[output_idx].value == htlc.amount_msat // 1000}
606
607
608 def map_htlcs_to_ctx_output_idxs(*, chan: 'Channel', ctx: Transaction, pcp: bytes,
609 subject: 'HTLCOwner', ctn: int) -> Dict[Tuple['Direction', 'UpdateAddHtlc'], Tuple[int, int]]:
610 """Returns a dict from (htlc_dir, htlc) to (ctx_output_idx, htlc_relative_idx)"""
611 htlc_to_ctx_output_idx_map = {} # type: Dict[Tuple[Direction, UpdateAddHtlc], int]
612 unclaimed_ctx_output_idxs = set(range(len(ctx.outputs())))
613 offered_htlcs = chan.included_htlcs(subject, SENT, ctn=ctn)
614 offered_htlcs.sort(key=lambda htlc: htlc.cltv_expiry)
615 received_htlcs = chan.included_htlcs(subject, RECEIVED, ctn=ctn)
616 received_htlcs.sort(key=lambda htlc: htlc.cltv_expiry)
617 for direction, htlcs in zip([SENT, RECEIVED], [offered_htlcs, received_htlcs]):
618 for htlc in htlcs:
619 cands = sorted(possible_output_idxs_of_htlc_in_ctx(chan=chan,
620 pcp=pcp,
621 subject=subject,
622 htlc_direction=direction,
623 ctx=ctx,
624 htlc=htlc))
625 for ctx_output_idx in cands:
626 if ctx_output_idx in unclaimed_ctx_output_idxs:
627 unclaimed_ctx_output_idxs.discard(ctx_output_idx)
628 htlc_to_ctx_output_idx_map[(direction, htlc)] = ctx_output_idx
629 break
630 # calc htlc_relative_idx
631 inverse_map = {ctx_output_idx: (direction, htlc)
632 for ((direction, htlc), ctx_output_idx) in htlc_to_ctx_output_idx_map.items()}
633
634 return {inverse_map[ctx_output_idx]: (ctx_output_idx, htlc_relative_idx)
635 for htlc_relative_idx, ctx_output_idx in enumerate(sorted(inverse_map))}
636
637
638 def make_htlc_tx_with_open_channel(*, chan: 'Channel', pcp: bytes, subject: 'HTLCOwner', ctn: int,
639 htlc_direction: 'Direction', commit: Transaction, ctx_output_idx: int,
640 htlc: 'UpdateAddHtlc', name: str = None) -> Tuple[bytes, PartialTransaction]:
641 amount_msat, cltv_expiry, payment_hash = htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash
642 for_us = subject == LOCAL
643 conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=for_us)
644
645 delayedpubkey = derive_pubkey(conf.delayed_basepoint.pubkey, pcp)
646 other_revocation_pubkey = derive_blinded_pubkey(other_conf.revocation_basepoint.pubkey, pcp)
647 other_htlc_pubkey = derive_pubkey(other_conf.htlc_basepoint.pubkey, pcp)
648 htlc_pubkey = derive_pubkey(conf.htlc_basepoint.pubkey, pcp)
649 # HTLC-success for the HTLC spending from a received HTLC output
650 # if we do not receive, and the commitment tx is not for us, they receive, so it is also an HTLC-success
651 is_htlc_success = htlc_direction == RECEIVED
652 witness_script_of_htlc_tx_output, htlc_tx_output = make_htlc_tx_output(
653 amount_msat = amount_msat,
654 local_feerate = chan.get_feerate(subject, ctn=ctn),
655 revocationpubkey=other_revocation_pubkey,
656 local_delayedpubkey=delayedpubkey,
657 success = is_htlc_success,
658 to_self_delay = other_conf.to_self_delay)
659 preimage_script = make_htlc_output_witness_script(is_received_htlc=is_htlc_success,
660 remote_revocation_pubkey=other_revocation_pubkey,
661 remote_htlc_pubkey=other_htlc_pubkey,
662 local_htlc_pubkey=htlc_pubkey,
663 payment_hash=payment_hash,
664 cltv_expiry=cltv_expiry)
665 htlc_tx_inputs = make_htlc_tx_inputs(
666 commit.txid(), ctx_output_idx,
667 amount_msat=amount_msat,
668 witness_script=bh2u(preimage_script))
669 if is_htlc_success:
670 cltv_expiry = 0
671 htlc_tx = make_htlc_tx(cltv_expiry=cltv_expiry, inputs=htlc_tx_inputs, output=htlc_tx_output)
672 return witness_script_of_htlc_tx_output, htlc_tx
673
674 def make_funding_input(local_funding_pubkey: bytes, remote_funding_pubkey: bytes,
675 funding_pos: int, funding_txid: str, funding_sat: int) -> PartialTxInput:
676 pubkeys = sorted([bh2u(local_funding_pubkey), bh2u(remote_funding_pubkey)])
677 # commitment tx input
678 prevout = TxOutpoint(txid=bfh(funding_txid), out_idx=funding_pos)
679 c_input = PartialTxInput(prevout=prevout)
680 c_input.script_type = 'p2wsh'
681 c_input.pubkeys = [bfh(pk) for pk in pubkeys]
682 c_input.num_sig = 2
683 c_input._trusted_value_sats = funding_sat
684 return c_input
685
686 class HTLCOwner(IntFlag):
687 LOCAL = 1
688 REMOTE = -LOCAL
689
690 def inverted(self) -> 'HTLCOwner':
691 return -self
692
693 def __neg__(self) -> 'HTLCOwner':
694 return HTLCOwner(super().__neg__())
695
696
697 class Direction(IntFlag):
698 SENT = -1 # in the context of HTLCs: "offered" HTLCs
699 RECEIVED = 1 # in the context of HTLCs: "received" HTLCs
700
701 SENT = Direction.SENT
702 RECEIVED = Direction.RECEIVED
703
704 LOCAL = HTLCOwner.LOCAL
705 REMOTE = HTLCOwner.REMOTE
706
707 def make_commitment_outputs(*, fees_per_participant: Mapping[HTLCOwner, int], local_amount_msat: int, remote_amount_msat: int,
708 local_script: str, remote_script: str, htlcs: List[ScriptHtlc], dust_limit_sat: int) -> Tuple[List[PartialTxOutput], List[PartialTxOutput]]:
709 # BOLT-03: "Base commitment transaction fees are extracted from the funder's amount;
710 # if that amount is insufficient, the entire amount of the funder's output is used."
711 # -> if funder cannot afford feerate, their output might go negative, so take max(0, x) here:
712 to_local_amt = max(0, local_amount_msat - fees_per_participant[LOCAL])
713 to_local = PartialTxOutput(scriptpubkey=bfh(local_script), value=to_local_amt // 1000)
714 to_remote_amt = max(0, remote_amount_msat - fees_per_participant[REMOTE])
715 to_remote = PartialTxOutput(scriptpubkey=bfh(remote_script), value=to_remote_amt // 1000)
716
717 non_htlc_outputs = [to_local, to_remote]
718 htlc_outputs = []
719 for script, htlc in htlcs:
720 addr = bitcoin.redeem_script_to_address('p2wsh', bh2u(script))
721 htlc_outputs.append(PartialTxOutput(scriptpubkey=bfh(address_to_script(addr)),
722 value=htlc.amount_msat // 1000))
723
724 # trim outputs
725 c_outputs_filtered = list(filter(lambda x: x.value >= dust_limit_sat, non_htlc_outputs + htlc_outputs))
726 return htlc_outputs, c_outputs_filtered
727
728
729 def offered_htlc_trim_threshold_sat(*, dust_limit_sat: int, feerate: int) -> int:
730 # offered htlcs strictly below this amount will be trimmed (from ctx).
731 # feerate is in sat/kw
732 # returns value in sat
733 weight = HTLC_TIMEOUT_WEIGHT
734 return dust_limit_sat + weight * feerate // 1000
735
736
737 def received_htlc_trim_threshold_sat(*, dust_limit_sat: int, feerate: int) -> int:
738 # received htlcs strictly below this amount will be trimmed (from ctx).
739 # feerate is in sat/kw
740 # returns value in sat
741 weight = HTLC_SUCCESS_WEIGHT
742 return dust_limit_sat + weight * feerate // 1000
743
744
745 def fee_for_htlc_output(*, feerate: int) -> int:
746 # feerate is in sat/kw
747 # returns fee in msat
748 return feerate * HTLC_OUTPUT_WEIGHT
749
750
751 def calc_fees_for_commitment_tx(*, num_htlcs: int, feerate: int,
752 is_local_initiator: bool, round_to_sat: bool = True) -> Dict['HTLCOwner', int]:
753 # feerate is in sat/kw
754 # returns fees in msats
755 # note: BOLT-02 specifies that msat fees need to be rounded down to sat.
756 # However, the rounding needs to happen for the total fees, so if the return value
757 # is to be used as part of additional fee calculation then rounding should be done after that.
758 overall_weight = COMMITMENT_TX_WEIGHT + num_htlcs * HTLC_OUTPUT_WEIGHT
759 fee = feerate * overall_weight
760 if round_to_sat:
761 fee = fee // 1000 * 1000
762 return {
763 LOCAL: fee if is_local_initiator else 0,
764 REMOTE: fee if not is_local_initiator else 0,
765 }
766
767
768 def make_commitment(
769 *,
770 ctn: int,
771 local_funding_pubkey: bytes,
772 remote_funding_pubkey: bytes,
773 remote_payment_pubkey: bytes,
774 funder_payment_basepoint: bytes,
775 fundee_payment_basepoint: bytes,
776 revocation_pubkey: bytes,
777 delayed_pubkey: bytes,
778 to_self_delay: int,
779 funding_txid: str,
780 funding_pos: int,
781 funding_sat: int,
782 local_amount: int,
783 remote_amount: int,
784 dust_limit_sat: int,
785 fees_per_participant: Mapping[HTLCOwner, int],
786 htlcs: List[ScriptHtlc]
787 ) -> PartialTransaction:
788 c_input = make_funding_input(local_funding_pubkey, remote_funding_pubkey,
789 funding_pos, funding_txid, funding_sat)
790 obs = get_obscured_ctn(ctn, funder_payment_basepoint, fundee_payment_basepoint)
791 locktime = (0x20 << 24) + (obs & 0xffffff)
792 sequence = (0x80 << 24) + (obs >> 24)
793 c_input.nsequence = sequence
794
795 c_inputs = [c_input]
796
797 # commitment tx outputs
798 local_address = make_commitment_output_to_local_address(revocation_pubkey, to_self_delay, delayed_pubkey)
799 remote_address = make_commitment_output_to_remote_address(remote_payment_pubkey)
800 # note: it is assumed that the given 'htlcs' are all non-dust (dust htlcs already trimmed)
801
802 # BOLT-03: "Transaction Input and Output Ordering
803 # Lexicographic ordering: see BIP69. In the case of identical HTLC outputs,
804 # the outputs are ordered in increasing cltv_expiry order."
805 # so we sort by cltv_expiry now; and the later BIP69-sort is assumed to be *stable*
806 htlcs = list(htlcs)
807 htlcs.sort(key=lambda x: x.htlc.cltv_expiry)
808
809 htlc_outputs, c_outputs_filtered = make_commitment_outputs(
810 fees_per_participant=fees_per_participant,
811 local_amount_msat=local_amount,
812 remote_amount_msat=remote_amount,
813 local_script=address_to_script(local_address),
814 remote_script=address_to_script(remote_address),
815 htlcs=htlcs,
816 dust_limit_sat=dust_limit_sat)
817
818 assert sum(x.value for x in c_outputs_filtered) <= funding_sat, (c_outputs_filtered, funding_sat)
819
820 # create commitment tx
821 tx = PartialTransaction.from_io(c_inputs, c_outputs_filtered, locktime=locktime, version=2)
822 return tx
823
824 def make_commitment_output_to_local_witness_script(
825 revocation_pubkey: bytes, to_self_delay: int, delayed_pubkey: bytes) -> bytes:
826 script = bfh(construct_script([
827 opcodes.OP_IF,
828 revocation_pubkey,
829 opcodes.OP_ELSE,
830 to_self_delay,
831 opcodes.OP_CHECKSEQUENCEVERIFY,
832 opcodes.OP_DROP,
833 delayed_pubkey,
834 opcodes.OP_ENDIF,
835 opcodes.OP_CHECKSIG,
836 ]))
837 return script
838
839 def make_commitment_output_to_local_address(
840 revocation_pubkey: bytes, to_self_delay: int, delayed_pubkey: bytes) -> str:
841 local_script = make_commitment_output_to_local_witness_script(revocation_pubkey, to_self_delay, delayed_pubkey)
842 return bitcoin.redeem_script_to_address('p2wsh', bh2u(local_script))
843
844 def make_commitment_output_to_remote_address(remote_payment_pubkey: bytes) -> str:
845 return bitcoin.pubkey_to_address('p2wpkh', bh2u(remote_payment_pubkey))
846
847 def sign_and_get_sig_string(tx: PartialTransaction, local_config, remote_config):
848 tx.sign({bh2u(local_config.multisig_key.pubkey): (local_config.multisig_key.privkey, True)})
849 sig = tx.inputs()[0].part_sigs[local_config.multisig_key.pubkey]
850 sig_64 = sig_string_from_der_sig(sig[:-1])
851 return sig_64
852
853 def funding_output_script(local_config, remote_config) -> str:
854 return funding_output_script_from_keys(local_config.multisig_key.pubkey, remote_config.multisig_key.pubkey)
855
856 def funding_output_script_from_keys(pubkey1: bytes, pubkey2: bytes) -> str:
857 pubkeys = sorted([bh2u(pubkey1), bh2u(pubkey2)])
858 return transaction.multisig_script(pubkeys, 2)
859
860
861 def get_obscured_ctn(ctn: int, funder: bytes, fundee: bytes) -> int:
862 mask = int.from_bytes(sha256(funder + fundee)[-6:], 'big')
863 return ctn ^ mask
864
865 def extract_ctn_from_tx(tx: Transaction, txin_index: int, funder_payment_basepoint: bytes,
866 fundee_payment_basepoint: bytes) -> int:
867 tx.deserialize()
868 locktime = tx.locktime
869 sequence = tx.inputs()[txin_index].nsequence
870 obs = ((sequence & 0xffffff) << 24) + (locktime & 0xffffff)
871 return get_obscured_ctn(obs, funder_payment_basepoint, fundee_payment_basepoint)
872
873 def extract_ctn_from_tx_and_chan(tx: Transaction, chan: 'AbstractChannel') -> int:
874 funder_conf = chan.config[LOCAL] if chan.is_initiator() else chan.config[REMOTE]
875 fundee_conf = chan.config[LOCAL] if not chan.is_initiator() else chan.config[REMOTE]
876 return extract_ctn_from_tx(tx, txin_index=0,
877 funder_payment_basepoint=funder_conf.payment_basepoint.pubkey,
878 fundee_payment_basepoint=fundee_conf.payment_basepoint.pubkey)
879
880 def get_ecdh(priv: bytes, pub: bytes) -> bytes:
881 pt = ECPubkey(pub) * string_to_number(priv)
882 return sha256(pt.get_public_key_bytes())
883
884
885 class LnFeatureContexts(enum.Flag):
886 INIT = enum.auto()
887 NODE_ANN = enum.auto()
888 CHAN_ANN_AS_IS = enum.auto()
889 CHAN_ANN_ALWAYS_ODD = enum.auto()
890 CHAN_ANN_ALWAYS_EVEN = enum.auto()
891 INVOICE = enum.auto()
892
893 LNFC = LnFeatureContexts
894
895 _ln_feature_direct_dependencies = defaultdict(set) # type: Dict[LnFeatures, Set[LnFeatures]]
896 _ln_feature_contexts = {} # type: Dict[LnFeatures, LnFeatureContexts]
897
898 class LnFeatures(IntFlag):
899 OPTION_DATA_LOSS_PROTECT_REQ = 1 << 0
900 OPTION_DATA_LOSS_PROTECT_OPT = 1 << 1
901 _ln_feature_contexts[OPTION_DATA_LOSS_PROTECT_OPT] = (LNFC.INIT | LnFeatureContexts.NODE_ANN)
902 _ln_feature_contexts[OPTION_DATA_LOSS_PROTECT_REQ] = (LNFC.INIT | LnFeatureContexts.NODE_ANN)
903
904 INITIAL_ROUTING_SYNC = 1 << 3
905 _ln_feature_contexts[INITIAL_ROUTING_SYNC] = LNFC.INIT
906
907 OPTION_UPFRONT_SHUTDOWN_SCRIPT_REQ = 1 << 4
908 OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT = 1 << 5
909 _ln_feature_contexts[OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
910 _ln_feature_contexts[OPTION_UPFRONT_SHUTDOWN_SCRIPT_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
911
912 GOSSIP_QUERIES_REQ = 1 << 6
913 GOSSIP_QUERIES_OPT = 1 << 7
914 _ln_feature_contexts[GOSSIP_QUERIES_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
915 _ln_feature_contexts[GOSSIP_QUERIES_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
916
917 VAR_ONION_REQ = 1 << 8
918 VAR_ONION_OPT = 1 << 9
919 _ln_feature_contexts[VAR_ONION_OPT] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
920 _ln_feature_contexts[VAR_ONION_REQ] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
921
922 GOSSIP_QUERIES_EX_REQ = 1 << 10
923 GOSSIP_QUERIES_EX_OPT = 1 << 11
924 _ln_feature_direct_dependencies[GOSSIP_QUERIES_EX_OPT] = {GOSSIP_QUERIES_OPT}
925 _ln_feature_contexts[GOSSIP_QUERIES_EX_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
926 _ln_feature_contexts[GOSSIP_QUERIES_EX_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
927
928 OPTION_STATIC_REMOTEKEY_REQ = 1 << 12
929 OPTION_STATIC_REMOTEKEY_OPT = 1 << 13
930 _ln_feature_contexts[OPTION_STATIC_REMOTEKEY_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
931 _ln_feature_contexts[OPTION_STATIC_REMOTEKEY_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
932
933 PAYMENT_SECRET_REQ = 1 << 14
934 PAYMENT_SECRET_OPT = 1 << 15
935 _ln_feature_direct_dependencies[PAYMENT_SECRET_OPT] = {VAR_ONION_OPT}
936 _ln_feature_contexts[PAYMENT_SECRET_OPT] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
937 _ln_feature_contexts[PAYMENT_SECRET_REQ] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
938
939 BASIC_MPP_REQ = 1 << 16
940 BASIC_MPP_OPT = 1 << 17
941 _ln_feature_direct_dependencies[BASIC_MPP_OPT] = {PAYMENT_SECRET_OPT}
942 _ln_feature_contexts[BASIC_MPP_OPT] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
943 _ln_feature_contexts[BASIC_MPP_REQ] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
944
945 OPTION_SUPPORT_LARGE_CHANNEL_REQ = 1 << 18
946 OPTION_SUPPORT_LARGE_CHANNEL_OPT = 1 << 19
947 _ln_feature_contexts[OPTION_SUPPORT_LARGE_CHANNEL_OPT] = (LNFC.INIT | LNFC.NODE_ANN)
948 _ln_feature_contexts[OPTION_SUPPORT_LARGE_CHANNEL_REQ] = (LNFC.INIT | LNFC.NODE_ANN)
949
950 OPTION_TRAMPOLINE_ROUTING_REQ = 1 << 24
951 OPTION_TRAMPOLINE_ROUTING_OPT = 1 << 25
952
953 _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_REQ] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
954 _ln_feature_contexts[OPTION_TRAMPOLINE_ROUTING_OPT] = (LNFC.INIT | LNFC.NODE_ANN | LNFC.INVOICE)
955
956 # temporary
957 OPTION_TRAMPOLINE_ROUTING_REQ_ECLAIR = 1 << 50
958 OPTION_TRAMPOLINE_ROUTING_OPT_ECLAIR = 1 << 51
959
960 def validate_transitive_dependencies(self) -> bool:
961 # for all even bit set, set corresponding odd bit:
962 features = self # copy
963 flags = list_enabled_bits(features)
964 for flag in flags:
965 if flag % 2 == 0:
966 features |= 1 << get_ln_flag_pair_of_bit(flag)
967 # Check dependencies. We only check that the direct dependencies of each flag set
968 # are satisfied: this implies that transitive dependencies are also satisfied.
969 flags = list_enabled_bits(features)
970 for flag in flags:
971 for dependency in _ln_feature_direct_dependencies[1 << flag]:
972 if not (dependency & features):
973 return False
974 return True
975
976 def for_init_message(self) -> 'LnFeatures':
977 features = LnFeatures(0)
978 for flag in list_enabled_bits(self):
979 if LnFeatureContexts.INIT & _ln_feature_contexts[1 << flag]:
980 features |= (1 << flag)
981 return features
982
983 def for_node_announcement(self) -> 'LnFeatures':
984 features = LnFeatures(0)
985 for flag in list_enabled_bits(self):
986 if LnFeatureContexts.NODE_ANN & _ln_feature_contexts[1 << flag]:
987 features |= (1 << flag)
988 return features
989
990 def for_invoice(self) -> 'LnFeatures':
991 features = LnFeatures(0)
992 for flag in list_enabled_bits(self):
993 if LnFeatureContexts.INVOICE & _ln_feature_contexts[1 << flag]:
994 features |= (1 << flag)
995 return features
996
997 def for_channel_announcement(self) -> 'LnFeatures':
998 features = LnFeatures(0)
999 for flag in list_enabled_bits(self):
1000 ctxs = _ln_feature_contexts[1 << flag]
1001 if LnFeatureContexts.CHAN_ANN_AS_IS & ctxs:
1002 features |= (1 << flag)
1003 elif LnFeatureContexts.CHAN_ANN_ALWAYS_EVEN & ctxs:
1004 if flag % 2 == 0:
1005 features |= (1 << flag)
1006 elif LnFeatureContexts.CHAN_ANN_ALWAYS_ODD & ctxs:
1007 if flag % 2 == 0:
1008 flag = get_ln_flag_pair_of_bit(flag)
1009 features |= (1 << flag)
1010 return features
1011
1012 def supports(self, feature: 'LnFeatures') -> bool:
1013 """Returns whether given feature is enabled.
1014
1015 Helper function that tries to hide the complexity of even/odd bits.
1016 For example, instead of:
1017 bool(myfeatures & LnFeatures.VAR_ONION_OPT or myfeatures & LnFeatures.VAR_ONION_REQ)
1018 you can do:
1019 myfeatures.supports(LnFeatures.VAR_ONION_OPT)
1020 """
1021 enabled_bits = list_enabled_bits(feature)
1022 if len(enabled_bits) != 1:
1023 raise ValueError(f"'feature' cannot be a combination of features: {feature}")
1024 flag = enabled_bits[0]
1025 our_flags = set(list_enabled_bits(self))
1026 return (flag in our_flags
1027 or get_ln_flag_pair_of_bit(flag) in our_flags)
1028
1029
1030 del LNFC # name is ambiguous without context
1031
1032 # features that are actually implemented and understood in our codebase:
1033 # (note: this is not what we send in e.g. init!)
1034 # (note: specify both OPT and REQ here)
1035 LN_FEATURES_IMPLEMENTED = (
1036 LnFeatures(0)
1037 | LnFeatures.OPTION_DATA_LOSS_PROTECT_OPT | LnFeatures.OPTION_DATA_LOSS_PROTECT_REQ
1038 | LnFeatures.GOSSIP_QUERIES_OPT | LnFeatures.GOSSIP_QUERIES_REQ
1039 | LnFeatures.OPTION_STATIC_REMOTEKEY_OPT | LnFeatures.OPTION_STATIC_REMOTEKEY_REQ
1040 | LnFeatures.VAR_ONION_OPT | LnFeatures.VAR_ONION_REQ
1041 | LnFeatures.PAYMENT_SECRET_OPT | LnFeatures.PAYMENT_SECRET_REQ
1042 | LnFeatures.BASIC_MPP_OPT | LnFeatures.BASIC_MPP_REQ
1043 | LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT | LnFeatures.OPTION_TRAMPOLINE_ROUTING_REQ
1044 )
1045
1046
1047 def get_ln_flag_pair_of_bit(flag_bit: int) -> int:
1048 """Ln Feature flags are assigned in pairs, one even, one odd. See BOLT-09.
1049 Return the other flag from the pair.
1050 e.g. 6 -> 7
1051 e.g. 7 -> 6
1052 """
1053 if flag_bit % 2 == 0:
1054 return flag_bit + 1
1055 else:
1056 return flag_bit - 1
1057
1058
1059
1060 class IncompatibleOrInsaneFeatures(Exception): pass
1061 class UnknownEvenFeatureBits(IncompatibleOrInsaneFeatures): pass
1062 class IncompatibleLightningFeatures(IncompatibleOrInsaneFeatures): pass
1063
1064
1065 def ln_compare_features(our_features: 'LnFeatures', their_features: int) -> 'LnFeatures':
1066 """Returns negotiated features.
1067 Raises IncompatibleLightningFeatures if incompatible.
1068 """
1069 our_flags = set(list_enabled_bits(our_features))
1070 their_flags = set(list_enabled_bits(their_features))
1071 # check that they have our required features, and disable the optional features they don't have
1072 for flag in our_flags:
1073 if flag not in their_flags and get_ln_flag_pair_of_bit(flag) not in their_flags:
1074 # they don't have this feature we wanted :(
1075 if flag % 2 == 0: # even flags are compulsory
1076 raise IncompatibleLightningFeatures(f"remote does not support {LnFeatures(1 << flag)!r}")
1077 our_features ^= 1 << flag # disable flag
1078 else:
1079 # They too have this flag.
1080 # For easier feature-bit-testing, if this is an even flag, we also
1081 # set the corresponding odd flag now.
1082 if flag % 2 == 0 and our_features & (1 << flag):
1083 our_features |= 1 << get_ln_flag_pair_of_bit(flag)
1084 # check that we have their required features
1085 for flag in their_flags:
1086 if flag not in our_flags and get_ln_flag_pair_of_bit(flag) not in our_flags:
1087 # we don't have this feature they wanted :(
1088 if flag % 2 == 0: # even flags are compulsory
1089 raise IncompatibleLightningFeatures(f"remote wanted feature we don't have: {LnFeatures(1 << flag)!r}")
1090 return our_features
1091
1092
1093 def validate_features(features: int) -> None:
1094 """Raises IncompatibleOrInsaneFeatures if
1095 - a mandatory feature is listed that we don't recognize, or
1096 - the features are inconsistent
1097 """
1098 features = LnFeatures(features)
1099 enabled_features = list_enabled_bits(features)
1100 for fbit in enabled_features:
1101 if (1 << fbit) & LN_FEATURES_IMPLEMENTED == 0 and fbit % 2 == 0:
1102 raise UnknownEvenFeatureBits(fbit)
1103 if not features.validate_transitive_dependencies():
1104 raise IncompatibleOrInsaneFeatures(f"not all transitive dependencies are set. "
1105 f"features={features}")
1106
1107
1108 def derive_payment_secret_from_payment_preimage(payment_preimage: bytes) -> bytes:
1109 """Returns secret to be put into invoice.
1110 Derivation is deterministic, based on the preimage.
1111 Crucially the payment_hash must be derived in an independent way from this.
1112 """
1113 # Note that this could be random data too, but then we would need to store it.
1114 # We derive it identically to clightning, so that we cannot be distinguished:
1115 # https://github.com/ElementsProject/lightning/blob/faac4b28adee5221e83787d64cd5d30b16b62097/lightningd/invoice.c#L115
1116 modified = bytearray(payment_preimage)
1117 modified[0] ^= 1
1118 return sha256(bytes(modified))
1119
1120
1121 class LNPeerAddr:
1122 # note: while not programmatically enforced, this class is meant to be *immutable*
1123
1124 def __init__(self, host: str, port: int, pubkey: bytes):
1125 assert isinstance(host, str), repr(host)
1126 assert isinstance(port, int), repr(port)
1127 assert isinstance(pubkey, bytes), repr(pubkey)
1128 try:
1129 net_addr = NetAddress(host, port) # this validates host and port
1130 except Exception as e:
1131 raise ValueError(f"cannot construct LNPeerAddr: invalid host or port (host={host}, port={port})") from e
1132 # note: not validating pubkey as it would be too expensive:
1133 # if not ECPubkey.is_pubkey_bytes(pubkey): raise ValueError()
1134 self.host = host
1135 self.port = port
1136 self.pubkey = pubkey
1137 self._net_addr = net_addr
1138
1139 def __str__(self):
1140 return '{}@{}'.format(self.pubkey.hex(), self.net_addr_str())
1141
1142 def __repr__(self):
1143 return f'<LNPeerAddr host={self.host} port={self.port} pubkey={self.pubkey.hex()}>'
1144
1145 def net_addr(self) -> NetAddress:
1146 return self._net_addr
1147
1148 def net_addr_str(self) -> str:
1149 return str(self._net_addr)
1150
1151 def __eq__(self, other):
1152 if not isinstance(other, LNPeerAddr):
1153 return False
1154 return (self.host == other.host
1155 and self.port == other.port
1156 and self.pubkey == other.pubkey)
1157
1158 def __ne__(self, other):
1159 return not (self == other)
1160
1161 def __hash__(self):
1162 return hash((self.host, self.port, self.pubkey))
1163
1164
1165 def get_compressed_pubkey_from_bech32(bech32_pubkey: str) -> bytes:
1166 hrp, data_5bits = segwit_addr.bech32_decode(bech32_pubkey)
1167 if hrp != 'ln':
1168 raise Exception('unexpected hrp: {}'.format(hrp))
1169 data_8bits = segwit_addr.convertbits(data_5bits, 5, 8, False)
1170 # pad with zeroes
1171 COMPRESSED_PUBKEY_LENGTH = 33
1172 data_8bits = data_8bits + ((COMPRESSED_PUBKEY_LENGTH - len(data_8bits)) * [0])
1173 return bytes(data_8bits)
1174
1175
1176 def make_closing_tx(local_funding_pubkey: bytes, remote_funding_pubkey: bytes,
1177 funding_txid: str, funding_pos: int, funding_sat: int,
1178 outputs: List[PartialTxOutput]) -> PartialTransaction:
1179 c_input = make_funding_input(local_funding_pubkey, remote_funding_pubkey,
1180 funding_pos, funding_txid, funding_sat)
1181 c_input.nsequence = 0xFFFF_FFFF
1182 tx = PartialTransaction.from_io([c_input], outputs, locktime=0, version=2)
1183 return tx
1184
1185
1186 def split_host_port(host_port: str) -> Tuple[str, str]: # port returned as string
1187 ipv6 = re.compile(r'\[(?P<host>[:0-9a-f]+)\](?P<port>:\d+)?$')
1188 other = re.compile(r'(?P<host>[^:]+)(?P<port>:\d+)?$')
1189 m = ipv6.match(host_port)
1190 if not m:
1191 m = other.match(host_port)
1192 if not m:
1193 raise ConnStringFormatError(_('Connection strings must be in <node_pubkey>@<host>:<port> format'))
1194 host = m.group('host')
1195 if m.group('port'):
1196 port = m.group('port')[1:]
1197 else:
1198 port = '9735'
1199 try:
1200 int(port)
1201 except ValueError:
1202 raise ConnStringFormatError(_('Port number must be decimal'))
1203 return host, port
1204
1205 def extract_nodeid(connect_contents: str) -> Tuple[bytes, str]:
1206 rest = None
1207 try:
1208 # connection string?
1209 nodeid_hex, rest = connect_contents.split("@", 1)
1210 except ValueError:
1211 try:
1212 # invoice?
1213 invoice = lndecode(connect_contents)
1214 nodeid_bytes = invoice.pubkey.serialize()
1215 nodeid_hex = bh2u(nodeid_bytes)
1216 except:
1217 # node id as hex?
1218 nodeid_hex = connect_contents
1219 if rest == '':
1220 raise ConnStringFormatError(_('At least a hostname must be supplied after the at symbol.'))
1221 try:
1222 node_id = bfh(nodeid_hex)
1223 if len(node_id) != 33:
1224 raise Exception()
1225 except:
1226 raise ConnStringFormatError(_('Invalid node ID, must be 33 bytes and hexadecimal'))
1227 return node_id, rest
1228
1229
1230 # key derivation
1231 # see lnd/keychain/derivation.go
1232 class LnKeyFamily(IntEnum):
1233 MULTISIG = 0 | BIP32_PRIME
1234 REVOCATION_BASE = 1 | BIP32_PRIME
1235 HTLC_BASE = 2 | BIP32_PRIME
1236 PAYMENT_BASE = 3 | BIP32_PRIME
1237 DELAY_BASE = 4 | BIP32_PRIME
1238 REVOCATION_ROOT = 5 | BIP32_PRIME
1239 NODE_KEY = 6
1240
1241
1242 def generate_keypair(node: BIP32Node, key_family: LnKeyFamily) -> Keypair:
1243 node2 = node.subkey_at_private_derivation([key_family, 0, 0])
1244 k = node2.eckey.get_secret_bytes()
1245 cK = ecc.ECPrivkey(k).get_public_key_bytes()
1246 return Keypair(cK, k)
1247
1248
1249
1250 NUM_MAX_HOPS_IN_PAYMENT_PATH = 20
1251 NUM_MAX_EDGES_IN_PAYMENT_PATH = NUM_MAX_HOPS_IN_PAYMENT_PATH
1252
1253
1254 class ShortChannelID(bytes):
1255
1256 def __repr__(self):
1257 return f"<ShortChannelID: {format_short_channel_id(self)}>"
1258
1259 def __str__(self):
1260 return format_short_channel_id(self)
1261
1262 @classmethod
1263 def from_components(cls, block_height: int, tx_pos_in_block: int, output_index: int) -> 'ShortChannelID':
1264 bh = block_height.to_bytes(3, byteorder='big')
1265 tpos = tx_pos_in_block.to_bytes(3, byteorder='big')
1266 oi = output_index.to_bytes(2, byteorder='big')
1267 return ShortChannelID(bh + tpos + oi)
1268
1269 @classmethod
1270 def from_str(cls, scid: str) -> 'ShortChannelID':
1271 """Parses a formatted scid str, e.g. '643920x356x0'."""
1272 components = scid.split("x")
1273 if len(components) != 3:
1274 raise ValueError(f"failed to parse ShortChannelID: {scid!r}")
1275 try:
1276 components = [int(x) for x in components]
1277 except ValueError:
1278 raise ValueError(f"failed to parse ShortChannelID: {scid!r}") from None
1279 return ShortChannelID.from_components(*components)
1280
1281 @classmethod
1282 def normalize(cls, data: Union[None, str, bytes, 'ShortChannelID']) -> Optional['ShortChannelID']:
1283 if isinstance(data, ShortChannelID) or data is None:
1284 return data
1285 if isinstance(data, str):
1286 assert len(data) == 16
1287 return ShortChannelID.fromhex(data)
1288 if isinstance(data, (bytes, bytearray)):
1289 assert len(data) == 8
1290 return ShortChannelID(data)
1291
1292 @property
1293 def block_height(self) -> int:
1294 return int.from_bytes(self[:3], byteorder='big')
1295
1296 @property
1297 def txpos(self) -> int:
1298 return int.from_bytes(self[3:6], byteorder='big')
1299
1300 @property
1301 def output_index(self) -> int:
1302 return int.from_bytes(self[6:8], byteorder='big')
1303
1304
1305 def format_short_channel_id(short_channel_id: Optional[bytes]):
1306 if not short_channel_id:
1307 return _('Not yet available')
1308 return str(int.from_bytes(short_channel_id[:3], 'big')) \
1309 + 'x' + str(int.from_bytes(short_channel_id[3:6], 'big')) \
1310 + 'x' + str(int.from_bytes(short_channel_id[6:], 'big'))
1311
1312
1313 @attr.s(frozen=True)
1314 class UpdateAddHtlc:
1315 amount_msat = attr.ib(type=int, kw_only=True)
1316 payment_hash = attr.ib(type=bytes, kw_only=True, converter=hex_to_bytes, repr=lambda val: val.hex())
1317 cltv_expiry = attr.ib(type=int, kw_only=True)
1318 timestamp = attr.ib(type=int, kw_only=True)
1319 htlc_id = attr.ib(type=int, kw_only=True, default=None)
1320
1321 @classmethod
1322 def from_tuple(cls, amount_msat, payment_hash, cltv_expiry, htlc_id, timestamp) -> 'UpdateAddHtlc':
1323 return cls(amount_msat=amount_msat,
1324 payment_hash=payment_hash,
1325 cltv_expiry=cltv_expiry,
1326 htlc_id=htlc_id,
1327 timestamp=timestamp)
1328
1329 def to_tuple(self):
1330 return (self.amount_msat, self.payment_hash, self.cltv_expiry, self.htlc_id, self.timestamp)
1331
1332
1333 class OnionFailureCodeMetaFlag(IntFlag):
1334 BADONION = 0x8000
1335 PERM = 0x4000
1336 NODE = 0x2000
1337 UPDATE = 0x1000
1338
1339
1340 class ChannelBlackList:
1341
1342 def __init__(self):
1343 self.blacklist = dict() # short_chan_id -> timestamp
1344
1345 def add(self, short_channel_id: ShortChannelID):
1346 now = int(time.time())
1347 self.blacklist[short_channel_id] = now
1348
1349 def get_current_list(self) -> Set[ShortChannelID]:
1350 BLACKLIST_DURATION = 3600
1351 now = int(time.time())
1352 return set(k for k, t in self.blacklist.items() if now - t < BLACKLIST_DURATION)