txbatcher: add TODOs re nLocktime block-height vs timestamp confusion
seems harmless atm but some rethinking is needed and checks should be added
This commit is contained in:
@@ -40,6 +40,7 @@ HTLCTX_INPUT_OUTPUT_INDEX = 0
|
|||||||
class SweepInfo(NamedTuple):
|
class SweepInfo(NamedTuple):
|
||||||
name: str
|
name: str
|
||||||
cltv_abs: Optional[int] # set to None only if the script has no cltv
|
cltv_abs: Optional[int] # set to None only if the script has no cltv
|
||||||
|
# TODO add asserts that cltv_abs is block-based (see NLOCKTIME_BLOCKHEIGHT_MAX)
|
||||||
txin: PartialTxInput
|
txin: PartialTxInput
|
||||||
txout: Optional[PartialTxOutput] # only for first-stage htlc tx
|
txout: Optional[PartialTxOutput] # only for first-stage htlc tx
|
||||||
can_be_batched: bool # todo: this could be more fine-grained
|
can_be_batched: bool # todo: this could be more fine-grained
|
||||||
|
|||||||
@@ -349,16 +349,18 @@ class TxInput:
|
|||||||
|
|
||||||
def get_time_based_relative_locktime(self) -> Optional[int]:
|
def get_time_based_relative_locktime(self) -> Optional[int]:
|
||||||
# see bip 68
|
# see bip 68
|
||||||
if self.nsequence & (1<<31):
|
if self.nsequence & (1<<31): # "disable" flag
|
||||||
return
|
return None
|
||||||
if self.nsequence & (1<<22):
|
if self.nsequence & (1<<22): # in units of 512 sec
|
||||||
return self.nsequence & 0xffff
|
return self.nsequence & 0xffff
|
||||||
|
return None
|
||||||
|
|
||||||
def get_block_based_relative_locktime(self) -> Optional[int]:
|
def get_block_based_relative_locktime(self) -> Optional[int]:
|
||||||
if self.nsequence & (1<<31):
|
if self.nsequence & (1<<31): # "disable" flag
|
||||||
return
|
return None
|
||||||
if not self.nsequence & (1<<22):
|
if not self.nsequence & (1<<22): # in blocks
|
||||||
return self.nsequence & 0xffff
|
return self.nsequence & 0xffff
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_id(self):
|
def short_id(self):
|
||||||
@@ -1328,13 +1330,13 @@ class Transaction:
|
|||||||
|
|
||||||
def get_time_based_relative_locktime(self) -> Optional[int]:
|
def get_time_based_relative_locktime(self) -> Optional[int]:
|
||||||
if self.version < 2:
|
if self.version < 2:
|
||||||
return
|
return None
|
||||||
locktimes = list(filter(None, [txin.get_time_based_relative_locktime() for txin in self.inputs()]))
|
locktimes = list(filter(None, [txin.get_time_based_relative_locktime() for txin in self.inputs()]))
|
||||||
return max(locktimes) if locktimes else None
|
return max(locktimes) if locktimes else None
|
||||||
|
|
||||||
def get_block_based_relative_locktime(self) -> Optional[int]:
|
def get_block_based_relative_locktime(self) -> Optional[int]:
|
||||||
if self.version < 2:
|
if self.version < 2:
|
||||||
return
|
return None
|
||||||
locktimes = list(filter(None, [txin.get_block_based_relative_locktime() for txin in self.inputs()]))
|
locktimes = list(filter(None, [txin.get_block_based_relative_locktime() for txin in self.inputs()]))
|
||||||
return max(locktimes) if locktimes else None
|
return max(locktimes) if locktimes else None
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
#
|
#
|
||||||
# 1. CPFP:
|
# 1. CPFP:
|
||||||
# When a batch is forgotten but not mined (because the server returned an error), we no longer bump its fee.
|
# When a batch is forgotten but not mined (because the server returned an error), we no longer bump its fee.
|
||||||
# However, the current code does not theat the next batch as a CPFP when computing the fee.
|
# However, the current code does not treat the next batch as a CPFP when computing the fee.
|
||||||
#
|
#
|
||||||
# 2. Reorgs:
|
# 2. Reorgs:
|
||||||
# This code does not guarantee that a payment or a sweep will happen.
|
# This code does not guarantee that a payment or a sweep will happen.
|
||||||
@@ -56,6 +56,13 @@
|
|||||||
# In the case of sweeps, lnwatcher ensures that SweepInfo is added again after a client restart.
|
# In the case of sweeps, lnwatcher ensures that SweepInfo is added again after a client restart.
|
||||||
# In order to generalize that logic to payments, callers would need to pass a unique ID along with
|
# In order to generalize that logic to payments, callers would need to pass a unique ID along with
|
||||||
# the payment output, so that we can prevent paying twice.
|
# the payment output, so that we can prevent paying twice.
|
||||||
|
#
|
||||||
|
# - nLocktime/CLTV values (bip-65) and nSequence/CSV values (bip-112) are either explicitly
|
||||||
|
# or implicitly block-height-based everywhere in this file.
|
||||||
|
# SCRIPT execution fails on height vs timestamp confusion, and
|
||||||
|
# it is not safe to do naive integer comparison between these values without establishing type.
|
||||||
|
# TODO review this is correct, and add checks.
|
||||||
|
# nLocktime/CLTV usage in particular seems dangerously *implicit* for being block-heights
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
@@ -517,7 +524,7 @@ class TxBatch(Logger):
|
|||||||
# sort inputs so that txin-txout pairs are first
|
# sort inputs so that txin-txout pairs are first
|
||||||
for sweep_info in sorted(to_sweep, key=lambda x: not bool(x.txout)):
|
for sweep_info in sorted(to_sweep, key=lambda x: not bool(x.txout)):
|
||||||
if sweep_info.cltv_abs is not None:
|
if sweep_info.cltv_abs is not None:
|
||||||
if locktime is None or locktime < sweep_info.cltv_abs:
|
if locktime is None or locktime < sweep_info.cltv_abs: # FIXME height vs timestamp confusion
|
||||||
# nLockTime must be greater than or equal to the stack operand.
|
# nLockTime must be greater than or equal to the stack operand.
|
||||||
locktime = sweep_info.cltv_abs
|
locktime = sweep_info.cltv_abs
|
||||||
inputs.append(copy.deepcopy(sweep_info.txin))
|
inputs.append(copy.deepcopy(sweep_info.txin))
|
||||||
|
|||||||
Reference in New Issue
Block a user