tests: Add prefunded itx and xmr protocol tests
This commit is contained in:
		
							parent
							
								
									80df3b1a34
								
							
						
					
					
						commit
						6860279faa
					
				@ -1194,16 +1194,22 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        ensure(amount_to > ci_to.min_amount(), 'To amount below min value for chain')
 | 
			
		||||
        ensure(amount_to < ci_to.max_amount(), 'To amount above max value for chain')
 | 
			
		||||
 | 
			
		||||
    def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value):
 | 
			
		||||
    def validateOfferLockValue(self, swap_type, coin_from, coin_to, lock_type, lock_value):
 | 
			
		||||
        coin_from_has_csv = self.coin_clients[coin_from]['use_csv']
 | 
			
		||||
        coin_to_has_csv = self.coin_clients[coin_to]['use_csv']
 | 
			
		||||
 | 
			
		||||
        if lock_type == OfferMessage.SEQUENCE_LOCK_TIME:
 | 
			
		||||
            ensure(lock_value >= self.min_sequence_lock_seconds and lock_value <= self.max_sequence_lock_seconds, 'Invalid lock_value time')
 | 
			
		||||
            ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
 | 
			
		||||
            if swap_type == SwapTypes.XMR_SWAP:
 | 
			
		||||
                ensure(coin_from_has_csv, 'Coin from needs CSV activated.')
 | 
			
		||||
            else:
 | 
			
		||||
                ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
 | 
			
		||||
        elif lock_type == OfferMessage.SEQUENCE_LOCK_BLOCKS:
 | 
			
		||||
            ensure(lock_value >= 5 and lock_value <= 1000, 'Invalid lock_value blocks')
 | 
			
		||||
            ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
 | 
			
		||||
            if swap_type == SwapTypes.XMR_SWAP:
 | 
			
		||||
                ensure(coin_from_has_csv, 'Coin from needs CSV activated.')
 | 
			
		||||
            else:
 | 
			
		||||
                ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
 | 
			
		||||
        elif lock_type == TxLockTypes.ABS_LOCK_TIME:
 | 
			
		||||
            # TODO: range?
 | 
			
		||||
            ensure(not coin_from_has_csv or not coin_to_has_csv, 'Should use CSV.')
 | 
			
		||||
@ -1262,7 +1268,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
 | 
			
		||||
        self.validateSwapType(coin_from_t, coin_to_t, swap_type)
 | 
			
		||||
        self.validateOfferAmounts(coin_from_t, coin_to_t, amount, rate, min_bid_amount)
 | 
			
		||||
        self.validateOfferLockValue(coin_from_t, coin_to_t, lock_type, lock_value)
 | 
			
		||||
        self.validateOfferLockValue(swap_type, coin_from_t, coin_to_t, lock_type, lock_value)
 | 
			
		||||
        self.validateOfferValidTime(swap_type, coin_from_t, coin_to_t, valid_for_seconds)
 | 
			
		||||
 | 
			
		||||
        offer_addr_to = self.getOfferAddressTo(extra_options)
 | 
			
		||||
