mirror of
https://github.com/basicswap/basicswap.git
synced 2026-06-07 19:51:41 +02:00
Fix: Refund-path / Maturity checks.
This commit is contained in:
+34
-11
@@ -5613,15 +5613,16 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
||||
)
|
||||
|
||||
# Check non-bip68 final
|
||||
try:
|
||||
txid = ci_from.publishTx(bid.initiate_txn_refund)
|
||||
self.log.error(
|
||||
f"Submit refund_txn unexpectedly worked {self.logIDT(bytes.fromhex(txid))}"
|
||||
)
|
||||
except Exception as ex:
|
||||
if ci_from.isTxNonFinalError(str(ex)) is False:
|
||||
self.log.error(f"Submit refund_txn unexpected error: {ex}")
|
||||
raise ex
|
||||
if not ci_from.useBackend():
|
||||
try:
|
||||
txid = ci_from.publishTx(bid.initiate_txn_refund)
|
||||
self.log.error(
|
||||
f"Submit refund_txn unexpectedly worked {self.logIDT(bytes.fromhex(txid))}"
|
||||
)
|
||||
except Exception as ex:
|
||||
if ci_from.isTxNonFinalError(str(ex)) is False:
|
||||
self.log.error(f"Submit refund_txn unexpected error: {ex}")
|
||||
raise ex
|
||||
|
||||
if txid is not None:
|
||||
msg_buf = BidAcceptMessage()
|
||||
@@ -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}"
|
||||
)
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user