diff --git a/.cirrus.yml b/.cirrus.yml index ce6d099..1c3fa8b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,7 +6,7 @@ lint_task: - pip install flake8 codespell script: - flake8 --version - - PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,messages_pb2.py,.eggs,.tox,bin/install_certifi.py + - PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503,E702,E131 --exclude=basicswap/contrib,basicswap/interface/contrib,messages_pb2.py,.eggs,.tox,bin/install_certifi.py - codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py,*basicswap/static test_task: diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index b159f8b..85812b6 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -47,7 +47,6 @@ from .util import ( DeserialiseNum, h2b, i2b, - i2h, zeroIfNone, make_int, ensure, @@ -3009,7 +3008,6 @@ class BasicSwap(BaseApp): lockExtraArgs['public_key'] = xmr_swap.pkal lockExtraArgs['timelock'] = xmr_offer.lock_time_1 - xmr_swap.a_lock_tx_script = pi.genScriptLockTxScript(ci_from, xmr_swap.pkal, xmr_swap.pkaf, **lockExtraArgs) prefunded_tx = self.getPreFundedTx(Concepts.OFFER, bid.offer_id, TxTypes.ITX_PRE_FUNDED, session=use_session) if prefunded_tx: @@ -3950,7 +3948,7 @@ class BasicSwap(BaseApp): return rv if 'txid' in lock_tx_chain_info and lock_tx_chain_info['txid'] != b2h(xmr_swap.a_lock_tx_id): - # if we find that txid was changed (by funding or otherwise), we need to update it to track correctly + # if we find that txid was changed (by funding or otherwise), we need to update it to track correctly xmr_swap.a_lock_tx_id = h2b(lock_tx_chain_info['txid']) xmr_swap.a_lock_tx = h2b(lock_tx_chain_info['txhex']) @@ -3972,7 +3970,7 @@ class BasicSwap(BaseApp): # update watcher self.watchXmrSwap(bid, offer, xmr_swap, session) bid_changed = True - + if bid.xmr_a_lock_tx.state == TxStates.TX_NONE and lock_tx_chain_info['height'] == 0: bid.xmr_a_lock_tx.setState(TxStates.TX_IN_MEMPOOL) @@ -4416,7 +4414,7 @@ class BasicSwap(BaseApp): ci_from = self.ci(coin_from) spend_tx = ci_from.loadTx(h2b(spend_txn_hex)) - + bid.xmr_a_lock_tx.spend_txid = spending_txid is_spending_lock_tx = False diff --git a/basicswap/interface/bch.py b/basicswap/interface/bch.py index 72854ac..8d6134f 100644 --- a/basicswap/interface/bch.py +++ b/basicswap/interface/bch.py @@ -57,6 +57,7 @@ from coincurve.ecdsaotves import ( ecdsaotves_rec_enc_key, ) + class BCHInterface(BTCInterface): @staticmethod def coin_type(): @@ -130,27 +131,19 @@ class BCHInterface(BTCInterface): # Return P2PKH return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]) - # def getScriptDest(self, script: bytearray) -> bytearray: - # # P2SH - - # script_hash = hash160(script) - # assert len(script_hash) == 20 - - # return CScript([OP_HASH160, script_hash, OP_EQUAL]) - def encodeScriptDest(self, script_dest: bytes) -> str: # Extract hash from script script_hash = script_dest[2:-1] return self.sh_to_address(script_hash) - + def sh_to_address(self, sh: bytes) -> str: assert (len(sh) == 20 or len(sh) == 32) network = self._network.upper() address = None if len(sh) == 20: - address = Address("P2SH20" if network == "MAINNET" else "P2SH20-"+network, sh) + address = Address("P2SH20" if network == "MAINNET" else "P2SH20-" + network, sh) else: - address = Address("P2SH32" if network == "MAINNET" else "P2SH32-"+network, sh) + address = Address("P2SH32" if network == "MAINNET" else "P2SH32-" + network, sh) return address.cash_address() @@ -165,9 +158,6 @@ class BCHInterface(BTCInterface): params = [addr_to, value, '', '', subfee, 0, False] return self.rpc_wallet('sendtoaddress', params) - def getSpendableBalance(self) -> int: - return self.make_int(self.rpc('getwalletinfo')['unconfirmed_balance']) - def getBLockSpendTxFee(self, tx, fee_rate: int) -> int: add_bytes = 107 size = len(tx.serialize_with_witness()) + add_bytes @@ -188,7 +178,7 @@ class BCHInterface(BTCInterface): return None def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False, vout: int = -1): - # Add watchonly address and rescan if required + # Add watchonly address and rescan if required txid = None # first lookup by dest_address @@ -252,10 +242,10 @@ class BCHInterface(BTCInterface): return CScript([ # // v4.1.0-CashTokens-Optimized # // Based on swaplock.cash v4.1.0-CashTokens - # + # # // Alice has XMR, wants BCH and/or CashTokens. # // Bob has BCH and/or CashTokens, wants XMR. - # + # # // Verify 1-in-1-out TX form OP_TXINPUTCOUNT, OP_1, OP_NUMEQUALVERIFY, @@ -305,7 +295,7 @@ class BCHInterface(BTCInterface): # // Refund will become available when timelock expires, and it would # // expire because Alice didn't collect on time, either of her own accord - # // or because Bob bailed out and witheld the encrypted signature. + # // or because Bob bailed out and withheld the encrypted signature. OP_ELSE, # // int timelock_0 timelock, @@ -340,7 +330,7 @@ class BCHInterface(BTCInterface): def addressToLockingBytecode(self, address: str) -> bytes: return b'\x76\xa9\x14' + bytes(Address.from_string(address).payload) + b'\x88\xac' - + def getSpendableBalance(self) -> int: return self.make_int(self.rpc_wallet('getbalance', ["*", 1, False])) @@ -353,22 +343,13 @@ class BCHInterface(BTCInterface): sha256(sha256(script)), OP_EQUAL, ]) - + def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes = None) -> bytes: tx = CTransaction() tx.nVersion = self.txVersion() tx.vout.append(self.txoType()(value, self.getScriptDest(script))) return tx.serialize_without_witness() - def getScriptForPubkeyHash(self, pkh: bytes) -> CScript: - return CScript([ - OP_DUP, - OP_HASH160, - pkh, - OP_EQUALVERIFY, - OP_CHECKSIG, - ]) - def getTxSize(self, tx: CTransaction) -> int: return len(tx.serialize_without_witness()) @@ -496,7 +477,7 @@ class BCHInterface(BTCInterface): def verifyTxSig(self, tx_bytes: bytes, sig: bytes, K: bytes, input_n: int, prevout_script: bytes, prevout_value: int) -> bool: # simple ecdsa signature verification return self.verifyDataSig(tx_bytes, sig, K) - + def verifyDataSig(self, data: bytes, sig: bytes, K: bytes) -> bool: # simple ecdsa signature verification pubkey = PublicKey(K) @@ -505,37 +486,6 @@ class BCHInterface(BTCInterface): def setTxSignature(self, tx_bytes: bytes, stack) -> bytes: return tx_bytes - def verifySCLockTx(self, tx_bytes, script_out, - swap_value, - Kal, Kaf, - feerate, - check_lock_tx_inputs, vkbv=None): - # Verify: - # - - # Not necessary to check the lock txn is mineable, as protocol will wait for it to confirm - # However by checking early we can avoid wasting time processing unmineable txns - # Check fee is reasonable - - tx = self.loadTx(tx_bytes) - txid = self.getTxid(tx) - self._log.info('Verifying lock tx: {}.'.format(b2h(txid))) - - ensure(tx.nVersion == self.txVersion(), 'Bad version') - ensure(tx.nLockTime == 0, 'Bad nLockTime') # TODO match txns created by cores - - script_pk = self.getScriptDest(script_out) - locked_n = findOutput(tx, script_pk) - ensure(locked_n is not None, 'Output not found in tx') - locked_coin = tx.vout[locked_n].nValue - - # Check value - ensure(locked_coin == swap_value, 'Bad locked value') - - # TODO: better script matching, see interfaces/btc.py - - return txid, locked_n - def extractScriptLockScriptValuesFromScriptSig(self, script_bytes): signature, nb = decodePushData(script_bytes, 0) if nb == len(script_bytes): @@ -598,7 +548,7 @@ class BCHInterface(BTCInterface): ensure_op(script_bytes[o] == OP_EQUALVERIFY); o += 1 public_key, nb = decodePushData(script_bytes, o); o += nb ensure_op(script_bytes[o] == OP_CHECKDATASIG); o += 1 - + ensure_op(script_bytes[o] == OP_ELSE); o += 1 timelock, nb = decodeScriptNum(script_bytes, o); o += nb ensure_op(script_bytes[o] == OP_CHECKSEQUENCEVERIFY); o += 1 @@ -708,7 +658,6 @@ class BCHInterface(BTCInterface): ensure(public_key == _public_key, 'public_key mismatch') ensure(timelock == _timelock, 'timelock mismatch') - fee_paid = locked_coin - mining_fee assert (fee_paid > 0) @@ -814,7 +763,7 @@ class BCHInterface(BTCInterface): msg = sha256(out_1) return ecdsaotves_enc_sign(key_sign, pubkey_encrypt, msg) - + def decryptOtVES(self, k: bytes, esig: bytes) -> bytes: return ecdsaotves_dec_sig(k, esig) diff --git a/basicswap/util/script.py b/basicswap/util/script.py index 9e63cf5..4b929e5 100644 --- a/basicswap/util/script.py +++ b/basicswap/util/script.py @@ -32,6 +32,7 @@ def decodeScriptNum(script_bytes, o): v += int(b) << 8 * i return (v, 1 + num_len) + def decodePushData(script_bytes, o): datasize = None pushdata_type = None @@ -76,6 +77,7 @@ def decodePushData(script_bytes, o): # return data and the number of bytes to skip forward return (data, i + datasize - o) + def getP2SHScriptForHash(p2sh): return bytes((OpCodes.OP_HASH160, 0x14)) \ + p2sh \ diff --git a/tests/basicswap/test_bch.py b/tests/basicswap/test_bch.py index 32c9c93..b444478 100644 --- a/tests/basicswap/test_bch.py +++ b/tests/basicswap/test_bch.py @@ -11,21 +11,14 @@ bch_lock_swipe_script = '4c8fc3519dc4519d02e80300c600cc949d00ce00d18800cf00d2880 coin_settings = {'rpcport': 0, 'rpcauth': 'none', 'blocks_confirmed': 1, 'conf_target': 1, 'use_segwit': False, 'connection_type': 'rpc'} + class TestXmrBchSwapInterface(unittest.TestCase): - # def test_generate_script(self): - # out_1 = bytes.fromhex('a9147171b53baf87efc9c78ffc0e37a78859cebaae4a87') - # out_2 = bytes.fromhex('a9147171b53baf87efc9c78ffc0e37a78859cebaae4a87') - # public_key = bytes.fromhex('03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556') - - # ci = BCHInterface(coin_settings, "regtest") - # print(ci.genScriptLockTxScript(None, 1000, out_1, out_2, public_key, 2).hex()) - def test_extractScriptLockScriptValues(self): ci = BCHInterface(coin_settings, "regtest") script_bytes = CScript(bytes.fromhex(bch_lock_script)) ci.extractScriptLockScriptValues(script_bytes) - + script_bytes = CScript(bytes.fromhex(bch_lock_spend_script)) signature, mining_fee, out_1, out_2, public_key, timelock = ci.extractScriptLockScriptValuesFromScriptSig(script_bytes) ensure(signature is not None, 'signature not present') diff --git a/tests/basicswap/test_bch_xmr.py b/tests/basicswap/test_bch_xmr.py index 178c7de..8f2756d 100644 --- a/tests/basicswap/test_bch_xmr.py +++ b/tests/basicswap/test_bch_xmr.py @@ -54,8 +54,6 @@ class TestFunctions(BaseTest): extra_wait_time = 0 test_coin_from = Coins.BCH - - def callnoderpc(self, method, params=[], wallet=None, node_id=0): return callnoderpc(node_id, method, params, wallet, self.base_rpc_port) @@ -86,6 +84,7 @@ class TestFunctions(BaseTest): swap_clients[1].ci(Coins.XMR).setFeePriority(0) + class TestBCH(BasicSwapTest): __test__ = True test_coin_from = Coins.BCH @@ -546,4 +545,4 @@ class TestBCH(BasicSwapTest): def test_08_insufficient_funds_rev(self): self.prepare_balance(Coins.BCH, 100.0, 1801, 1800) - super().test_08_insufficient_funds_rev() \ No newline at end of file + super().test_08_insufficient_funds_rev() diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index b80e4eb..27f39c1 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -241,9 +241,11 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey, with_c def btcCli(cmd, node_id=0): return callrpc_cli(cfg.BITCOIN_BINDIR, os.path.join(TEST_DIR, 'btc_' + str(node_id)), 'regtest', cmd, cfg.BITCOIN_CLI) + def bchCli(cmd, node_id=0): return callrpc_cli(cfg.BITCOINCASH_BINDIR, os.path.join(TEST_DIR, 'bch_' + str(node_id)), 'regtest', cmd, cfg.BITCOINCASH_CLI) + def ltcCli(cmd, node_id=0): return callrpc_cli(cfg.LITECOIN_BINDIR, os.path.join(TEST_DIR, 'ltc_' + str(node_id)), 'regtest', cmd, cfg.LITECOIN_CLI)