@ -3916,7 +3922,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
 | 
			
		||||
        self.validateSwapType(coin_from, coin_to, offer_data.swap_type)
 | 
			
		||||
        self.validateOfferAmounts(coin_from, coin_to, offer_data.amount_from, offer_data.rate, offer_data.min_bid_amount)
 | 
			
		||||
        self.validateOfferLockValue(coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
 | 
			
		||||
        self.validateOfferLockValue(offer_data.swap_type, coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
 | 
			
		||||
        self.validateOfferValidTime(offer_data.swap_type, coin_from, coin_to, offer_data.time_valid)
 | 
			
		||||
 | 
			
		||||
        ensure(msg['sent'] + offer_data.time_valid >= now, 'Offer expired')
 | 
			
		||||
 | 
			
		||||
@ -321,7 +321,7 @@ chainparams = {
 | 
			
		||||
            'rpcport': 28888,
 | 
			
		||||
            'pubkey_address': 65,
 | 
			
		||||
            'script_address': 178,
 | 
			
		||||
            'key_prefix': 185,
 | 
			
		||||
            'key_prefix': 239,
 | 
			
		||||
            'hrp': '',
 | 
			
		||||
            'bip44': 1,
 | 
			
		||||
            'min_amount': 1000,
 | 
			
		||||
 | 
			
		||||
@ -92,7 +92,7 @@ def findOutput(tx, script_pk: bytes):
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_vout_for_address_from_txobj(tx_obj, addr) -> int:
 | 
			
		||||
def find_vout_for_address_from_txobj(tx_obj, addr: str) -> int:
 | 
			
		||||
    """
 | 
			
		||||
    Locate the vout index of the given transaction sending to the
 | 
			
		||||
    given address. Raises runtime error exception if not found.
 | 
			
		||||
@ -1033,7 +1033,15 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
        return None
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
    def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
 | 
			
		||||
        witness_bytes = 109
 | 
			
		||||
        vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
 | 
			
		||||
        pay_fee = int(fee_rate * vsize // 1000)
 | 
			
		||||
        self._log.info(f'BLockSpendTx  fee_rate, vsize, fee: {fee_rate}, {vsize}, {pay_fee}.')
 | 
			
		||||
        return pay_fee
 | 
			
		||||
 | 
			
		||||
    def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int) -> bytes:
 | 
			
		||||
        self._log.info('spendBLockTx %s:\n', chain_b_lock_txid.hex())
 | 
			
		||||
        wtx = self.rpc_callback('gettransaction', [chain_b_lock_txid.hex(), ])
 | 
			
		||||
        lock_tx = self.loadTx(bytes.fromhex(wtx['hex']))
 | 
			
		||||
 | 
			
		||||
@ -1054,12 +1062,8 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
                            scriptSig=self.getScriptScriptSig(script_lock)))
 | 
			
		||||
        tx.vout.append(self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to)))
 | 
			
		||||
 | 
			
		||||
        witness_bytes = 109
 | 
			
		||||
        vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
 | 
			
		||||
        pay_fee = int(b_fee * vsize // 1000)
 | 
			
		||||
        pay_fee = self.getBLockSpendTxFee(tx, b_fee)
 | 
			
		||||
        tx.vout[0].nValue = cb_swap_value - pay_fee
 | 
			
		||||
        self._log.info('spendBLockTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
			
		||||
                       chain_b_lock_txid.hex(), b_fee, vsize, pay_fee)
 | 
			
		||||
 | 
			
		||||
        b_lock_spend_tx = tx.serialize()
 | 
			
		||||
        b_lock_spend_tx = self.signTxWithKey(b_lock_spend_tx, kbs)
 | 
			
		||||
@ -1366,10 +1370,8 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
 | 
			
		||||
            return {'txid': txid_hex, 'amount': 0, 'height': rv['blockheight']}
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,10 @@ from .btc import BTCInterface
 | 
			
		||||
from basicswap.chainparams import Coins
 | 
			
		||||
from basicswap.util.address import decodeAddress
 | 
			
		||||
from mnemonic import Mnemonic
 | 
			
		||||
from basicswap.contrib.test_framework.script import (
 | 
			
		||||
    CScript,
 | 
			
		||||
    OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DASHInterface(BTCInterface):
 | 
			
		||||
@ -37,8 +41,32 @@ class DASHInterface(BTCInterface):
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def withdrawCoin(self, value, addr_to, subfee):
 | 
			
		||||
        params = [addr_to, value, '', '', subfee]
 | 
			
		||||
        params = [addr_to, value, '', '', subfee, False, False, self._conf_target]
 | 
			
		||||
        return self.rpc_callback('sendtoaddress', params)
 | 
			
		||||
 | 
			
		||||
    def getSpendableBalance(self):
 | 
			
		||||
        return self.make_int(self.rpc_callback('getwalletinfo')['balance'])
 | 
			
		||||
 | 
			
		||||
    def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
 | 
			
		||||
        # Return P2PKH
 | 
			
		||||
        return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
 | 
			
		||||
 | 
			
		||||
    def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
 | 
			
		||||
        add_bytes = 107
 | 
			
		||||
        size = len(tx.serialize_with_witness()) + add_bytes
 | 
			
		||||
        pay_fee = int(fee_rate * size // 1000)
 | 
			
		||||
        self._log.info(f'BLockSpendTx  fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
 | 
			
		||||
        return pay_fee
 | 
			
		||||
 | 
			
		||||
    def findTxnByHash(self, txid_hex: str):
 | 
			
		||||
        # Only works for wallet txns
 | 
			
		||||
        try:
 | 
			
		||||
            rv = self.rpc_callback('gettransaction', [txid_hex])
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
 | 
			
		||||
            return None
 | 
			
		||||
        if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
 | 
			
		||||
            block_height = self.getBlockHeader(rv['blockhash'])['height']
 | 
			
		||||
            return {'txid': txid_hex, 'amount': 0, 'height': block_height}
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@ -151,7 +151,6 @@ class FIROInterface(BTCInterface):
 | 
			
		||||
 | 
			
		||||
    def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
 | 
			
		||||
        # Return P2PKH
 | 
			
		||||
 | 
			
		||||
        return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
 | 
			
		||||
 | 
			
		||||
    def getScriptDest(self, script: bytearray) -> bytearray:
 | 
			
		||||
@ -184,3 +183,27 @@ class FIROInterface(BTCInterface):
 | 
			
		||||
 | 
			
		||||
    def getSpendableBalance(self):
 | 
			
		||||
        return self.make_int(self.rpc_callback('getwalletinfo')['balance'])
 | 
			
		||||
 | 
			
		||||
    def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
 | 
			
		||||
        add_bytes = 107
 | 
			
		||||
        size = len(tx.serialize_with_witness()) + add_bytes
 | 
			
		||||
        pay_fee = int(fee_rate * size // 1000)
 | 
			
		||||
        self._log.info(f'BLockSpendTx  fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
 | 
			
		||||
        return pay_fee
 | 
			
		||||
 | 
			
		||||
    def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
 | 
			
		||||
        key_wif = self.encodeKey(key)
 | 
			
		||||
        rv = self.rpc_callback('signrawtransaction', [tx.hex(), [], [key_wif, ]])
 | 
			
		||||
        return bytes.fromhex(rv['hex'])
 | 
			
		||||
 | 
			
		||||
    def findTxnByHash(self, txid_hex: str):
 | 
			
		||||
        # Only works for wallet txns
 | 
			
		||||
        try:
 | 
			
		||||
            rv = self.rpc_callback('gettransaction', [txid_hex])
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
 | 
			
		||||
            return None
 | 
			
		||||
        if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
 | 
			
		||||
            block_height = self.getBlockHeader(rv['blockhash'])['height']
 | 
			
		||||
            return {'txid': txid_hex, 'amount': 0, 'height': block_height}
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,13 @@ from .contrib.pivx_test_framework.messages import (
 | 
			
		||||
    ToHex,
 | 
			
		||||
    FromHex,
 | 
			
		||||
    CTransaction)
 | 
			
		||||
from basicswap.contrib.test_framework.script import (
 | 
			
		||||
    CScript,
 | 
			
		||||
    OP_DUP,
 | 
			
		||||
    OP_HASH160,
 | 
			
		||||
    OP_CHECKSIG,
 | 
			
		||||
    OP_EQUALVERIFY,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PIVXInterface(BTCInterface):
 | 
			
		||||
@ -80,3 +87,31 @@ class PIVXInterface(BTCInterface):
 | 
			
		||||
        tx = CTransaction()
 | 
			
		||||
        tx.deserialize(BytesIO(tx_bytes))
 | 
			
		||||
        return tx
 | 
			
		||||
 | 
			
		||||
    def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
 | 
			
		||||
        # Return P2PKH
 | 
			
		||||
        return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
 | 
			
		||||
 | 
			
		||||
    def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
 | 
			
		||||
        add_bytes = 107
 | 
			
		||||
        size = len(tx.serialize_with_witness()) + add_bytes
 | 
			
		||||
        pay_fee = int(fee_rate * size // 1000)
 | 
			
		||||
        self._log.info(f'BLockSpendTx  fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
 | 
			
		||||
        return pay_fee
 | 
			
		||||
 | 
			
		||||
    def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
 | 
			
		||||
        key_wif = self.encodeKey(key)
 | 
			
		||||
        rv = self.rpc_callback('signrawtransaction', [tx.hex(), [], [key_wif, ]])
 | 
			
		||||
        return bytes.fromhex(rv['hex'])
 | 
			
		||||
 | 
			
		||||
    def findTxnByHash(self, txid_hex: str):
 | 
			
		||||
        # Only works for wallet txns
 | 
			
		||||
        try:
 | 
			
		||||
            rv = self.rpc_callback('gettransaction', [txid_hex])
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
 | 
			
		||||
            return None
 | 
			
		||||
        if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
 | 
			
		||||
            block_height = self.getBlockHeader(rv['blockhash'])['height']
 | 
			
		||||
            return {'txid': txid_hex, 'amount': 0, 'height': block_height}
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,9 @@
 | 
			
		||||
from basicswap.script import (
 | 
			
		||||
    OpCodes,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.util.script import (
 | 
			
		||||
    getP2WSH,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProtocolInterface:
 | 
			
		||||
@ -22,3 +25,7 @@ class ProtocolInterface:
 | 
			
		||||
    def getMockScriptScriptPubkey(self, ci) -> bytearray:
 | 
			
		||||
        script = self.getMockScript()
 | 
			
		||||
        return ci.get_p2wsh_script_pubkey(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
 | 
			
		||||
 | 
			
		||||
    def getMockAddrTo(self, ci):
 | 
			
		||||
        script = self.getMockScript()
 | 
			
		||||
        return ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,6 @@ from basicswap.db import (
 | 
			
		||||
from basicswap.util import (
 | 
			
		||||
    SerialiseNum,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.util.script import (
 | 
			
		||||
    getP2WSH,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.script import (
 | 
			
		||||
    OpCodes,
 | 
			
		||||
)
 | 
			
		||||
@ -77,8 +74,7 @@ class AtomicSwapInterface(ProtocolInterface):
 | 
			
		||||
    swap_type = SwapTypes.SELLER_FIRST
 | 
			
		||||
 | 
			
		||||
    def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
 | 
			
		||||
        script = self.getMockScript()
 | 
			
		||||
        addr_to = ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
 | 
			
		||||
        addr_to = self.getMockAddrTo(ci)
 | 
			
		||||
        funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
 | 
			
		||||
 | 
			
		||||
        return bytes.fromhex(funded_tx)
 | 
			
		||||
 | 
			
		||||
@ -9,9 +9,6 @@ from sqlalchemy.orm import scoped_session
 | 
			
		||||
from basicswap.util import (
 | 
			
		||||
    ensure,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.util.script import (
 | 
			
		||||
    getP2WSH,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.chainparams import (
 | 
			
		||||
    Coins,
 | 
			
		||||
)
 | 
			
		||||
@ -104,8 +101,7 @@ class XmrSwapInterface(ProtocolInterface):
 | 
			
		||||
        return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)])
 | 
			
		||||
 | 
			
		||||
    def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
 | 
			
		||||
        script = self.getMockScript()
 | 
			
		||||
        addr_to = ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
 | 
			
		||||
        addr_to = self.getMockAddrTo(ci)
 | 
			
		||||
        funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
 | 
			
		||||
 | 
			
		||||
        return bytes.fromhex(funded_tx)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								doc/protocols/sequence_diagrams/notes.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								doc/protocols/sequence_diagrams/notes.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
nvm use 14
 | 
			
		||||
npm install -g mscgenjs-cli
 | 
			
		||||
 | 
			
		||||
mscgenjs -T svg -i bidder.alt.xu -o bidder.alt.xu.svg
 | 
			
		||||
mscgenjs -T svg -i offerer.alt.xu -o offerer.alt.xu.svg
 | 
			
		||||
mscgenjs -T svg -i xmr.bidder.alt.xu -o xmr.bidder.alt.xu.svg
 | 
			
		||||
mscgenjs -T svg -i xmr.offerer.alt.xu -o xmr.offerer.alt.xu.svg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
npm -g install svgo
 | 
			
		||||
 | 
			
		||||
svgo --pretty bidder.alt.xu.svg -o bidder.alt.xu.min.svg
 | 
			
		||||
svgo --pretty offerer.alt.xu.svg -o offerer.alt.xu.min.svg
 | 
			
		||||
svgo --pretty xmr.bidder.alt.xu.svg -o xmr.bidder.alt.xu.min.svg
 | 
			
		||||
svgo --pretty xmr.offerer.alt.xu.svg -o xmr.offerer.alt.xu.min.svg
 | 
			
		||||
@ -25,9 +25,10 @@ import basicswap.config as cfg
 | 
			
		||||
from basicswap.basicswap import (
 | 
			
		||||
    BasicSwap,
 | 
			
		||||
    Coins,
 | 
			
		||||
    TxStates,
 | 
			
		||||
    SwapTypes,
 | 
			
		||||
    BidStates,
 | 
			
		||||
    TxStates,
 | 
			
		||||
    DebugTypes,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.util import (
 | 
			
		||||
    COIN,
 | 
			
		||||
@ -56,6 +57,8 @@ from tests.basicswap.common import (
 | 
			
		||||
    stopDaemons,
 | 
			
		||||
    wait_for_offer,
 | 
			
		||||
    wait_for_bid,
 | 
			
		||||
    wait_for_balance,
 | 
			
		||||
    wait_for_unspent,
 | 
			
		||||
    wait_for_bid_tx_state,
 | 
			
		||||
    wait_for_in_progress,
 | 
			
		||||
    TEST_HTTP_HOST,
 | 
			
		||||
@ -192,7 +195,15 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
 | 
			
		||||
        },
 | 
			
		||||
        'check_progress_seconds': 2,
 | 
			
		||||
        'check_watched_seconds': 4,
 | 
			
		||||
        'check_expired_seconds': 60
 | 
			
		||||
        'check_expired_seconds': 60,
 | 
			
		||||
        'check_events_seconds': 1,
 | 
			
		||||
        'check_xmr_swaps_seconds': 1,
 | 
			
		||||
        'min_delay_event': 1,
 | 
			
		||||
        'max_delay_event': 3,
 | 
			
		||||
        'min_delay_event_short': 1,
 | 
			
		||||
        'max_delay_event_short': 3,
 | 
			
		||||
        'min_delay_retry': 2,
 | 
			
		||||
        'max_delay_retry': 10
 | 
			
		||||
    }
 | 
			
		||||
    with open(settings_path, 'w') as fp:
 | 
			
		||||
        json.dump(settings, fp, indent=4)
 | 
			
		||||
@ -305,8 +316,8 @@ class Test(unittest.TestCase):
 | 
			
		||||
                rpc('rescanblockchain')
 | 
			
		||||
            else:
 | 
			
		||||
                rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
 | 
			
		||||
            rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
			
		||||
            rpc('reservebalance', [False])
 | 
			
		||||
            rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
 | 
			
		||||
            rpc('reservebalance', ['false'])
 | 
			
		||||
 | 
			
		||||
            basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
 | 
			
		||||
            settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
 | 
			
		||||
@ -564,6 +575,133 @@ class Test(unittest.TestCase):
 | 
			
		||||
        dashRpc('unloadwallet', wallet=new_wallet_name)
 | 
			
		||||
        assert (addr_test == addr)
 | 
			
		||||
 | 
			
		||||
    def test_10_prefunded_itx(self):
 | 
			
		||||
        logging.info('---------- Test prefunded itx offer')
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        coin_from = Coins.DASH
 | 
			
		||||
        coin_to = Coins.BTC
 | 
			
		||||
        swap_type = SwapTypes.SELLER_FIRST
 | 
			
		||||
        ci_from = swap_clients[2].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
        tla_from = coin_from.name
 | 
			
		||||
 | 
			
		||||
        # Prepare balance
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        if float(js_w2[tla_from]['balance']) < 100.0:
 | 
			
		||||
            post_json = {
 | 
			
		||||
                'value': 100,
 | 
			
		||||
                'address': js_w2[tla_from]['deposit_address'],
 | 
			
		||||
                'subfee': False,
 | 
			
		||||
            }
 | 
			
		||||
            json_rv = read_json_api(1800, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
 | 
			
		||||
            assert (len(json_rv['txid']) == 64)
 | 
			
		||||
            wait_for_balance(delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 100.0)
 | 
			
		||||
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        assert (float(js_w2[tla_from]['balance']) >= 100.0)
 | 
			
		||||
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        post_json = {
 | 
			
		||||
            'value': 100.0,
 | 
			
		||||
            'address': read_json_api(1802, 'wallets/{}/nextdepositaddr'.format(tla_from.lower())),
 | 
			
		||||
            'subfee': True,
 | 
			
		||||
        }
 | 
			
		||||
        json_rv = read_json_api(1802, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
 | 
			
		||||
        wait_for_balance(delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 10.0)
 | 
			
		||||
        assert (len(json_rv['txid']) == 64)
 | 
			
		||||
 | 
			
		||||
        # Create prefunded ITX
 | 
			
		||||
        pi = swap_clients[2].pi(SwapTypes.XMR_SWAP)
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        swap_value = 100.0
 | 
			
		||||
        if float(js_w2[tla_from]['balance']) < swap_value:
 | 
			
		||||
            swap_value = js_w2[tla_from]['balance']
 | 
			
		||||
        swap_value = ci_from.make_int(swap_value)
 | 
			
		||||
        assert (swap_value > ci_from.make_int(95))
 | 
			
		||||
 | 
			
		||||
        itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
 | 
			
		||||
        itx_decoded = ci_from.describeTx(itx.hex())
 | 
			
		||||
        value_after_subfee = ci_from.make_int(itx_decoded['vout'][0]['value'])
 | 
			
		||||
        assert (value_after_subfee < swap_value)
 | 
			
		||||
        swap_value = value_after_subfee
 | 
			
		||||
        wait_for_unspent(delay_event, ci_from, swap_value)
 | 
			
		||||
 | 
			
		||||
        extra_options = {'prefunded_itx': itx}
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[2].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type, extra_options=extra_options)
 | 
			
		||||
 | 
			
		||||
        wait_for_offer(delay_event, swap_clients[1], offer_id)
 | 
			
		||||
        offer = swap_clients[1].getOffer(offer_id)
 | 
			
		||||
        bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
        swap_clients[2].acceptBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
 | 
			
		||||
 | 
			
		||||
        # Verify expected inputs were used
 | 
			
		||||
        bid, offer = swap_clients[2].getBidAndOffer(bid_id)
 | 
			
		||||
        assert (bid.initiate_tx)
 | 
			
		||||
        wtx = ci_from.rpc_callback('gettransaction', [bid.initiate_tx.txid.hex(),])
 | 
			
		||||
        itx_after = ci_from.describeTx(wtx['hex'])
 | 
			
		||||
        assert (len(itx_after['vin']) == len(itx_decoded['vin']))
 | 
			
		||||
        for i, txin in enumerate(itx_decoded['vin']):
 | 
			
		||||
            assert (txin['txid'] == itx_after['vin'][i]['txid'])
 | 
			
		||||
            assert (txin['vout'] == itx_after['vin'][i]['vout'])
 | 
			
		||||
 | 
			
		||||
    def test_11_xmrswap_to(self):
 | 
			
		||||
        logging.info('---------- Test xmr swap protocol to')
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        coin_from = Coins.BTC
 | 
			
		||||
        coin_to = Coins.DASH
 | 
			
		||||
        swap_type = SwapTypes.XMR_SWAP
 | 
			
		||||
        ci_from = swap_clients[0].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        swap_value = ci_from.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[0].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type)
 | 
			
		||||
 | 
			
		||||
        wait_for_offer(delay_event, swap_clients[1], offer_id)
 | 
			
		||||
        offer = swap_clients[1].getOffer(offer_id)
 | 
			
		||||
        bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
        swap_clients[0].acceptBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
 | 
			
		||||
 | 
			
		||||
    def test_12_xmrswap_to_recover_b_lock_tx(self):
 | 
			
		||||
        coin_from = Coins.BTC
 | 
			
		||||
        coin_to = Coins.DASH
 | 
			
		||||
        logging.info('---------- Test {} to {} follower recovers coin b lock tx'.format(coin_from.name, coin_to.name))
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        ci_from = swap_clients[0].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[0].postOffer(
 | 
			
		||||
            coin_from, coin_to, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP,
 | 
			
		||||
            lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=32)
 | 
			
		||||
        wait_for_offer(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(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
 | 
			
		||||
        swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
 | 
			
		||||
        swap_clients[0].acceptXmrBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ from basicswap.basicswap import (
 | 
			
		||||
    TxStates,
 | 
			
		||||
    SwapTypes,
 | 
			
		||||
    BidStates,
 | 
			
		||||
    DebugTypes,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.basicswap_util import (
 | 
			
		||||
    TxLockTypes,
 | 
			
		||||
@ -38,6 +39,8 @@ from tests.basicswap.common import (
 | 
			
		||||
    make_rpc_func,
 | 
			
		||||
    TEST_HTTP_PORT,
 | 
			
		||||
    wait_for_offer,
 | 
			
		||||
    wait_for_balance,
 | 
			
		||||
    wait_for_unspent,
 | 
			
		||||
    wait_for_in_progress,
 | 
			
		||||
    wait_for_bid_tx_state,
 | 
			
		||||
)
 | 
			
		||||
@ -420,6 +423,135 @@ class Test(BaseTest):
 | 
			
		||||
        json_rv = read_json_api(TEST_HTTP_PORT + 0, 'wallets/{}/createutxo'.format(self.test_coin_from.name.lower()), post_json)
 | 
			
		||||
        assert (len(json_rv['txid']) == 64)
 | 
			
		||||
 | 
			
		||||
    def ensure_balance(self, coin_type, node_id, amount):
 | 
			
		||||
        tla = coin_type.name
 | 
			
		||||
        js_w = read_json_api(1800 + node_id, 'wallets')
 | 
			
		||||
        if float(js_w[tla]['balance']) < amount:
 | 
			
		||||
            post_json = {
 | 
			
		||||
                'value': amount,
 | 
			
		||||
                'address': js_w[tla]['deposit_address'],
 | 
			
		||||
                'subfee': False,
 | 
			
		||||
            }
 | 
			
		||||
            json_rv = read_json_api(1800, 'wallets/{}/withdraw'.format(tla.lower()), post_json)
 | 
			
		||||
            assert (len(json_rv['txid']) == 64)
 | 
			
		||||
            wait_for_balance(test_delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(1800 + node_id, tla.lower()), 'balance', amount)
 | 
			
		||||
 | 
			
		||||
    def test_10_prefunded_itx(self):
 | 
			
		||||
        logging.info('---------- Test prefunded itx offer')
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        coin_from = Coins.FIRO
 | 
			
		||||
        coin_to = Coins.BTC
 | 
			
		||||
        swap_type = SwapTypes.SELLER_FIRST
 | 
			
		||||
        ci_from = swap_clients[2].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
        tla_from = coin_from.name
 | 
			
		||||
 | 
			
		||||
        # Prepare balance
 | 
			
		||||
        self.ensure_balance(coin_from, 2, 10.0)
 | 
			
		||||
        self.ensure_balance(coin_to, 1, 100.0)
 | 
			
		||||
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        post_json = {
 | 
			
		||||
            'value': 10.0,
 | 
			
		||||
            'address': read_json_api(1802, 'wallets/{}/nextdepositaddr'.format(tla_from.lower())),
 | 
			
		||||
            'subfee': True,
 | 
			
		||||
        }
 | 
			
		||||
        json_rv = read_json_api(1802, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
 | 
			
		||||
        wait_for_balance(test_delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 9.0)
 | 
			
		||||
        assert (len(json_rv['txid']) == 64)
 | 
			
		||||
 | 
			
		||||
        # Create prefunded ITX
 | 
			
		||||
        pi = swap_clients[2].pi(SwapTypes.XMR_SWAP)
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        swap_value = 10.0
 | 
			
		||||
        if float(js_w2[tla_from]['balance']) < swap_value:
 | 
			
		||||
            swap_value = js_w2[tla_from]['balance']
 | 
			
		||||
        swap_value = ci_from.make_int(swap_value)
 | 
			
		||||
        assert (swap_value > ci_from.make_int(9))
 | 
			
		||||
 | 
			
		||||
        itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
 | 
			
		||||
        itx_decoded = ci_from.describeTx(itx.hex())
 | 
			
		||||
        value_after_subfee = ci_from.make_int(itx_decoded['vout'][0]['value'])
 | 
			
		||||
        assert (value_after_subfee < swap_value)
 | 
			
		||||
        swap_value = value_after_subfee
 | 
			
		||||
        wait_for_unspent(test_delay_event, ci_from, swap_value)
 | 
			
		||||
 | 
			
		||||
        extra_options = {'prefunded_itx': itx}
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 10.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[2].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type, extra_options=extra_options)
 | 
			
		||||
 | 
			
		||||
        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
			
		||||
        offer = swap_clients[1].getOffer(offer_id)
 | 
			
		||||
        bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
        swap_clients[2].acceptBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[2], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
 | 
			
		||||
 | 
			
		||||
        # Verify expected inputs were used
 | 
			
		||||
        bid, offer = swap_clients[2].getBidAndOffer(bid_id)
 | 
			
		||||
        assert (bid.initiate_tx)
 | 
			
		||||
        wtx = ci_from.rpc_callback('gettransaction', [bid.initiate_tx.txid.hex(),])
 | 
			
		||||
        itx_after = ci_from.describeTx(wtx['hex'])
 | 
			
		||||
        assert (len(itx_after['vin']) == len(itx_decoded['vin']))
 | 
			
		||||
        for i, txin in enumerate(itx_decoded['vin']):
 | 
			
		||||
            assert (txin['txid'] == itx_after['vin'][i]['txid'])
 | 
			
		||||
            assert (txin['vout'] == itx_after['vin'][i]['vout'])
 | 
			
		||||
 | 
			
		||||
    def test_11_xmrswap_to(self):
 | 
			
		||||
        logging.info('---------- Test xmr swap protocol to')
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        coin_from = Coins.BTC
 | 
			
		||||
        coin_to = Coins.FIRO
 | 
			
		||||
        swap_type = SwapTypes.XMR_SWAP
 | 
			
		||||
        ci_from = swap_clients[0].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        swap_value = ci_from.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[0].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type)
 | 
			
		||||
 | 
			
		||||
        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
			
		||||
        offer = swap_clients[1].getOffer(offer_id)
 | 
			
		||||
        bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
        swap_clients[0].acceptBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
 | 
			
		||||
 | 
			
		||||
    def test_12_xmrswap_to_recover_b_lock_tx(self):
 | 
			
		||||
        coin_from = Coins.BTC
 | 
			
		||||
        coin_to = Coins.FIRO
 | 
			
		||||
        logging.info('---------- Test {} to {} follower recovers coin b lock tx'.format(coin_from.name, coin_to.name))
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        ci_from = swap_clients[0].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[0].postOffer(
 | 
			
		||||
            coin_from, coin_to, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP,
 | 
			
		||||
            lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=32)
 | 
			
		||||
        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)
 | 
			
		||||
        swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
 | 
			
		||||
        swap_clients[0].acceptXmrBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
 | 
			
		||||
        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
 | 
			
		||||
 | 
			
		||||
    def test_101_full_swap(self):
 | 
			
		||||
        logging.info('---------- Test {} to XMR'.format(self.test_coin_from.name))
 | 
			
		||||
        if not self.test_xmr:
 | 
			
		||||
 | 
			
		||||
@ -220,8 +220,8 @@ class Test(unittest.TestCase):
 | 
			
		||||
                else:
 | 
			
		||||
                    rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
 | 
			
		||||
                # Lower output split threshold for more stakeable outputs
 | 
			
		||||
                rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
			
		||||
                rpc('reservebalance', [False])
 | 
			
		||||
                rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
 | 
			
		||||
                rpc('reservebalance', ['false'])
 | 
			
		||||
 | 
			
		||||
            for i in range(NUM_BTC_NODES):
 | 
			
		||||
                data_dir = prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT)
 | 
			
		||||
 | 
			
		||||
@ -297,8 +297,8 @@ class Test(unittest.TestCase):
 | 
			
		||||
                rpc('rescanblockchain')
 | 
			
		||||
            else:
 | 
			
		||||
                rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
 | 
			
		||||
            rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
			
		||||
            rpc('reservebalance', [False])
 | 
			
		||||
            rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
 | 
			
		||||
            rpc('reservebalance', ['false'])
 | 
			
		||||
 | 
			
		||||
            basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
 | 
			
		||||
            settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ import os
 | 
			
		||||
import sys
 | 
			
		||||
import json
 | 
			
		||||
import time
 | 
			
		||||
import random
 | 
			
		||||
import shutil
 | 
			
		||||
import signal
 | 
			
		||||
import logging
 | 
			
		||||
@ -27,6 +28,7 @@ from basicswap.basicswap import (
 | 
			
		||||
    SwapTypes,
 | 
			
		||||
    BidStates,
 | 
			
		||||
    TxStates,
 | 
			
		||||
    DebugTypes,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.util import (
 | 
			
		||||
    COIN,
 | 
			
		||||
@ -47,16 +49,21 @@ from basicswap.contrib.key import (
 | 
			
		||||
from basicswap.http_server import (
 | 
			
		||||
    HttpThread,
 | 
			
		||||
)
 | 
			
		||||
from basicswap.interface.btc import (
 | 
			
		||||
    find_vout_for_address_from_txobj,
 | 
			
		||||
)
 | 
			
		||||
from tests.basicswap.util import (
 | 
			
		||||
    read_json_api,
 | 
			
		||||
)
 | 
			
		||||
from tests.basicswap.common import (
 | 
			
		||||
    checkForks,
 | 
			
		||||
    stopDaemons,
 | 
			
		||||
    wait_for_offer,
 | 
			
		||||
    wait_for_bid,
 | 
			
		||||
    wait_for_bid_tx_state,
 | 
			
		||||
    wait_for_offer,
 | 
			
		||||
    wait_for_balance,
 | 
			
		||||
    wait_for_unspent,
 | 
			
		||||
    wait_for_in_progress,
 | 
			
		||||
    wait_for_bid_tx_state,
 | 
			
		||||
    TEST_HTTP_HOST,
 | 
			
		||||
    TEST_HTTP_PORT,
 | 
			
		||||
    BASE_PORT,
 | 
			
		||||
@ -197,7 +204,15 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
 | 
			
		||||
        },
 | 
			
		||||
        'check_progress_seconds': 2,
 | 
			
		||||
        'check_watched_seconds': 4,
 | 
			
		||||
        'check_expired_seconds': 60
 | 
			
		||||
        'check_expired_seconds': 60,
 | 
			
		||||
        'check_events_seconds': 1,
 | 
			
		||||
        'check_xmr_swaps_seconds': 1,
 | 
			
		||||
        'min_delay_event': 1,
 | 
			
		||||
        'max_delay_event': 3,
 | 
			
		||||
        'min_delay_event_short': 1,
 | 
			
		||||
        'max_delay_event_short': 3,
 | 
			
		||||
        'min_delay_retry': 2,
 | 
			
		||||
        'max_delay_retry': 10
 | 
			
		||||
    }
 | 
			
		||||
    with open(settings_path, 'w') as fp:
 | 
			
		||||
        json.dump(settings, fp, indent=4)
 | 
			
		||||
@ -317,8 +332,8 @@ class Test(unittest.TestCase):
 | 
			
		||||
                rpc('rescanblockchain')
 | 
			
		||||
            else:
 | 
			
		||||
                rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
 | 
			
		||||
            rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
			
		||||
            rpc('reservebalance', [False])
 | 
			
		||||
            rpc('walletsettings', ['stakingoptions', json.dumps({'stakecombinethreshold': 100, 'stakesplitthreshold': 200}).replace('"', '\\"')])
 | 
			
		||||
            rpc('reservebalance', ['false'])
 | 
			
		||||
 | 
			
		||||
            basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
 | 
			
		||||
            settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
 | 
			
		||||
@ -581,6 +596,138 @@ class Test(unittest.TestCase):
 | 
			
		||||
                break
 | 
			
		||||
        assert found
 | 
			
		||||
 | 
			
		||||
    def ensure_balance(self, coin_type, node_id, amount):
 | 
			
		||||
        tla = coin_type.name
 | 
			
		||||
        js_w = read_json_api(1800 + node_id, 'wallets')
 | 
			
		||||
        if float(js_w[tla]['balance']) < amount:
 | 
			
		||||
            post_json = {
 | 
			
		||||
                'value': amount,
 | 
			
		||||
                'address': js_w[tla]['deposit_address'],
 | 
			
		||||
                'subfee': False,
 | 
			
		||||
            }
 | 
			
		||||
            json_rv = read_json_api(1800, 'wallets/{}/withdraw'.format(tla.lower()), post_json)
 | 
			
		||||
            assert (len(json_rv['txid']) == 64)
 | 
			
		||||
            wait_for_balance(delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(1800 + node_id, tla.lower()), 'balance', amount)
 | 
			
		||||
 | 
			
		||||
    def test_10_prefunded_itx(self):
 | 
			
		||||
        logging.info('---------- Test prefunded itx offer')
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        coin_from = Coins.PIVX
 | 
			
		||||
        coin_to = Coins.BTC
 | 
			
		||||
        swap_type = SwapTypes.SELLER_FIRST
 | 
			
		||||
        ci_from = swap_clients[2].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
        tla_from = coin_from.name
 | 
			
		||||
 | 
			
		||||
        # Prepare balance
 | 
			
		||||
        self.ensure_balance(coin_from, 2, 10.0)
 | 
			
		||||
        self.ensure_balance(coin_to, 1, 100.0)
 | 
			
		||||
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        post_json = {
 | 
			
		||||
            'value': 10.0,
 | 
			
		||||
            'address': read_json_api(1802, 'wallets/{}/nextdepositaddr'.format(tla_from.lower())),
 | 
			
		||||
            'subfee': True,
 | 
			
		||||
        }
 | 
			
		||||
        json_rv = read_json_api(1802, 'wallets/{}/withdraw'.format(tla_from.lower()), post_json)
 | 
			
		||||
        wait_for_balance(delay_event, 'http://127.0.0.1:1802/json/wallets/{}'.format(tla_from.lower()), 'balance', 9.0)
 | 
			
		||||
        assert (len(json_rv['txid']) == 64)
 | 
			
		||||
 | 
			
		||||
        # Create prefunded ITX
 | 
			
		||||
        pi = swap_clients[2].pi(SwapTypes.XMR_SWAP)
 | 
			
		||||
        js_w2 = read_json_api(1802, 'wallets')
 | 
			
		||||
        swap_value = 10.0
 | 
			
		||||
        if float(js_w2[tla_from]['balance']) < swap_value:
 | 
			
		||||
            swap_value = js_w2[tla_from]['balance']
 | 
			
		||||
        swap_value = ci_from.make_int(swap_value)
 | 
			
		||||
        assert (swap_value > ci_from.make_int(9))
 | 
			
		||||
 | 
			
		||||
        itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
 | 
			
		||||
        itx_decoded = ci_from.describeTx(itx.hex())
 | 
			
		||||
 | 
			
		||||
        mock_addr = pi.getMockAddrTo(ci_from)
 | 
			
		||||
        n = find_vout_for_address_from_txobj(itx_decoded, mock_addr)
 | 
			
		||||
        value_after_subfee = ci_from.make_int(itx_decoded['vout'][n]['value'])
 | 
			
		||||
        assert (value_after_subfee < swap_value)
 | 
			
		||||
        swap_value = value_after_subfee
 | 
			
		||||
        wait_for_unspent(delay_event, ci_from, swap_value)
 | 
			
		||||
 | 
			
		||||
        extra_options = {'prefunded_itx': itx}
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 10.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[2].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type, TxLockTypes.ABS_LOCK_TIME, extra_options=extra_options)
 | 
			
		||||
 | 
			
		||||
        wait_for_offer(delay_event, swap_clients[1], offer_id)
 | 
			
		||||
        offer = swap_clients[1].getOffer(offer_id)
 | 
			
		||||
        bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
        swap_clients[2].acceptBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[2], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
 | 
			
		||||
 | 
			
		||||
        # Verify expected inputs were used
 | 
			
		||||
        bid, offer = swap_clients[2].getBidAndOffer(bid_id)
 | 
			
		||||
        assert (bid.initiate_tx)
 | 
			
		||||
        wtx = ci_from.rpc_callback('gettransaction', [bid.initiate_tx.txid.hex(),])
 | 
			
		||||
        itx_after = ci_from.describeTx(wtx['hex'])
 | 
			
		||||
        assert (len(itx_after['vin']) == len(itx_decoded['vin']))
 | 
			
		||||
        for i, txin in enumerate(itx_decoded['vin']):
 | 
			
		||||
            assert (txin['txid'] == itx_after['vin'][i]['txid'])
 | 
			
		||||
            assert (txin['vout'] == itx_after['vin'][i]['vout'])
 | 
			
		||||
 | 
			
		||||
    def test_11_xmrswap_to(self):
 | 
			
		||||
        logging.info('---------- Test xmr swap protocol to')
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        coin_from = Coins.BTC
 | 
			
		||||
        coin_to = Coins.PIVX
 | 
			
		||||
        swap_type = SwapTypes.XMR_SWAP
 | 
			
		||||
        ci_from = swap_clients[0].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        swap_value = ci_from.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[0].postOffer(coin_from, coin_to, swap_value, rate_swap, swap_value, swap_type)
 | 
			
		||||
 | 
			
		||||
        wait_for_offer(delay_event, swap_clients[1], offer_id)
 | 
			
		||||
        offer = swap_clients[1].getOffer(offer_id)
 | 
			
		||||
        bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
        swap_clients[0].acceptBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=120)
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=120)
 | 
			
		||||
 | 
			
		||||
    def test_12_xmrswap_to_recover_b_lock_tx(self):
 | 
			
		||||
        coin_from = Coins.BTC
 | 
			
		||||
        coin_to = Coins.PIVX
 | 
			
		||||
        logging.info('---------- Test {} to {} follower recovers coin b lock tx'.format(coin_from.name, coin_to.name))
 | 
			
		||||
 | 
			
		||||
        swap_clients = self.swap_clients
 | 
			
		||||
        ci_from = swap_clients[0].ci(coin_from)
 | 
			
		||||
        ci_to = swap_clients[1].ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
 | 
			
		||||
        rate_swap = ci_to.make_int(random.uniform(0.2, 20.0), r=1)
 | 
			
		||||
        offer_id = swap_clients[0].postOffer(
 | 
			
		||||
            coin_from, coin_to, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP,
 | 
			
		||||
            lock_type=TxLockTypes.SEQUENCE_LOCK_BLOCKS, lock_value=32)
 | 
			
		||||
        wait_for_offer(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(delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
 | 
			
		||||
        swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
 | 
			
		||||
        swap_clients[0].acceptXmrBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
 | 
			
		||||
        wait_for_bid(delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user