mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-23 03:49:25 +00:00
Add BTC type swipe tx mercy outputs.
This commit is contained in:
parent
9d7841da46
commit
c561efaba0
7 changed files with 119 additions and 18 deletions
|
@ -4492,7 +4492,7 @@ class BasicSwap(BaseApp):
|
||||||
if session is None:
|
if session is None:
|
||||||
self.closeSession(use_session)
|
self.closeSession(use_session)
|
||||||
|
|
||||||
def process_XMR_SWAP_A_LOCK_REFUND_tx_spend(self, bid_id: bytes, spend_txid_hex, spend_txn) -> None:
|
def process_XMR_SWAP_A_LOCK_REFUND_tx_spend(self, bid_id: bytes, spend_txid_hex: str, spend_txn) -> None:
|
||||||
self.log.debug('Detected spend of Adaptor-sig swap coin a lock refund tx for bid %s', bid_id.hex())
|
self.log.debug('Detected spend of Adaptor-sig swap coin a lock refund tx for bid %s', bid_id.hex())
|
||||||
try:
|
try:
|
||||||
session = self.openSession()
|
session = self.openSession()
|
||||||
|
@ -4510,15 +4510,17 @@ class BasicSwap(BaseApp):
|
||||||
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
||||||
was_received: bool = bid.was_sent if reverse_bid else bid.was_received
|
was_received: bool = bid.was_sent if reverse_bid else bid.was_received
|
||||||
|
|
||||||
|
ci_from = self.ci(coin_from)
|
||||||
|
|
||||||
state = BidStates(bid.state)
|
state = BidStates(bid.state)
|
||||||
spending_txid = bytes.fromhex(spend_txid_hex)
|
spending_txid = bytes.fromhex(spend_txid_hex)
|
||||||
|
|
||||||
spend_txn_hex = spend_txn['hex']
|
spend_txn_hex = spend_txn['hex']
|
||||||
spend_tx = self.ci(coin_from).loadTx(h2b(spend_txn_hex))
|
spend_tx = ci_from.loadTx(h2b(spend_txn_hex))
|
||||||
|
|
||||||
is_spending_lock_refund_tx = False
|
is_spending_lock_refund_tx = False
|
||||||
if self.isBchXmrSwap(offer):
|
if self.isBchXmrSwap(offer):
|
||||||
is_spending_lock_refund_tx = self.ci(coin_from).isSpendingLockRefundTx(spend_tx)
|
is_spending_lock_refund_tx = ci_from.isSpendingLockRefundTx(spend_tx)
|
||||||
|
|
||||||
if spending_txid == xmr_swap.a_lock_refund_spend_tx_id or (i2b(spend_tx.vin[0].prevout.hash) == xmr_swap.a_lock_refund_tx_id and is_spending_lock_refund_tx):
|
if spending_txid == xmr_swap.a_lock_refund_spend_tx_id or (i2b(spend_tx.vin[0].prevout.hash) == xmr_swap.a_lock_refund_tx_id and is_spending_lock_refund_tx):
|
||||||
self.log.info('Found coin a lock refund spend tx, bid {}'.format(bid_id.hex()))
|
self.log.info('Found coin a lock refund spend tx, bid {}'.format(bid_id.hex()))
|
||||||
|
@ -4559,9 +4561,38 @@ class BasicSwap(BaseApp):
|
||||||
else:
|
else:
|
||||||
self.log.info('Coin a lock refund spent by unknown tx, bid {}'.format(bid_id.hex()))
|
self.log.info('Coin a lock refund spent by unknown tx, bid {}'.format(bid_id.hex()))
|
||||||
|
|
||||||
if not was_received or bid.xmr_b_lock_tx is None:
|
mercy_keyshare = None
|
||||||
# Leave active if was received, to try and get lock tx b with mercy release
|
if was_received:
|
||||||
|
if self.isBchXmrSwap(offer):
|
||||||
|
# Mercy tx is sent separately
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Look for a mercy output
|
||||||
|
try:
|
||||||
|
mercy_keyshare = ci_from.inspectSwipeTx(spend_txn)
|
||||||
|
if mercy_keyshare is None:
|
||||||
|
raise ValueError('Not found')
|
||||||
|
ensure(self.ci(coin_to).verifyKey(mercy_keyshare), 'Invalid keyshare')
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning('Could not extract mercy output from swipe tx: {}, {}'.format(spend_txid_hex, e))
|
||||||
|
|
||||||
|
if mercy_keyshare is None:
|
||||||
bid.setState(BidStates.XMR_SWAP_FAILED_SWIPED)
|
bid.setState(BidStates.XMR_SWAP_FAILED_SWIPED)
|
||||||
|
else:
|
||||||
|
delay = self.get_delay_event_seconds()
|
||||||
|
self.log.info('Redeeming coin b lock tx for bid %s in %d seconds', bid_id.hex(), delay)
|
||||||
|
self.createActionInSession(delay, ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session)
|
||||||
|
else:
|
||||||
|
bid.setState(BidStates.XMR_SWAP_FAILED_SWIPED)
|
||||||
|
|
||||||
|
if TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns:
|
||||||
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE] = SwapTx(
|
||||||
|
bid_id=bid_id,
|
||||||
|
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE,
|
||||||
|
txid=spending_txid,
|
||||||
|
)
|
||||||
|
if mercy_keyshare:
|
||||||
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE].tx_data = mercy_keyshare
|
||||||
|
|
||||||
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
|
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -4628,15 +4659,15 @@ class BasicSwap(BaseApp):
|
||||||
if bid_id not in self.swaps_in_progress:
|
if bid_id not in self.swaps_in_progress:
|
||||||
self.log.warning('Could not find active bid for found mercy tx: {}'.format(bid_id.hex()))
|
self.log.warning('Could not find active bid for found mercy tx: {}'.format(bid_id.hex()))
|
||||||
else:
|
else:
|
||||||
remote_keyshare = bytes.fromhex(tx['vout'][0]['scriptPubKey']['asm'].split(' ')[2])
|
mercy_keyshare = bytes.fromhex(tx['vout'][0]['scriptPubKey']['asm'].split(' ')[2])
|
||||||
ensure(ci.verifyKey(remote_keyshare), 'Invalid keyshare')
|
ensure(ci.verifyKey(mercy_keyshare), 'Invalid keyshare')
|
||||||
|
|
||||||
bid = self.swaps_in_progress[bid_id][0]
|
bid = self.swaps_in_progress[bid_id][0]
|
||||||
bid.txns[TxTypes.BCH_MERCY] = SwapTx(
|
bid.txns[TxTypes.BCH_MERCY] = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
tx_type=TxTypes.BCH_MERCY,
|
tx_type=TxTypes.BCH_MERCY,
|
||||||
txid=txid,
|
txid=txid,
|
||||||
tx_data=remote_keyshare,
|
tx_data=mercy_keyshare,
|
||||||
)
|
)
|
||||||
self.saveBid(bid_id, bid)
|
self.saveBid(bid_id, bid)
|
||||||
|
|
||||||
|
@ -6168,15 +6199,19 @@ class BasicSwap(BaseApp):
|
||||||
if lock_tx_depth < ci_to.depth_spendable():
|
if lock_tx_depth < ci_to.depth_spendable():
|
||||||
raise TemporaryError(f'Chain B lock tx depth {lock_tx_depth} < required for spending.')
|
raise TemporaryError(f'Chain B lock tx depth {lock_tx_depth} < required for spending.')
|
||||||
|
|
||||||
# Extract the leader's decrypted signature and use it to recover the follower's privatekey
|
|
||||||
xmr_swap.al_lock_spend_tx_sig = ci_from.extractLeaderSig(xmr_swap.a_lock_spend_tx)
|
|
||||||
|
|
||||||
if TxTypes.BCH_MERCY in bid.txns:
|
if TxTypes.BCH_MERCY in bid.txns:
|
||||||
self.log.info('Using keyshare from mercy tx.')
|
self.log.info('Using keyshare from mercy tx.')
|
||||||
kbsf = bid.txns[TxTypes.BCH_MERCY].tx_data
|
kbsf = bid.txns[TxTypes.BCH_MERCY].tx_data
|
||||||
pkbsf = ci_to.getPubkey(kbsf)
|
pkbsf = ci_to.getPubkey(kbsf)
|
||||||
ensure(pkbsf == xmr_swap.pkbsf, 'Keyshare from mercy tx does not match expected pubkey')
|
ensure(pkbsf == xmr_swap.pkbsf, 'Keyshare from mercy tx does not match expected pubkey')
|
||||||
|
elif TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE in bid.txns:
|
||||||
|
self.log.info('Using keyshare from swipe tx.')
|
||||||
|
kbsf = bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE].tx_data
|
||||||
|
pkbsf = ci_to.getPubkey(kbsf)
|
||||||
|
ensure(pkbsf == xmr_swap.pkbsf, 'Keyshare from swipe tx does not match expected pubkey')
|
||||||
else:
|
else:
|
||||||
|
# Extract the leader's decrypted signature and use it to recover the follower's privatekey
|
||||||
|
xmr_swap.al_lock_spend_tx_sig = ci_from.extractLeaderSig(xmr_swap.a_lock_spend_tx)
|
||||||
kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf)
|
kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf)
|
||||||
assert (kbsf is not None)
|
assert (kbsf is not None)
|
||||||
|
|
||||||
|
@ -6201,7 +6236,6 @@ class BasicSwap(BaseApp):
|
||||||
if num_retries > 0:
|
if num_retries > 0:
|
||||||
error_msg += ', retry no. {}'.format(num_retries)
|
error_msg += ', retry no. {}'.format(num_retries)
|
||||||
self.log.error(error_msg)
|
self.log.error(error_msg)
|
||||||
self.log.error(traceback.format_exc()) # [rm]
|
|
||||||
|
|
||||||
if num_retries < 100 and (ci_to.is_transient_error(ex) or self.is_transient_error(ex)):
|
if num_retries < 100 and (ci_to.is_transient_error(ex) or self.is_transient_error(ex)):
|
||||||
delay = self.get_delay_retry_seconds()
|
delay = self.get_delay_retry_seconds()
|
||||||
|
@ -7912,7 +7946,7 @@ class BasicSwap(BaseApp):
|
||||||
kbsf = None
|
kbsf = None
|
||||||
# BCH sends a separate mercy tx
|
# BCH sends a separate mercy tx
|
||||||
else:
|
else:
|
||||||
for_ed25519: bool = True if ci.curve_type() == Curves.ed25519 else False
|
for_ed25519: bool = True if self.ci(coin_to).curve_type() == Curves.ed25519 else False
|
||||||
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSF, for_ed25519)
|
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSF, for_ed25519)
|
||||||
|
|
||||||
pkh_dest = ci.decodeAddress(self.getReceiveAddressForCoin(ci.coin_type()))
|
pkh_dest = ci.decodeAddress(self.getReceiveAddressForCoin(ci.coin_type()))
|
||||||
|
|
|
@ -75,6 +75,7 @@ from basicswap.contrib.test_framework.script import (
|
||||||
OP_CHECKSEQUENCEVERIFY,
|
OP_CHECKSEQUENCEVERIFY,
|
||||||
OP_DROP,
|
OP_DROP,
|
||||||
OP_HASH160, OP_EQUAL,
|
OP_HASH160, OP_EQUAL,
|
||||||
|
OP_RETURN,
|
||||||
SIGHASH_ALL,
|
SIGHASH_ALL,
|
||||||
SegwitV0SignatureHash,
|
SegwitV0SignatureHash,
|
||||||
)
|
)
|
||||||
|
@ -258,6 +259,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
self._sc = swap_client
|
self._sc = swap_client
|
||||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||||
self._expect_seedid_hex = None
|
self._expect_seedid_hex = None
|
||||||
|
self._altruistic = coin_settings.get('altruistic', True)
|
||||||
|
|
||||||
def open_rpc(self, wallet=None):
|
def open_rpc(self, wallet=None):
|
||||||
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
||||||
|
@ -601,7 +603,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
|
|
||||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None):
|
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None, kbsf=None):
|
||||||
# lock refund swipe tx
|
# lock refund swipe tx
|
||||||
# Sends the coinA locked coin to the follower
|
# Sends the coinA locked coin to the follower
|
||||||
|
|
||||||
|
@ -625,6 +627,10 @@ class BTCInterface(Secp256k1Interface):
|
||||||
|
|
||||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
|
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
|
||||||
|
|
||||||
|
if self._altruistic and kbsf:
|
||||||
|
# Add mercy_keyshare
|
||||||
|
tx.vout.append(self.txoType()(0, CScript([OP_RETURN, b'XBSW', kbsf])))
|
||||||
|
|
||||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
||||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||||
|
@ -1492,6 +1498,18 @@ class BTCInterface(Secp256k1Interface):
|
||||||
'amount': txjs['vout'][n]['value']
|
'amount': txjs['vout'][n]['value']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def inspectSwipeTx(self, tx: dict) -> bytes | None:
|
||||||
|
mercy_keyshare = None
|
||||||
|
for vout in tx['vout']:
|
||||||
|
script_bytes = bytes.fromhex(vout['scriptPubKey']['hex'])
|
||||||
|
if len(script_bytes) < 39:
|
||||||
|
continue
|
||||||
|
if script_bytes[0] != OP_RETURN:
|
||||||
|
continue
|
||||||
|
script_bytes[0]
|
||||||
|
return script_bytes[7: 7 + 32]
|
||||||
|
return None
|
||||||
|
|
||||||
def isTxExistsError(self, err_str: str) -> bool:
|
def isTxExistsError(self, err_str: str) -> bool:
|
||||||
return 'Transaction already in block chain' in err_str
|
return 'Transaction already in block chain' in err_str
|
||||||
|
|
||||||
|
|
|
@ -262,6 +262,7 @@ class DCRInterface(Secp256k1Interface):
|
||||||
|
|
||||||
self._use_segwit = True # Decred is natively segwit
|
self._use_segwit = True # Decred is natively segwit
|
||||||
self._connection_type = coin_settings['connection_type']
|
self._connection_type = coin_settings['connection_type']
|
||||||
|
self._altruistic = coin_settings.get('altruistic', True)
|
||||||
|
|
||||||
def open_rpc(self):
|
def open_rpc(self):
|
||||||
return openrpc(self._rpcport, self._rpcauth, host=self._rpc_host)
|
return openrpc(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||||
|
@ -1232,7 +1233,7 @@ class DCRInterface(Secp256k1Interface):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None):
|
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None, kbsf=None):
|
||||||
# lock refund swipe tx
|
# lock refund swipe tx
|
||||||
# Sends the coinA locked coin to the follower
|
# Sends the coinA locked coin to the follower
|
||||||
|
|
||||||
|
|
|
@ -668,7 +668,7 @@ class NAVInterface(BTCInterface):
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
|
|
||||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None):
|
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None, kbsf=None):
|
||||||
# lock refund swipe tx
|
# lock refund swipe tx
|
||||||
# Sends the coinA locked coin to the follower
|
# Sends the coinA locked coin to the follower
|
||||||
|
|
||||||
|
|
|
@ -622,7 +622,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv):
|
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv, kbsf=None):
|
||||||
# lock refund swipe tx
|
# lock refund swipe tx
|
||||||
# Sends the coinA locked coin to the follower
|
# Sends the coinA locked coin to the follower
|
||||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [tx_lock_refund_bytes.hex()])
|
lock_refund_tx_obj = self.rpc('decoderawtransaction', [tx_lock_refund_bytes.hex()])
|
||||||
|
|
|
@ -268,7 +268,7 @@ class TestFunctions(BaseTest):
|
||||||
logging.info('---------- Test {} to {} follower recovers coin a lock tx{}'.format(coin_from.name, coin_to.name, ' (with mercy tx)' if with_mercy else ''))
|
logging.info('---------- Test {} to {} follower recovers coin a lock tx{}'.format(coin_from.name, coin_to.name, ' (with mercy tx)' if with_mercy else ''))
|
||||||
|
|
||||||
# Leader is too slow to recover the coin a lock tx and follower swipes it
|
# Leader is too slow to recover the coin a lock tx and follower swipes it
|
||||||
# coin b lock tx remains unspent
|
# Coin B lock tx remains unspent unless a mercy output revealing the follower's keyshare is sent
|
||||||
|
|
||||||
id_offerer: int = self.node_a_id
|
id_offerer: int = self.node_a_id
|
||||||
id_bidder: int = self.node_b_id
|
id_bidder: int = self.node_b_id
|
||||||
|
@ -282,6 +282,8 @@ class TestFunctions(BaseTest):
|
||||||
id_follower: int = id_offerer if reverse_bid else id_bidder
|
id_follower: int = id_offerer if reverse_bid else id_bidder
|
||||||
logging.info(f'Offerer, bidder, leader, follower: {id_offerer}, {id_bidder}, {id_leader}, {id_follower}')
|
logging.info(f'Offerer, bidder, leader, follower: {id_offerer}, {id_bidder}, {id_leader}, {id_follower}')
|
||||||
|
|
||||||
|
swap_clients[id_follower].ci(coin_from if reverse_bid else coin_to)._altruistic = with_mercy
|
||||||
|
|
||||||
js_w0_before = read_json_api(1800 + id_offerer, 'wallets')
|
js_w0_before = read_json_api(1800 + id_offerer, 'wallets')
|
||||||
js_w1_before = read_json_api(1800 + id_bidder, 'wallets')
|
js_w1_before = read_json_api(1800 + id_bidder, 'wallets')
|
||||||
|
|
||||||
|
@ -941,6 +943,17 @@ class BasicSwapTest(TestFunctions):
|
||||||
def test_03_d_follower_recover_a_lock_tx_from_part(self):
|
def test_03_d_follower_recover_a_lock_tx_from_part(self):
|
||||||
self.do_test_03_follower_recover_a_lock_tx(Coins.PART, self.test_coin_from)
|
self.do_test_03_follower_recover_a_lock_tx(Coins.PART, self.test_coin_from)
|
||||||
|
|
||||||
|
def test_03_e_follower_recover_a_lock_tx_mercy_release(self):
|
||||||
|
if not self.has_segwit:
|
||||||
|
return
|
||||||
|
self.do_test_03_follower_recover_a_lock_tx(self.test_coin_from, Coins.XMR, with_mercy=True)
|
||||||
|
|
||||||
|
def test_03_f_follower_recover_a_lock_tx_mercy_release_reverse(self):
|
||||||
|
if not self.has_segwit:
|
||||||
|
return
|
||||||
|
self.prepare_balance(Coins.XMR, 100.0, 1800, 1801)
|
||||||
|
self.do_test_03_follower_recover_a_lock_tx(Coins.XMR, self.test_coin_from, with_mercy=True)
|
||||||
|
|
||||||
def test_04_a_follower_recover_b_lock_tx(self):
|
def test_04_a_follower_recover_b_lock_tx(self):
|
||||||
if not self.has_segwit:
|
if not self.has_segwit:
|
||||||
return
|
return
|
||||||
|
|
|
@ -1031,6 +1031,8 @@ class Test(BaseTest):
|
||||||
logging.info('---------- Test PART to XMR follower recovers coin a lock tx')
|
logging.info('---------- Test PART to XMR follower recovers coin a lock tx')
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
|
|
||||||
|
swap_clients[1].ci(Coins.PART)._altruistic = False
|
||||||
|
|
||||||
offer_id = swap_clients[0].postOffer(
|
offer_id = swap_clients[0].postOffer(
|
||||||
Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
|
Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
|
||||||
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=16)
|
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=16)
|
||||||
|
@ -1062,6 +1064,39 @@ class Test(BaseTest):
|
||||||
bidder_states = [s for s in bidder_states if s[1] != 'Bid Stalled (debug)']
|
bidder_states = [s for s in bidder_states if s[1] != 'Bid Stalled (debug)']
|
||||||
assert (compare_bid_states(bidder_states, self.states_bidder[2]) is True)
|
assert (compare_bid_states(bidder_states, self.states_bidder[2]) is True)
|
||||||
|
|
||||||
|
def test_03b_follower_recover_a_lock_tx_with_mercy(self):
|
||||||
|
logging.info('---------- Test PART to XMR follower recovers coin a lock tx with mercy output')
|
||||||
|
swap_clients = self.swap_clients
|
||||||
|
|
||||||
|
swap_clients[1].ci(Coins.PART)._altruistic = True
|
||||||
|
|
||||||
|
offer_id = swap_clients[0].postOffer(
|
||||||
|
Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
|
||||||
|
lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=16)
|
||||||
|
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
|
||||||
|
offer = swap_clients[1].getOffer(offer_id)
|
||||||
|
|
||||||
|
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
|
||||||
|
|
||||||
|
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
|
||||||
|
|
||||||
|
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
|
||||||
|
assert (xmr_swap)
|
||||||
|
|
||||||
|
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.BID_DONT_SPEND_COIN_B_LOCK)
|
||||||
|
swap_clients[0].setBidDebugInd(bid_id, DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND2)
|
||||||
|
|
||||||
|
swap_clients[0].setBidDebugInd(bid_id, DebugTypes.WAIT_FOR_COIN_B_LOCK_BEFORE_REFUND, False)
|
||||||
|
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.WAIT_FOR_COIN_B_LOCK_BEFORE_REFUND, False)
|
||||||
|
|
||||||
|
swap_clients[0].acceptXmrBid(bid_id)
|
||||||
|
|
||||||
|
wait_for_bid(test_delay_event, swap_clients[0], bid_id, (BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED, BidStates.SWAP_COMPLETED), wait_for=220)
|
||||||
|
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_SWIPED, wait_for=120, sent=True)
|
||||||
|
|
||||||
|
wait_for_none_active(test_delay_event, 1800)
|
||||||
|
wait_for_none_active(test_delay_event, 1801)
|
||||||
|
|
||||||
def test_04_follower_recover_b_lock_tx(self):
|
def test_04_follower_recover_b_lock_tx(self):
|
||||||
logging.info('---------- Test PART to XMR follower recovers coin b lock tx')
|
logging.info('---------- Test PART to XMR follower recovers coin b lock tx')
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue