Fix: Refund-path / Maturity checks.

This commit is contained in:
gerlofvanek
2026-05-29 18:36:05 +02:00
parent 5099b9ebaa
commit 221d962c12
2 changed files with 89 additions and 15 deletions
+25 -2
View File
@@ -5613,6 +5613,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
)
# Check non-bip68 final
if not ci_from.useBackend():
try:
txid = ci_from.publishTx(bid.initiate_txn_refund)
self.log.error(
@@ -7686,7 +7687,16 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
self.commitDB()
if TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns:
if (
TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns
and refund_tx.block_height is not None
and ci_from.isCsvLockMature(
offer.lock_type,
xmr_offer.lock_time_2,
refund_tx.block_height,
refund_tx.block_time,
)
):
try:
if self.haveDebugInd(
bid.bid_id,
@@ -7782,6 +7792,13 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
if (
len(xmr_swap.al_lock_refund_tx_sig) > 0
and len(xmr_swap.af_lock_refund_tx_sig) > 0
and bid.xmr_a_lock_tx is not None
and ci_from.isCsvLockMature(
offer.lock_type,
xmr_offer.lock_time_1,
bid.xmr_a_lock_tx.block_height,
bid.xmr_a_lock_tx.block_time,
)
):
try:
@@ -8473,6 +8490,9 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
if (
bid.getITxState() in (TxStates.TX_SENT, TxStates.TX_CONFIRMED)
and bid.initiate_txn_refund is not None
and ci_from.isAbsLockTimeMature(
ci_from.loadTx(bid.initiate_txn_refund).nLockTime
)
):
try:
txid = ci_from.publishTx(bid.initiate_txn_refund)
@@ -8496,6 +8516,9 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
if (
bid.getPTxState() in (TxStates.TX_SENT, TxStates.TX_CONFIRMED)
and bid.participate_txn_refund is not None
and ci_to.isAbsLockTimeMature(
ci_to.loadTx(bid.participate_txn_refund).nLockTime
)
):
try:
txid = ci_to.publishTx(bid.participate_txn_refund)
@@ -8511,7 +8534,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
)
# State will update when spend is detected
except Exception as ex:
if ci_to.isTxNonFinalError(str(ex)):
if ci_to.isTxNonFinalError(str(ex)) is False:
self.log.warning(
f"Error trying to submit participate refund txn: {ex}"
)
+55 -4
View File
@@ -502,6 +502,57 @@ class BTCInterface(Secp256k1Interface):
return height
return self.rpc("getblockcount")
def getChainMedianTime(self) -> int:
if self.useBackend():
height = self._backend.getBlockHeight()
times = []
for h in range(max(0, height - 10), height + 1):
header = self._getBlockHeaderFromHeightElectrum(h)
times.append(header["time"])
times.sort()
return times[len(times) // 2]
return self.rpc("getblockchaininfo")["mediantime"]
def isCsvLockMature(
self,
lock_type: int,
encoded_sequence: int,
parent_block_height: Optional[int],
parent_block_time: Optional[int],
chain_height: Optional[int] = None,
chain_mtp: Optional[int] = None,
) -> bool:
if parent_block_height is None or parent_block_height < 1:
return False
lock_value: int = self.decodeSequence(encoded_sequence)
if lock_type == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
if chain_height is None:
chain_height = self.getChainHeight()
return chain_height + 1 >= parent_block_height + lock_value
if lock_type == TxLockTypes.SEQUENCE_LOCK_TIME:
if parent_block_time is None or parent_block_time < 1:
return False
if chain_mtp is None:
chain_mtp = self.getChainMedianTime()
return chain_mtp >= parent_block_time + lock_value
raise ValueError(f"Unknown lock type {lock_type}")
def isAbsLockTimeMature(
self,
nlocktime: int,
chain_height: Optional[int] = None,
chain_mtp: Optional[int] = None,
) -> bool:
if nlocktime == 0:
return True
if nlocktime < 500000000:
if chain_height is None:
chain_height = self.getChainHeight()
return chain_height + 1 >= nlocktime
if chain_mtp is None:
chain_mtp = self.getChainMedianTime()
return chain_mtp >= nlocktime
def getMempoolTx(self, txid):
if self._connection_type == "electrum":
backend = self.getBackend()
@@ -4370,11 +4421,11 @@ class BTCInterface(Secp256k1Interface):
return "Transaction already in block chain" in err_str
def isTxNonFinalError(self, err_str: str) -> bool:
err_lower = err_str.lower()
return (
"non-BIP68-final" in err_str
or "non-final" in err_str
or "Missing inputs" in err_str
or "bad-txns-inputs-missingorspent" in err_str
"non-bip68-final" in err_lower
or "non-final" in err_lower
or "locktime requirement not satisfied" in err_lower
)
def combine_non_segwit_prevouts(self):