diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index a37e7eb..8ac455c 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -138,7 +138,6 @@ from .basicswap_util import ( describeEventEntry, getVoutByAddress, getVoutByP2WSH, - replaceAddrPrefix, getOfferProofOfFundsHash, getLastBidState, isActiveBidState, @@ -2893,8 +2892,6 @@ class BasicSwap(BaseApp): if self.coin_clients[coin_type]['connection_type'] != 'rpc': return None - prevout_s = ' in={}:{}'.format(prev_txnid, prev_n) - if fee_rate is None: fee_rate, fee_src = self.getFeeRateForCoin(coin_type) @@ -2910,37 +2907,30 @@ class BasicSwap(BaseApp): addr_redeem_out = self.getReceiveAddressFromPool(coin_type, bid.bid_id, TxTypes.PTX_REDEEM if for_txn_type == 'participate' else TxTypes.ITX_REDEEM) assert (addr_redeem_out is not None) - if self.coin_clients[coin_type]['use_segwit']: - # Change to part hrp - addr_redeem_out = self.ci(Coins.PART).encodeSegwitAddress(ci.decodeSegwitAddress(addr_redeem_out)) - else: - addr_redeem_out = replaceAddrPrefix(addr_redeem_out, Coins.PART, self.chain) self.log.debug('addr_redeem_out %s', addr_redeem_out) - output_to = ' outaddr={}:{}'.format(ci.format_amount(amount_out), addr_redeem_out) - if coin_type == Coins.PART: - redeem_txn = self.calltx('-create' + prevout_s + output_to) - else: - redeem_txn = self.calltx('-btcmode -create nversion=2' + prevout_s + output_to) + redeem_txn = ci.createRedeemTxn(prevout, addr_redeem_out, amount_out) options = {} if self.coin_clients[coin_type]['use_segwit']: options['force_segwit'] = True + redeem_sig = self.callcoinrpc(Coins.PART, 'createsignaturewithkey', [redeem_txn, prevout, privkey, 'ALL', options]) + if coin_type == Coins.PART or self.coin_clients[coin_type]['use_segwit']: witness_stack = [ - redeem_sig, - pubkey.hex(), - secret.hex(), - '01', - txn_script.hex()] - redeem_txn = self.calltx(redeem_txn + ' witness=0:' + ':'.join(witness_stack)) + bytes.fromhex(redeem_sig), + pubkey, + secret, + bytes((1,)), + txn_script] + redeem_txn = ci.setTxSignature(bytes.fromhex(redeem_txn), witness_stack).hex() else: script = format(len(redeem_sig) // 2, '02x') + redeem_sig script += format(33, '02x') + pubkey.hex() script += format(32, '02x') + secret.hex() script += format(OpCodes.OP_1, '02x') script += format(OpCodes.OP_PUSHDATA1, '02x') + format(len(txn_script), '02x') + txn_script.hex() - redeem_txn = self.calltx(redeem_txn + ' scriptsig=0:' + script) + redeem_txn = ci.setTxScriptSig(bytes.fromhex(redeem_txn), 0, bytes.fromhex(script)).hex() ro = self.callcoinrpc(Coins.PART, 'verifyrawtransaction', [redeem_txn, [prevout]]) ensure(ro['inputs_valid'] is True, 'inputs_valid is false') @@ -2991,11 +2981,9 @@ class BasicSwap(BaseApp): 'amount': prev_amount} lock_value = DeserialiseNum(txn_script, 64) + sequence: int = 1 if offer.lock_type < TxLockTypes.ABS_LOCK_BLOCKS: sequence = lock_value - else: - sequence = 1 - prevout_s = ' in={}:{}:{}'.format(txjs['txid'], vout, sequence) fee_rate, fee_src = self.getFeeRateForCoin(coin_type) @@ -3012,21 +3000,12 @@ class BasicSwap(BaseApp): if addr_refund_out is None: addr_refund_out = self.getReceiveAddressFromPool(coin_type, bid.bid_id, tx_type) ensure(addr_refund_out is not None, 'addr_refund_out is null') - if self.coin_clients[coin_type]['use_segwit']: - # Change to part hrp - addr_refund_out = self.ci(Coins.PART).encodeSegwitAddress(ci.decodeSegwitAddress(addr_refund_out)) - else: - addr_refund_out = replaceAddrPrefix(addr_refund_out, Coins.PART, self.chain) self.log.debug('addr_refund_out %s', addr_refund_out) - output_to = ' outaddr={}:{}'.format(ci.format_amount(amount_out), addr_refund_out) - if coin_type == Coins.PART: - refund_txn = self.calltx('-create' + prevout_s + output_to) - else: - refund_txn = self.calltx('-btcmode -create nversion=2' + prevout_s + output_to) - + locktime: int = 0 if offer.lock_type == TxLockTypes.ABS_LOCK_BLOCKS or offer.lock_type == TxLockTypes.ABS_LOCK_TIME: - refund_txn = self.calltx('{} locktime={}'.format(refund_txn, lock_value)) + locktime = lock_value + refund_txn = ci.createRefundTxn(prevout, addr_refund_out, amount_out, locktime, sequence) options = {} if self.coin_clients[coin_type]['use_segwit']: @@ -3034,17 +3013,17 @@ class BasicSwap(BaseApp): refund_sig = self.callcoinrpc(Coins.PART, 'createsignaturewithkey', [refund_txn, prevout, privkey, 'ALL', options]) if coin_type == Coins.PART or self.coin_clients[coin_type]['use_segwit']: witness_stack = [ - refund_sig, - pubkey.hex(), - '', # SCRIPT_VERIFY_MINIMALIF - txn_script.hex()] - refund_txn = self.calltx(refund_txn + ' witness=0:' + ':'.join(witness_stack)) + bytes.fromhex(refund_sig), + pubkey, + b'', + txn_script] + refund_txn = ci.setTxSignature(bytes.fromhex(refund_txn), witness_stack).hex() else: script = format(len(refund_sig) // 2, '02x') + refund_sig script += format(33, '02x') + pubkey.hex() script += format(OpCodes.OP_0, '02x') script += format(OpCodes.OP_PUSHDATA1, '02x') + format(len(txn_script), '02x') + txn_script.hex() - refund_txn = self.calltx(refund_txn + ' scriptsig=0:' + script) + refund_txn = ci.setTxScriptSig(bytes.fromhex(refund_txn), 0, bytes.fromhex(script)).hex() ro = self.callcoinrpc(Coins.PART, 'verifyrawtransaction', [refund_txn, [prevout]]) ensure(ro['inputs_valid'] is True, 'inputs_valid is false') diff --git a/basicswap/interface/btc.py b/basicswap/interface/btc.py index 1f3b3c3..6a3efa4 100644 --- a/basicswap/interface/btc.py +++ b/basicswap/interface/btc.py @@ -968,13 +968,18 @@ class BTCInterface(CoinInterface): # TODO: filter errors return None - def setTxSignature(self, tx_bytes, stack) -> bytes: + def setTxSignature(self, tx_bytes: bytes, stack) -> bytes: tx = self.loadTx(tx_bytes) tx.wit.vtxinwit.clear() tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = stack return tx.serialize() + def setTxScriptSig(self, tx_bytes: bytes, input_no: int, script_sig: bytes) -> bytes: + tx = self.loadTx(tx_bytes) + tx.vin[0].scriptSig = script_sig + return tx.serialize() + def stripTxSignature(self, tx_bytes) -> bytes: tx = self.loadTx(tx_bytes) tx.wit.vtxinwit.clear() @@ -1389,6 +1394,29 @@ class BTCInterface(CoinInterface): return {'txid': txid_hex, 'amount': 0, 'height': rv['blockheight']} return None + def createRedeemTxn(self, prevout, output_addr: str, output_value: int) -> str: + tx = CTransaction() + tx.nVersion = self.txVersion() + prev_txid = uint256_from_str(bytes.fromhex(prevout['txid'])[::-1]) + tx.vin.append(CTxIn(COutPoint(prev_txid, prevout['vout']))) + pkh = self.decodeAddress(output_addr) + script = self.getScriptForPubkeyHash(pkh) + tx.vout.append(self.txoType()(output_value, script)) + tx.rehash() + return tx.serialize().hex() + + def createRefundTxn(self, prevout, output_addr: str, output_value: int, locktime: int, sequence: int) -> str: + tx = CTransaction() + tx.nVersion = self.txVersion() + tx.nLockTime = locktime + prev_txid = uint256_from_str(bytes.fromhex(prevout['txid'])[::-1]) + tx.vin.append(CTxIn(COutPoint(prev_txid, prevout['vout']), nSequence=sequence,)) + pkh = self.decodeAddress(output_addr) + script = self.getScriptForPubkeyHash(pkh) + tx.vout.append(self.txoType()(output_value, script)) + tx.rehash() + return tx.serialize().hex() + def testBTCInterface(): print('TODO: testBTCInterface') diff --git a/basicswap/interface/part.py b/basicswap/interface/part.py index 79ebe4f..3c045e2 100644 --- a/basicswap/interface/part.py +++ b/basicswap/interface/part.py @@ -102,7 +102,7 @@ class PARTInterface(BTCInterface): {'conf_target': self._conf_target}] return self.rpc_callback('sendtypeto', params) - def getScriptForPubkeyHash(self, pkh): + def getScriptForPubkeyHash(self, pkh: bytes) -> CScript: return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]) def formatStealthAddress(self, scan_pubkey, spend_pubkey) -> str: diff --git a/doc/release-notes.md b/doc/release-notes.md index e7974ef..27a7ad8 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -7,7 +7,8 @@ - Accepted bids will timeout if the peer does not respond within an hour after the bid expires. - Ensure messages are always sent from and to the expected addresses. -- ui: Add pagination and filters to smsgaddresses page +- ui: Add pagination and filters to smsgaddresses page. +- Removed dependency on particl-tx. 0.0.59 diff --git a/tests/basicswap/extended/test_dash.py b/tests/basicswap/extended/test_dash.py index 1498a81..6e6b541 100644 --- a/tests/basicswap/extended/test_dash.py +++ b/tests/basicswap/extended/test_dash.py @@ -531,9 +531,14 @@ class Test(unittest.TestCase): wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 - swap_clients[0].getChainClientSettings(Coins.DASH)['override_feerate'] = 10.0 - wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + try: + swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 + swap_clients[0].getChainClientSettings(Coins.DASH)['override_feerate'] = 10.0 + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + swap_clients[0].abandonBid(bid_id) + finally: + del swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] + del swap_clients[0].getChainClientSettings(Coins.DASH)['override_feerate'] def test_08_wallet(self): logging.info('---------- Test {} wallet'.format(self.test_coin_from.name)) diff --git a/tests/basicswap/extended/test_firo.py b/tests/basicswap/extended/test_firo.py index 51a32c1..eb92227 100644 --- a/tests/basicswap/extended/test_firo.py +++ b/tests/basicswap/extended/test_firo.py @@ -396,9 +396,14 @@ class Test(BaseTest): wait_for_bid(test_delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 - swap_clients[0].getChainClientSettings(Coins.FIRO)['override_feerate'] = 10.0 - wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + try: + swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 + swap_clients[0].getChainClientSettings(Coins.FIRO)['override_feerate'] = 10.0 + wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + swap_clients[0].abandonBid(bid_id) + finally: + del swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] + del swap_clients[0].getChainClientSettings(Coins.FIRO)['override_feerate'] def test_08_wallet(self): logging.info('---------- Test {} wallet'.format(self.test_coin_from.name)) diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py index 107b824..2076ad1 100644 --- a/tests/basicswap/extended/test_nmc.py +++ b/tests/basicswap/extended/test_nmc.py @@ -512,9 +512,14 @@ class Test(unittest.TestCase): wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 - swap_clients[0].getChainClientSettings(Coins.NMC)['override_feerate'] = 10.0 - wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + try: + swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 + swap_clients[0].getChainClientSettings(Coins.NMC)['override_feerate'] = 10.0 + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + swap_clients[0].abandonBid(bid_id) + finally: + del swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] + del swap_clients[0].getChainClientSettings(Coins.NMC)['override_feerate'] def pass_99_delay(self): global stop_test diff --git a/tests/basicswap/extended/test_pivx.py b/tests/basicswap/extended/test_pivx.py index 9f6d5ee..76765f6 100644 --- a/tests/basicswap/extended/test_pivx.py +++ b/tests/basicswap/extended/test_pivx.py @@ -541,9 +541,14 @@ class Test(unittest.TestCase): wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 - swap_clients[0].getChainClientSettings(Coins.PIVX)['override_feerate'] = 10.0 - wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + try: + swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 + swap_clients[0].getChainClientSettings(Coins.PIVX)['override_feerate'] = 10.0 + wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + swap_clients[0].abandonBid(bid_id) + finally: + del swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] + del swap_clients[0].getChainClientSettings(Coins.PIVX)['override_feerate'] def test_08_wallet(self): logging.info('---------- Test {} wallet'.format(self.test_coin_from.name)) diff --git a/tests/basicswap/test_run.py b/tests/basicswap/test_run.py index 1646ed3..fcb2a61 100644 --- a/tests/basicswap/test_run.py +++ b/tests/basicswap/test_run.py @@ -350,14 +350,15 @@ class Test(BaseTest): wait_for_bid(test_delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) - swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 - swap_clients[0].getChainClientSettings(Coins.LTC)['override_feerate'] = 10.0 - wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) - - swap_clients[0].abandonBid(bid_id) - del swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] - del swap_clients[0].getChainClientSettings(Coins.LTC)['override_feerate'] + try: + swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] = 10.0 + swap_clients[0].getChainClientSettings(Coins.LTC)['override_feerate'] = 10.0 + wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60) + swap_clients[0].abandonBid(bid_id) + finally: + del swap_clients[0].getChainClientSettings(Coins.BTC)['override_feerate'] + del swap_clients[0].getChainClientSettings(Coins.LTC)['override_feerate'] def test_08_part_ltc_buyer_first(self): logging.info('---------- Test PART to LTC, buyer first')