Add non-segwit Firo support.
Rework tests to combine atomic and xmr test cases. Modify btc interface to support P2WSH_nested_in_BIP16_P2SH Add coin feature tests to test_btc_xmr.py
This commit is contained in:
		
							parent
							
								
									c0c2c8b9bb
								
							
						
					
					
						commit
						ca264db0d0
					
				@ -1,3 +1,3 @@
 | 
				
			|||||||
name = "basicswap"
 | 
					name = "basicswap"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__version__ = "0.11.44"
 | 
					__version__ = "0.11.45"
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,6 @@ import traceback
 | 
				
			|||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import basicswap.config as cfg
 | 
					import basicswap.config as cfg
 | 
				
			||||||
import basicswap.contrib.segwit_addr as segwit_addr
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .rpc import (
 | 
					from .rpc import (
 | 
				
			||||||
    callrpc,
 | 
					    callrpc,
 | 
				
			||||||
@ -112,12 +111,6 @@ class BaseApp:
 | 
				
			|||||||
                return c
 | 
					                return c
 | 
				
			||||||
        raise ValueError('Unknown coin: {}'.format(coin_name))
 | 
					        raise ValueError('Unknown coin: {}'.format(coin_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def encodeSegwit(self, coin_type, raw):
 | 
					 | 
				
			||||||
        return segwit_addr.encode(chainparams[coin_type][self.chain]['hrp'], 0, raw)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def decodeSegwit(self, coin_type, addr):
 | 
					 | 
				
			||||||
        return bytes(segwit_addr.decode(chainparams[coin_type][self.chain]['hrp'], addr)[1])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def callrpc(self, method, params=[], wallet=None):
 | 
					    def callrpc(self, method, params=[], wallet=None):
 | 
				
			||||||
        cc = self.coin_clients[Coins.PART]
 | 
					        cc = self.coin_clients[Coins.PART]
 | 
				
			||||||
        return callrpc(cc['rpcport'], cc['rpcauth'], method, params, wallet, cc['rpchost'])
 | 
					        return callrpc(cc['rpcport'], cc['rpcauth'], method, params, wallet, cc['rpchost'])
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,7 @@ from .interface.nmc import NMCInterface
 | 
				
			|||||||
from .interface.xmr import XMRInterface
 | 
					from .interface.xmr import XMRInterface
 | 
				
			||||||
from .interface.pivx import PIVXInterface
 | 
					from .interface.pivx import PIVXInterface
 | 
				
			||||||
from .interface.dash import DASHInterface
 | 
					from .interface.dash import DASHInterface
 | 
				
			||||||
 | 
					from .interface.firo import FIROInterface
 | 
				
			||||||
from .interface.passthrough_btc import PassthroughBTCInterface
 | 
					from .interface.passthrough_btc import PassthroughBTCInterface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import __version__
 | 
					from . import __version__
 | 
				
			||||||
@ -58,7 +59,6 @@ from .util.address import (
 | 
				
			|||||||
    getKeyID,
 | 
					    getKeyID,
 | 
				
			||||||
    decodeWif,
 | 
					    decodeWif,
 | 
				
			||||||
    decodeAddress,
 | 
					    decodeAddress,
 | 
				
			||||||
    encodeAddress,
 | 
					 | 
				
			||||||
    pubkeyToAddress,
 | 
					    pubkeyToAddress,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from .chainparams import (
 | 
					from .chainparams import (
 | 
				
			||||||
@ -531,6 +531,8 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            return PIVXInterface(self.coin_clients[coin], self.chain, self)
 | 
					            return PIVXInterface(self.coin_clients[coin], self.chain, self)
 | 
				
			||||||
        elif coin == Coins.DASH:
 | 
					        elif coin == Coins.DASH:
 | 
				
			||||||
            return DASHInterface(self.coin_clients[coin], self.chain, self)
 | 
					            return DASHInterface(self.coin_clients[coin], self.chain, self)
 | 
				
			||||||
 | 
					        elif coin == Coins.FIRO:
 | 
				
			||||||
 | 
					            return FIROInterface(self.coin_clients[coin], self.chain, self)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            raise ValueError('Unknown coin type')
 | 
					            raise ValueError('Unknown coin type')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -549,7 +551,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
 | 
					            authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            pidfilename = cc['name']
 | 
					            pidfilename = cc['name']
 | 
				
			||||||
            if cc['name'] in ('bitcoin', 'litecoin', 'namecoin', 'dash'):
 | 
					            if cc['name'] in ('bitcoin', 'litecoin', 'namecoin', 'dash', 'firo'):
 | 
				
			||||||
                pidfilename += 'd'
 | 
					                pidfilename += 'd'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            pidfilepath = os.path.join(self.getChainDatadirPath(coin), pidfilename + '.pid')
 | 
					            pidfilepath = os.path.join(self.getChainDatadirPath(coin), pidfilename + '.pid')
 | 
				
			||||||
@ -975,10 +977,8 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            raise ValueError('Invalid swap type for PART_ANON')
 | 
					            raise ValueError('Invalid swap type for PART_ANON')
 | 
				
			||||||
        if (coin_from == Coins.PART_BLIND or coin_to == Coins.PART_BLIND) and swap_type != SwapTypes.XMR_SWAP:
 | 
					        if (coin_from == Coins.PART_BLIND or coin_to == Coins.PART_BLIND) and swap_type != SwapTypes.XMR_SWAP:
 | 
				
			||||||
            raise ValueError('Invalid swap type for PART_BLIND')
 | 
					            raise ValueError('Invalid swap type for PART_BLIND')
 | 
				
			||||||
        if coin_from == Coins.PIVX and swap_type == SwapTypes.XMR_SWAP:
 | 
					        if coin_from in (Coins.PIVX, Coins.DASH, Coins.FIRO, Coins.NMC) and swap_type == SwapTypes.XMR_SWAP:
 | 
				
			||||||
            raise ValueError('TODO: PIVX -> XMR')
 | 
					            raise ValueError('TODO: {} -> XMR'.format(coin_from.name))
 | 
				
			||||||
        if coin_from == Coins.DASH and swap_type == SwapTypes.XMR_SWAP:
 | 
					 | 
				
			||||||
            raise ValueError('TODO: DASH -> XMR')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def notify(self, event_type, event_data, session=None):
 | 
					    def notify(self, event_type, event_data, session=None):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1053,7 +1053,6 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        ensure(amount_to < ci_to.max_amount(), 'To amount above max 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, coin_from, coin_to, lock_type, lock_value):
 | 
				
			||||||
 | 
					 | 
				
			||||||
        coin_from_has_csv = self.coin_clients[coin_from]['use_csv']
 | 
					        coin_from_has_csv = self.coin_clients[coin_from]['use_csv']
 | 
				
			||||||
        coin_to_has_csv = self.coin_clients[coin_to]['use_csv']
 | 
					        coin_to_has_csv = self.coin_clients[coin_to]['use_csv']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1609,18 +1608,6 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            self.mxDB.release()
 | 
					            self.mxDB.release()
 | 
				
			||||||
        return self._contract_count
 | 
					        return self._contract_count
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getUnspentsByAddr(self, coin_type):
 | 
					 | 
				
			||||||
        ci = self.ci(coin_type)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        unspent_addr = dict()
 | 
					 | 
				
			||||||
        unspent = self.callcoinrpc(coin_type, 'listunspent')
 | 
					 | 
				
			||||||
        for u in unspent:
 | 
					 | 
				
			||||||
            if u['spendable'] is not True:
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            unspent_addr[u['address']] = unspent_addr.get(u['address'], 0) + ci.make_int(u['amount'], r=1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return unspent_addr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getProofOfFunds(self, coin_type, amount_for, extra_commit_bytes):
 | 
					    def getProofOfFunds(self, coin_type, amount_for, extra_commit_bytes):
 | 
				
			||||||
        ci = self.ci(coin_type)
 | 
					        ci = self.ci(coin_type)
 | 
				
			||||||
        self.log.debug('getProofOfFunds %s %s', ci.coin_name(), ci.format_amount(amount_for))
 | 
					        self.log.debug('getProofOfFunds %s %s', ci.coin_name(), ci.format_amount(amount_for))
 | 
				
			||||||
@ -1628,28 +1615,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        if self.coin_clients[coin_type]['connection_type'] != 'rpc':
 | 
					        if self.coin_clients[coin_type]['connection_type'] != 'rpc':
 | 
				
			||||||
            return (None, None)
 | 
					            return (None, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO: Lock unspent and use same output/s to fund bid
 | 
					        return ci.getProofOfFunds(amount_for, extra_commit_bytes)
 | 
				
			||||||
        unspent_addr = self.getUnspentsByAddr(coin_type)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sign_for_addr = None
 | 
					 | 
				
			||||||
        for addr, value in unspent_addr.items():
 | 
					 | 
				
			||||||
            if value >= amount_for:
 | 
					 | 
				
			||||||
                sign_for_addr = addr
 | 
					 | 
				
			||||||
                break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ensure(sign_for_addr is not None, 'Could not find address with enough funds for proof')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.log.debug('sign_for_addr %s', sign_for_addr)
 | 
					 | 
				
			||||||
        if self.coin_clients[coin_type]['use_segwit']:  # TODO: Use isSegwitAddress when scantxoutset can use combo
 | 
					 | 
				
			||||||
            # 'Address does not refer to key' for non p2pkh
 | 
					 | 
				
			||||||
            addrinfo = self.callcoinrpc(coin_type, 'getaddressinfo', [sign_for_addr])
 | 
					 | 
				
			||||||
            pkh = addrinfo['scriptPubKey'][4:]
 | 
					 | 
				
			||||||
            sign_for_addr = encodeAddress(bytes((chainparams[coin_type][self.chain]['pubkey_address'],)) + bytes.fromhex(pkh))
 | 
					 | 
				
			||||||
            self.log.debug('sign_for_addr converted %s', sign_for_addr)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        signature = self.callcoinrpc(coin_type, 'signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + extra_commit_bytes.hex()])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return (sign_for_addr, signature)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def saveBidInSession(self, bid_id, bid, session, xmr_swap=None, save_in_progress=None):
 | 
					    def saveBidInSession(self, bid_id, bid, session, xmr_swap=None, save_in_progress=None):
 | 
				
			||||||
        session.add(bid)
 | 
					        session.add(bid)
 | 
				
			||||||
@ -2293,16 +2259,16 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
                xmr_swap.kbsl_dleag = xmr_swap.pkbsl
 | 
					                xmr_swap.kbsl_dleag = xmr_swap.pkbsl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # MSG2F
 | 
					            # MSG2F
 | 
				
			||||||
            xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script = ci_from.createScriptLockTx(
 | 
					            xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script = ci_from.createSCLockTx(
 | 
				
			||||||
                bid.amount,
 | 
					                bid.amount,
 | 
				
			||||||
                xmr_swap.pkal, xmr_swap.pkaf, xmr_swap.vkbv
 | 
					                xmr_swap.pkal, xmr_swap.pkaf, xmr_swap.vkbv
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            xmr_swap.a_lock_tx = ci_from.fundScriptLockTx(xmr_swap.a_lock_tx, xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
					            xmr_swap.a_lock_tx = ci_from.fundSCLockTx(xmr_swap.a_lock_tx, xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            xmr_swap.a_lock_tx_id = ci_from.getTxid(xmr_swap.a_lock_tx)
 | 
					            xmr_swap.a_lock_tx_id = ci_from.getTxid(xmr_swap.a_lock_tx)
 | 
				
			||||||
            a_lock_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_tx_script)
 | 
					            a_lock_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_tx_script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script, xmr_swap.a_swap_refund_value = ci_from.createScriptLockRefundTx(
 | 
					            xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script, xmr_swap.a_swap_refund_value = ci_from.createSCLockRefundTx(
 | 
				
			||||||
                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
					                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
				
			||||||
                xmr_swap.pkal, xmr_swap.pkaf,
 | 
					                xmr_swap.pkal, xmr_swap.pkaf,
 | 
				
			||||||
                xmr_offer.lock_time_1, xmr_offer.lock_time_2,
 | 
					                xmr_offer.lock_time_1, xmr_offer.lock_time_2,
 | 
				
			||||||
@ -2316,7 +2282,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            ensure(v, 'Invalid coin A lock refund tx leader sig')
 | 
					            ensure(v, 'Invalid coin A lock refund tx leader sig')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            pkh_refund_to = ci_from.decodeAddress(self.getReceiveAddressForCoin(coin_from))
 | 
					            pkh_refund_to = ci_from.decodeAddress(self.getReceiveAddressForCoin(coin_from))
 | 
				
			||||||
            xmr_swap.a_lock_refund_spend_tx = ci_from.createScriptLockRefundSpendTx(
 | 
					            xmr_swap.a_lock_refund_spend_tx = ci_from.createSCLockRefundSpendTx(
 | 
				
			||||||
                xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
 | 
					                xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
 | 
				
			||||||
                pkh_refund_to,
 | 
					                pkh_refund_to,
 | 
				
			||||||
                xmr_offer.a_fee_rate, xmr_swap.vkbv
 | 
					                xmr_offer.a_fee_rate, xmr_swap.vkbv
 | 
				
			||||||
@ -2326,7 +2292,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            # Double check txns before sending
 | 
					            # Double check txns before sending
 | 
				
			||||||
            self.log.debug('Bid: {} - Double checking chain A lock txns are valid before sending bid accept.'.format(bid_id.hex()))
 | 
					            self.log.debug('Bid: {} - Double checking chain A lock txns are valid before sending bid accept.'.format(bid_id.hex()))
 | 
				
			||||||
            check_lock_tx_inputs = False  # TODO: check_lock_tx_inputs without txindex
 | 
					            check_lock_tx_inputs = False  # TODO: check_lock_tx_inputs without txindex
 | 
				
			||||||
            _, xmr_swap.a_lock_tx_vout = ci_from.verifyLockTx(
 | 
					            _, xmr_swap.a_lock_tx_vout = ci_from.verifySCLockTx(
 | 
				
			||||||
                xmr_swap.a_lock_tx,
 | 
					                xmr_swap.a_lock_tx,
 | 
				
			||||||
                xmr_swap.a_lock_tx_script,
 | 
					                xmr_swap.a_lock_tx_script,
 | 
				
			||||||
                bid.amount,
 | 
					                bid.amount,
 | 
				
			||||||
@ -2336,7 +2302,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
                check_lock_tx_inputs,
 | 
					                check_lock_tx_inputs,
 | 
				
			||||||
                xmr_swap.vkbv)
 | 
					                xmr_swap.vkbv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _, _, lock_refund_vout = ci_from.verifyLockRefundTx(
 | 
					            _, _, lock_refund_vout = ci_from.verifySCLockRefundTx(
 | 
				
			||||||
                xmr_swap.a_lock_refund_tx,
 | 
					                xmr_swap.a_lock_refund_tx,
 | 
				
			||||||
                xmr_swap.a_lock_tx,
 | 
					                xmr_swap.a_lock_tx,
 | 
				
			||||||
                xmr_swap.a_lock_refund_tx_script,
 | 
					                xmr_swap.a_lock_refund_tx_script,
 | 
				
			||||||
@ -2351,7 +2317,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
                xmr_offer.a_fee_rate,
 | 
					                xmr_offer.a_fee_rate,
 | 
				
			||||||
                xmr_swap.vkbv)
 | 
					                xmr_swap.vkbv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ci_from.verifyLockRefundSpendTx(
 | 
					            ci_from.verifySCLockRefundSpendTx(
 | 
				
			||||||
                xmr_swap.a_lock_refund_spend_tx, xmr_swap.a_lock_refund_tx,
 | 
					                xmr_swap.a_lock_refund_spend_tx, xmr_swap.a_lock_refund_tx,
 | 
				
			||||||
                xmr_swap.a_lock_refund_tx_id, xmr_swap.a_lock_refund_tx_script,
 | 
					                xmr_swap.a_lock_refund_tx_id, xmr_swap.a_lock_refund_tx_script,
 | 
				
			||||||
                xmr_swap.pkal,
 | 
					                xmr_swap.pkal,
 | 
				
			||||||
@ -2602,8 +2568,8 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        assert (addr_redeem_out is not None)
 | 
					        assert (addr_redeem_out is not None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.coin_clients[coin_type]['use_segwit']:
 | 
					        if self.coin_clients[coin_type]['use_segwit']:
 | 
				
			||||||
            # Change to btc hrp
 | 
					            # Change to part hrp
 | 
				
			||||||
            addr_redeem_out = self.encodeSegwit(Coins.PART, self.decodeSegwit(coin_type, addr_redeem_out))
 | 
					            addr_redeem_out = self.ci(Coins.PART).encodeSegwitAddress(ci.decodeSegwitAddress(addr_redeem_out))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            addr_redeem_out = replaceAddrPrefix(addr_redeem_out, Coins.PART, self.chain)
 | 
					            addr_redeem_out = replaceAddrPrefix(addr_redeem_out, Coins.PART, self.chain)
 | 
				
			||||||
        self.log.debug('addr_redeem_out %s', addr_redeem_out)
 | 
					        self.log.debug('addr_redeem_out %s', addr_redeem_out)
 | 
				
			||||||
@ -2704,8 +2670,8 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            addr_refund_out = self.getReceiveAddressFromPool(coin_type, bid.bid_id, tx_type)
 | 
					            addr_refund_out = self.getReceiveAddressFromPool(coin_type, bid.bid_id, tx_type)
 | 
				
			||||||
        ensure(addr_refund_out is not None, 'addr_refund_out is null')
 | 
					        ensure(addr_refund_out is not None, 'addr_refund_out is null')
 | 
				
			||||||
        if self.coin_clients[coin_type]['use_segwit']:
 | 
					        if self.coin_clients[coin_type]['use_segwit']:
 | 
				
			||||||
            # Change to btc hrp
 | 
					            # Change to part hrp
 | 
				
			||||||
            addr_refund_out = self.encodeSegwit(Coins.PART, self.decodeSegwit(coin_type, addr_refund_out))
 | 
					            addr_refund_out = self.ci(Coins.PART).encodeSegwitAddress(ci.decodeSegwitAddress(addr_refund_out))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            addr_refund_out = replaceAddrPrefix(addr_refund_out, Coins.PART, self.chain)
 | 
					            addr_refund_out = replaceAddrPrefix(addr_refund_out, Coins.PART, self.chain)
 | 
				
			||||||
        self.log.debug('addr_refund_out %s', addr_refund_out)
 | 
					        self.log.debug('addr_refund_out %s', addr_refund_out)
 | 
				
			||||||
@ -2861,7 +2827,6 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            # TODO: random offset into explorers, try blocks
 | 
					            # TODO: random offset into explorers, try blocks
 | 
				
			||||||
            for exp in explorers:
 | 
					            for exp in explorers:
 | 
				
			||||||
 | 
					 | 
				
			||||||
                # TODO: ExplorerBitAps use only gettransaction if assert_txid is set
 | 
					                # TODO: ExplorerBitAps use only gettransaction if assert_txid is set
 | 
				
			||||||
                rv = exp.lookupUnspentByAddress(address)
 | 
					                rv = exp.lookupUnspentByAddress(address)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -3033,11 +2998,11 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                # TODO: Timeout waiting for transactions
 | 
					                # TODO: Timeout waiting for transactions
 | 
				
			||||||
                bid_changed = False
 | 
					                bid_changed = False
 | 
				
			||||||
                a_lock_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_tx_script)
 | 
					                if offer.coin_from == Coins.FIRO:
 | 
				
			||||||
                # Changed from ci_from.getOutput(bid.xmr_a_lock_tx.txid, a_lock_tx_dest, bid.amount, xmr_swap)
 | 
					                    lock_tx_chain_info = ci_from.getLockTxHeightFiro(bid.xmr_a_lock_tx.txid, xmr_swap.a_lock_tx_script, bid.amount, bid.chain_a_height_start)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
                p2wsh_addr = ci_from.encode_p2wsh(a_lock_tx_dest)
 | 
					                    a_lock_tx_addr = ci_from.getSCLockScriptAddress(xmr_swap.a_lock_tx_script)
 | 
				
			||||||
                lock_tx_chain_info = ci_from.getLockTxHeight(bid.xmr_a_lock_tx.txid, p2wsh_addr, bid.amount, bid.chain_a_height_start)
 | 
					                    lock_tx_chain_info = ci_from.getLockTxHeight(bid.xmr_a_lock_tx.txid, a_lock_tx_addr, bid.amount, bid.chain_a_height_start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if lock_tx_chain_info is None:
 | 
					                if lock_tx_chain_info is None:
 | 
				
			||||||
                    return rv
 | 
					                    return rv
 | 
				
			||||||
@ -3149,9 +3114,11 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
                if TxTypes.XMR_SWAP_A_LOCK_REFUND in bid.txns:
 | 
					                if TxTypes.XMR_SWAP_A_LOCK_REFUND in bid.txns:
 | 
				
			||||||
                    refund_tx = bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND]
 | 
					                    refund_tx = bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND]
 | 
				
			||||||
                    if refund_tx.block_time is None:
 | 
					                    if refund_tx.block_time is None:
 | 
				
			||||||
                        a_lock_refund_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_refund_tx_script)
 | 
					                        if offer.coin_from == Coins.FIRO:
 | 
				
			||||||
                        p2wsh_addr = ci_from.encode_p2wsh(a_lock_refund_tx_dest)
 | 
					                            lock_refund_tx_chain_info = ci_from.getLockTxHeightFiro(refund_tx.txid, xmr_swap.a_lock_refund_tx_script, 0, bid.chain_a_height_start)
 | 
				
			||||||
                        lock_refund_tx_chain_info = ci_from.getLockTxHeight(refund_tx.txid, p2wsh_addr, 0, bid.chain_a_height_start)
 | 
					                        else:
 | 
				
			||||||
 | 
					                            refund_tx_addr = ci_from.getSCLockScriptAddress(xmr_swap.a_lock_refund_tx_script)
 | 
				
			||||||
 | 
					                            lock_refund_tx_chain_info = ci_from.getLockTxHeight(refund_tx.txid, refund_tx_addr, 0, bid.chain_a_height_start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if lock_refund_tx_chain_info is not None and lock_refund_tx_chain_info.get('height', 0) > 0:
 | 
					                        if lock_refund_tx_chain_info is not None and lock_refund_tx_chain_info.get('height', 0) > 0:
 | 
				
			||||||
                            block_header = ci_from.getBlockHeaderFromHeight(lock_refund_tx_chain_info['height'])
 | 
					                            block_header = ci_from.getBlockHeaderFromHeight(lock_refund_tx_chain_info['height'])
 | 
				
			||||||
@ -4015,18 +3982,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        if swap_type == SwapTypes.SELLER_FIRST:
 | 
					        if swap_type == SwapTypes.SELLER_FIRST:
 | 
				
			||||||
            ensure(len(bid_data.pkhash_buyer) == 20, 'Bad pkhash_buyer length')
 | 
					            ensure(len(bid_data.pkhash_buyer) == 20, 'Bad pkhash_buyer length')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Verify proof of funds
 | 
					            sum_unspent = ci_to.verifyProofOfFunds(bid_data.proof_address, bid_data.proof_signature, offer_id)
 | 
				
			||||||
            bid_proof_address = replaceAddrPrefix(bid_data.proof_address, Coins.PART, self.chain)
 | 
					 | 
				
			||||||
            mm = chainparams[coin_to]['message_magic']
 | 
					 | 
				
			||||||
            passed = self.ci(Coins.PART).verifyMessage(bid_proof_address, bid_data.proof_address + '_swap_proof_' + offer_id.hex(), bid_data.proof_signature, mm)
 | 
					 | 
				
			||||||
            ensure(passed is True, 'Proof of funds signature invalid')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if self.coin_clients[coin_to]['use_segwit']:
 | 
					 | 
				
			||||||
                addr_search = self.encodeSegwit(coin_to, decodeAddress(bid_data.proof_address)[1:])
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                addr_search = bid_data.proof_address
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            sum_unspent = self.getAddressBalance(coin_to, addr_search)
 | 
					 | 
				
			||||||
            self.log.debug('Proof of funds %s %s', bid_data.proof_address, self.ci(coin_to).format_amount(sum_unspent))
 | 
					            self.log.debug('Proof of funds %s %s', bid_data.proof_address, self.ci(coin_to).format_amount(sum_unspent))
 | 
				
			||||||
            ensure(sum_unspent >= amount_to, 'Proof of funds failed')
 | 
					            ensure(sum_unspent >= amount_to, 'Proof of funds failed')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -4382,7 +4338,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            # TODO: check_lock_tx_inputs without txindex
 | 
					            # TODO: check_lock_tx_inputs without txindex
 | 
				
			||||||
            check_a_lock_tx_inputs = False
 | 
					            check_a_lock_tx_inputs = False
 | 
				
			||||||
            xmr_swap.a_lock_tx_id, xmr_swap.a_lock_tx_vout = ci_from.verifyLockTx(
 | 
					            xmr_swap.a_lock_tx_id, xmr_swap.a_lock_tx_vout = ci_from.verifySCLockTx(
 | 
				
			||||||
                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
					                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
				
			||||||
                bid.amount,
 | 
					                bid.amount,
 | 
				
			||||||
                xmr_swap.pkal, xmr_swap.pkaf,
 | 
					                xmr_swap.pkal, xmr_swap.pkaf,
 | 
				
			||||||
@ -4390,14 +4346,14 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
                check_a_lock_tx_inputs, xmr_swap.vkbv)
 | 
					                check_a_lock_tx_inputs, xmr_swap.vkbv)
 | 
				
			||||||
            a_lock_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_tx_script)
 | 
					            a_lock_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_tx_script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            xmr_swap.a_lock_refund_tx_id, xmr_swap.a_swap_refund_value, lock_refund_vout = ci_from.verifyLockRefundTx(
 | 
					            xmr_swap.a_lock_refund_tx_id, xmr_swap.a_swap_refund_value, lock_refund_vout = ci_from.verifySCLockRefundTx(
 | 
				
			||||||
                xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_tx, xmr_swap.a_lock_refund_tx_script,
 | 
					                xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_tx, xmr_swap.a_lock_refund_tx_script,
 | 
				
			||||||
                xmr_swap.a_lock_tx_id, xmr_swap.a_lock_tx_vout, xmr_offer.lock_time_1, xmr_swap.a_lock_tx_script,
 | 
					                xmr_swap.a_lock_tx_id, xmr_swap.a_lock_tx_vout, xmr_offer.lock_time_1, xmr_swap.a_lock_tx_script,
 | 
				
			||||||
                xmr_swap.pkal, xmr_swap.pkaf,
 | 
					                xmr_swap.pkal, xmr_swap.pkaf,
 | 
				
			||||||
                xmr_offer.lock_time_2,
 | 
					                xmr_offer.lock_time_2,
 | 
				
			||||||
                bid.amount, xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
					                bid.amount, xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ci_from.verifyLockRefundSpendTx(
 | 
					            ci_from.verifySCLockRefundSpendTx(
 | 
				
			||||||
                xmr_swap.a_lock_refund_spend_tx, xmr_swap.a_lock_refund_tx,
 | 
					                xmr_swap.a_lock_refund_spend_tx, xmr_swap.a_lock_refund_tx,
 | 
				
			||||||
                xmr_swap.a_lock_refund_tx_id, xmr_swap.a_lock_refund_tx_script,
 | 
					                xmr_swap.a_lock_refund_tx_id, xmr_swap.a_lock_refund_tx_script,
 | 
				
			||||||
                xmr_swap.pkal,
 | 
					                xmr_swap.pkal,
 | 
				
			||||||
@ -4517,7 +4473,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        xmr_swap.kal_sig = ci_from.signCompact(kal, 'proof key owned for swap')
 | 
					        xmr_swap.kal_sig = ci_from.signCompact(kal, 'proof key owned for swap')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Create Script lock spend tx
 | 
					        # Create Script lock spend tx
 | 
				
			||||||
        xmr_swap.a_lock_spend_tx = ci_from.createScriptLockSpendTx(
 | 
					        xmr_swap.a_lock_spend_tx = ci_from.createSCLockSpendTx(
 | 
				
			||||||
            xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
					            xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
				
			||||||
            xmr_swap.dest_af,
 | 
					            xmr_swap.dest_af,
 | 
				
			||||||
            xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
					            xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
				
			||||||
@ -4923,12 +4879,12 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            xmr_swap.a_lock_spend_tx_id = ci_from.getTxid(xmr_swap.a_lock_spend_tx)
 | 
					            xmr_swap.a_lock_spend_tx_id = ci_from.getTxid(xmr_swap.a_lock_spend_tx)
 | 
				
			||||||
            xmr_swap.kal_sig = msg_data.kal_sig
 | 
					            xmr_swap.kal_sig = msg_data.kal_sig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ci_from.verifyLockSpendTx(
 | 
					            ci_from.verifySCLockSpendTx(
 | 
				
			||||||
                xmr_swap.a_lock_spend_tx,
 | 
					                xmr_swap.a_lock_spend_tx,
 | 
				
			||||||
                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
					                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
				
			||||||
                xmr_swap.dest_af, xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
					                xmr_swap.dest_af, xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ci_from.verifyCompact(xmr_swap.pkal, 'proof key owned for swap', xmr_swap.kal_sig)
 | 
					            ci_from.verifyCompactSig(xmr_swap.pkal, 'proof key owned for swap', xmr_swap.kal_sig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX)
 | 
					            bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX)
 | 
				
			||||||
            bid.setState(BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX)
 | 
					            bid.setState(BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX)
 | 
				
			||||||
@ -5865,7 +5821,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        self.log.debug('Creating %s lock refund swipe tx', ci.coin_name())
 | 
					        self.log.debug('Creating %s lock refund swipe tx', ci.coin_name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pkh_dest = ci.decodeAddress(self.getReceiveAddressForCoin(ci.coin_type()))
 | 
					        pkh_dest = ci.decodeAddress(self.getReceiveAddressForCoin(ci.coin_type()))
 | 
				
			||||||
        spend_tx = ci.createScriptLockRefundSpendToFTx(
 | 
					        spend_tx = ci.createSCLockRefundSpendToFTx(
 | 
				
			||||||
            xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
 | 
					            xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
 | 
				
			||||||
            pkh_dest,
 | 
					            pkh_dest,
 | 
				
			||||||
            xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
					            xmr_offer.a_fee_rate, xmr_swap.vkbv)
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@ import struct
 | 
				
			|||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
from enum import IntEnum, auto
 | 
					from enum import IntEnum, auto
 | 
				
			||||||
from .util.address import (
 | 
					from .util.address import (
 | 
				
			||||||
    decodeAddress,
 | 
					 | 
				
			||||||
    encodeAddress,
 | 
					    encodeAddress,
 | 
				
			||||||
 | 
					    decodeAddress,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from .chainparams import (
 | 
					from .chainparams import (
 | 
				
			||||||
    chainparams,
 | 
					    chainparams,
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@ class Coins(IntEnum):
 | 
				
			|||||||
    # NDAU = 10
 | 
					    # NDAU = 10
 | 
				
			||||||
    PIVX = 11
 | 
					    PIVX = 11
 | 
				
			||||||
    DASH = 12
 | 
					    DASH = 12
 | 
				
			||||||
 | 
					    FIRO = 13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
chainparams = {
 | 
					chainparams = {
 | 
				
			||||||
@ -287,6 +288,45 @@ chainparams = {
 | 
				
			|||||||
            'min_amount': 1000,
 | 
					            'min_amount': 1000,
 | 
				
			||||||
            'max_amount': 100000 * COIN,
 | 
					            'max_amount': 100000 * COIN,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Coins.FIRO: {
 | 
				
			||||||
 | 
					        'name': 'firo',
 | 
				
			||||||
 | 
					        'ticker': 'FIRO',
 | 
				
			||||||
 | 
					        'message_magic': 'Zcoin Signed Message:\n',
 | 
				
			||||||
 | 
					        'blocks_target': 60 * 10,
 | 
				
			||||||
 | 
					        'decimal_places': 8,
 | 
				
			||||||
 | 
					        'has_csv': True,
 | 
				
			||||||
 | 
					        'has_segwit': True,
 | 
				
			||||||
 | 
					        'mainnet': {
 | 
				
			||||||
 | 
					            'rpcport': 8888,
 | 
				
			||||||
 | 
					            'pubkey_address': 82,
 | 
				
			||||||
 | 
					            'script_address': 7,
 | 
				
			||||||
 | 
					            'key_prefix': 210,
 | 
				
			||||||
 | 
					            'hrp': '',
 | 
				
			||||||
 | 
					            'bip44': 136,
 | 
				
			||||||
 | 
					            'min_amount': 1000,
 | 
				
			||||||
 | 
					            'max_amount': 100000 * COIN,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'testnet': {
 | 
				
			||||||
 | 
					            'rpcport': 18888,
 | 
				
			||||||
 | 
					            'pubkey_address': 65,
 | 
				
			||||||
 | 
					            'script_address': 178,
 | 
				
			||||||
 | 
					            'key_prefix': 185,
 | 
				
			||||||
 | 
					            'hrp': '',
 | 
				
			||||||
 | 
					            'bip44': 1,
 | 
				
			||||||
 | 
					            'min_amount': 1000,
 | 
				
			||||||
 | 
					            'max_amount': 100000 * COIN,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'regtest': {
 | 
				
			||||||
 | 
					            'rpcport': 28888,
 | 
				
			||||||
 | 
					            'pubkey_address': 65,
 | 
				
			||||||
 | 
					            'script_address': 178,
 | 
				
			||||||
 | 
					            'key_prefix': 185,
 | 
				
			||||||
 | 
					            'hrp': '',
 | 
				
			||||||
 | 
					            'bip44': 1,
 | 
				
			||||||
 | 
					            'min_amount': 1000,
 | 
				
			||||||
 | 
					            'max_amount': 100000 * COIN,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
ticker_map = {}
 | 
					ticker_map = {}
 | 
				
			||||||
 | 
				
			|||||||
@ -46,3 +46,8 @@ DASH_BINDIR = os.path.expanduser(os.getenv('DASH_BINDIR', os.path.join(DEFAULT_T
 | 
				
			|||||||
DASHD = os.getenv('DASHD', 'dashd' + bin_suffix)
 | 
					DASHD = os.getenv('DASHD', 'dashd' + bin_suffix)
 | 
				
			||||||
DASH_CLI = os.getenv('DASH_CLI', 'dash-cli' + bin_suffix)
 | 
					DASH_CLI = os.getenv('DASH_CLI', 'dash-cli' + bin_suffix)
 | 
				
			||||||
DASH_TX = os.getenv('DASH_TX', 'dash-tx' + bin_suffix)
 | 
					DASH_TX = os.getenv('DASH_TX', 'dash-tx' + bin_suffix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FIRO_BINDIR = os.path.expanduser(os.getenv('FIRO_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'firo')))
 | 
				
			||||||
 | 
					FIROD = os.getenv('FIROD', 'firod' + bin_suffix)
 | 
				
			||||||
 | 
					FIRO_CLI = os.getenv('FIRO_CLI', 'firo-cli' + bin_suffix)
 | 
				
			||||||
 | 
					FIRO_TX = os.getenv('FIRO_TX', 'firo-tx' + bin_suffix)
 | 
				
			||||||
 | 
				
			|||||||
@ -320,7 +320,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def decodeAddress(self, address):
 | 
					    def decodeAddress(self, address):
 | 
				
			||||||
        bech32_prefix = self.chainparams_network()['hrp']
 | 
					        bech32_prefix = self.chainparams_network()['hrp']
 | 
				
			||||||
        if address.startswith(bech32_prefix + '1'):
 | 
					        if len(bech32_prefix) > 0 and address.startswith(bech32_prefix + '1'):
 | 
				
			||||||
            return bytes(segwit_addr.decode(bech32_prefix, address)[1])
 | 
					            return bytes(segwit_addr.decode(bech32_prefix, address)[1])
 | 
				
			||||||
        return decodeAddress(address)[1:]
 | 
					        return decodeAddress(address)[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -338,12 +338,22 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        checksum = hashlib.sha256(hashlib.sha256(data).digest()).digest()
 | 
					        checksum = hashlib.sha256(hashlib.sha256(data).digest()).digest()
 | 
				
			||||||
        return b58encode(data + checksum[0:4])
 | 
					        return b58encode(data + checksum[0:4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sh_to_address(self, sh):
 | 
				
			||||||
 | 
					        assert (len(sh) == 20)
 | 
				
			||||||
 | 
					        prefix = self.chainparams_network()['script_address']
 | 
				
			||||||
 | 
					        data = bytes((prefix,)) + sh
 | 
				
			||||||
 | 
					        checksum = hashlib.sha256(hashlib.sha256(data).digest()).digest()
 | 
				
			||||||
 | 
					        return b58encode(data + checksum[0:4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def encode_p2wsh(self, script):
 | 
					    def encode_p2wsh(self, script):
 | 
				
			||||||
        bech32_prefix = self.chainparams_network()['hrp']
 | 
					        bech32_prefix = self.chainparams_network()['hrp']
 | 
				
			||||||
        version = 0
 | 
					        version = 0
 | 
				
			||||||
        program = script[2:]  # strip version and length
 | 
					        program = script[2:]  # strip version and length
 | 
				
			||||||
        return segwit_addr.encode(bech32_prefix, version, program)
 | 
					        return segwit_addr.encode(bech32_prefix, version, program)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeScriptDest(self, script):
 | 
				
			||||||
 | 
					        return self.encode_p2wsh(script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def encode_p2sh(self, script):
 | 
					    def encode_p2sh(self, script):
 | 
				
			||||||
        return pubkeyToAddress(self.chainparams_network()['script_address'], script)
 | 
					        return pubkeyToAddress(self.chainparams_network()['script_address'], script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -375,6 +385,12 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
    def encodePubkey(self, pk):
 | 
					    def encodePubkey(self, pk):
 | 
				
			||||||
        return pointToCPK(pk)
 | 
					        return pointToCPK(pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeSegwitAddress(self, key_hash):
 | 
				
			||||||
 | 
					        return segwit_addr.encode(self.chainparams_network()['hrp'], 0, key_hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decodeSegwitAddress(self, addr):
 | 
				
			||||||
 | 
					        return bytes(segwit_addr.decode(self.chainparams_network()['hrp'], addr)[1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def decodePubkey(self, pke):
 | 
					    def decodePubkey(self, pke):
 | 
				
			||||||
        return CPKToPoint(pke)
 | 
					        return CPKToPoint(pke)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -415,7 +431,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)])
 | 
					        return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockTx(self, value, Kal, Kaf, vkbv=None):
 | 
					    def createSCLockTx(self, value, Kal, Kaf, vkbv=None):
 | 
				
			||||||
        script = self.genScriptLockTxScript(Kal, Kaf)
 | 
					        script = self.genScriptLockTxScript(Kal, Kaf)
 | 
				
			||||||
        tx = CTransaction()
 | 
					        tx = CTransaction()
 | 
				
			||||||
        tx.nVersion = self.txVersion()
 | 
					        tx.nVersion = self.txVersion()
 | 
				
			||||||
@ -423,7 +439,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return tx.serialize(), script
 | 
					        return tx.serialize(), script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fundScriptLockTx(self, tx_bytes, feerate, vkbv=None):
 | 
					    def fundSCLockTx(self, tx_bytes, feerate, vkbv=None):
 | 
				
			||||||
        return self.fundTx(tx_bytes, feerate)
 | 
					        return self.fundTx(tx_bytes, feerate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def extractScriptLockRefundScriptValues(self, script_bytes):
 | 
					    def extractScriptLockRefundScriptValues(self, script_bytes):
 | 
				
			||||||
@ -470,11 +486,11 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
            Kaf_enc, CScriptOp(OP_CHECKSIG),
 | 
					            Kaf_enc, CScriptOp(OP_CHECKSIG),
 | 
				
			||||||
            CScriptOp(OP_ENDIF)])
 | 
					            CScriptOp(OP_ENDIF)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv=None):
 | 
					    def createSCLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv=None):
 | 
				
			||||||
        tx_lock = CTransaction()
 | 
					        tx_lock = CTransaction()
 | 
				
			||||||
        tx_lock = FromHex(tx_lock, tx_lock_bytes.hex())
 | 
					        tx_lock = FromHex(tx_lock, tx_lock_bytes.hex())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        output_script = CScript([OP_0, hashlib.sha256(script_lock).digest()])
 | 
					        output_script = self.getScriptDest(script_lock)
 | 
				
			||||||
        locked_n = findOutput(tx_lock, output_script)
 | 
					        locked_n = findOutput(tx_lock, output_script)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = tx_lock.vout[locked_n].nValue
 | 
					        locked_coin = tx_lock.vout[locked_n].nValue
 | 
				
			||||||
@ -485,8 +501,10 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        refund_script = self.genScriptLockRefundTxScript(Kal, Kaf, csv_val)
 | 
					        refund_script = self.genScriptLockRefundTxScript(Kal, Kaf, csv_val)
 | 
				
			||||||
        tx = CTransaction()
 | 
					        tx = CTransaction()
 | 
				
			||||||
        tx.nVersion = self.txVersion()
 | 
					        tx.nVersion = self.txVersion()
 | 
				
			||||||
        tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n), nSequence=lock1_value))
 | 
					        tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n),
 | 
				
			||||||
        tx.vout.append(self.txoType()(locked_coin, CScript([OP_0, hashlib.sha256(refund_script).digest()])))
 | 
					                            nSequence=lock1_value,
 | 
				
			||||||
 | 
					                            scriptSig=self.getScriptScriptSig(script_lock)))
 | 
				
			||||||
 | 
					        tx.vout.append(self.txoType()(locked_coin, self.getScriptDest(refund_script)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
 | 
					        dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
 | 
				
			||||||
        witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
 | 
					        witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
 | 
				
			||||||
@ -495,19 +513,19 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        tx.vout[0].nValue = locked_coin - pay_fee
 | 
					        tx.vout[0].nValue = locked_coin - pay_fee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.rehash()
 | 
					        tx.rehash()
 | 
				
			||||||
        self._log.info('createScriptLockRefundTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
					        self._log.info('createSCLockRefundTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
				
			||||||
                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
					                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return tx.serialize(), refund_script, tx.vout[0].nValue
 | 
					        return tx.serialize(), refund_script, tx.vout[0].nValue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockRefundSpendTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_refund_to, tx_fee_rate, vkbv=None):
 | 
					    def createSCLockRefundSpendTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_refund_to, tx_fee_rate, vkbv=None):
 | 
				
			||||||
        # Returns the coinA locked coin to the leader
 | 
					        # Returns the coinA locked coin to the leader
 | 
				
			||||||
        # The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
 | 
					        # The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
 | 
				
			||||||
        # If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
 | 
					        # If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx_lock_refund = self.loadTx(tx_lock_refund_bytes)
 | 
					        tx_lock_refund = self.loadTx(tx_lock_refund_bytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        output_script = CScript([OP_0, hashlib.sha256(script_lock_refund).digest()])
 | 
					        output_script = self.getScriptDest(script_lock_refund)
 | 
				
			||||||
        locked_n = findOutput(tx_lock_refund, output_script)
 | 
					        locked_n = findOutput(tx_lock_refund, output_script)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = tx_lock_refund.vout[locked_n].nValue
 | 
					        locked_coin = tx_lock_refund.vout[locked_n].nValue
 | 
				
			||||||
@ -517,7 +535,9 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        tx = CTransaction()
 | 
					        tx = CTransaction()
 | 
				
			||||||
        tx.nVersion = self.txVersion()
 | 
					        tx.nVersion = self.txVersion()
 | 
				
			||||||
        tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n), nSequence=0))
 | 
					        tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n),
 | 
				
			||||||
 | 
					                            nSequence=0,
 | 
				
			||||||
 | 
					                            scriptSig=self.getScriptScriptSig(script_lock_refund)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_refund_to)))
 | 
					        tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_refund_to)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -528,18 +548,18 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        tx.vout[0].nValue = locked_coin - pay_fee
 | 
					        tx.vout[0].nValue = locked_coin - pay_fee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.rehash()
 | 
					        tx.rehash()
 | 
				
			||||||
        self._log.info('createScriptLockRefundSpendTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
					        self._log.info('createSCLockRefundSpendTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
				
			||||||
                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
					                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return tx.serialize()
 | 
					        return tx.serialize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockRefundSpendToFTx(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):
 | 
				
			||||||
        # lock refund swipe tx
 | 
					        # lock refund swipe tx
 | 
				
			||||||
        # Sends the coinA locked coin to the follower
 | 
					        # Sends the coinA locked coin to the follower
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx_lock_refund = self.loadTx(tx_lock_refund_bytes)
 | 
					        tx_lock_refund = self.loadTx(tx_lock_refund_bytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        output_script = CScript([OP_0, hashlib.sha256(script_lock_refund).digest()])
 | 
					        output_script = self.getScriptDest(script_lock_refund)
 | 
				
			||||||
        locked_n = findOutput(tx_lock_refund, output_script)
 | 
					        locked_n = findOutput(tx_lock_refund, output_script)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = tx_lock_refund.vout[locked_n].nValue
 | 
					        locked_coin = tx_lock_refund.vout[locked_n].nValue
 | 
				
			||||||
@ -551,7 +571,9 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        tx = CTransaction()
 | 
					        tx = CTransaction()
 | 
				
			||||||
        tx.nVersion = self.txVersion()
 | 
					        tx.nVersion = self.txVersion()
 | 
				
			||||||
        tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n), nSequence=lock2_value))
 | 
					        tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n),
 | 
				
			||||||
 | 
					                            nSequence=lock2_value,
 | 
				
			||||||
 | 
					                            scriptSig=self.getScriptScriptSig(script_lock_refund)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
 | 
					        tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -562,14 +584,14 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        tx.vout[0].nValue = locked_coin - pay_fee
 | 
					        tx.vout[0].nValue = locked_coin - pay_fee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.rehash()
 | 
					        tx.rehash()
 | 
				
			||||||
        self._log.info('createScriptLockRefundSpendToFTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
					        self._log.info('createSCLockRefundSpendToFTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
				
			||||||
                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
					                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return tx.serialize()
 | 
					        return tx.serialize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None):
 | 
					    def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None):
 | 
				
			||||||
        tx_lock = self.loadTx(tx_lock_bytes)
 | 
					        tx_lock = self.loadTx(tx_lock_bytes)
 | 
				
			||||||
        output_script = CScript([OP_0, hashlib.sha256(script_lock).digest()])
 | 
					        output_script = self.getScriptDest(script_lock)
 | 
				
			||||||
        locked_n = findOutput(tx_lock, output_script)
 | 
					        locked_n = findOutput(tx_lock, output_script)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = tx_lock.vout[locked_n].nValue
 | 
					        locked_coin = tx_lock.vout[locked_n].nValue
 | 
				
			||||||
@ -579,7 +601,8 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        tx = CTransaction()
 | 
					        tx = CTransaction()
 | 
				
			||||||
        tx.nVersion = self.txVersion()
 | 
					        tx.nVersion = self.txVersion()
 | 
				
			||||||
        tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n)))
 | 
					        tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n),
 | 
				
			||||||
 | 
					                            scriptSig=self.getScriptScriptSig(script_lock)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
 | 
					        tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -590,16 +613,16 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        tx.vout[0].nValue = locked_coin - pay_fee
 | 
					        tx.vout[0].nValue = locked_coin - pay_fee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx.rehash()
 | 
					        tx.rehash()
 | 
				
			||||||
        self._log.info('createScriptLockSpendTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
					        self._log.info('createSCLockSpendTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
				
			||||||
                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
					                       i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return tx.serialize()
 | 
					        return tx.serialize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockTx(self, tx_bytes, script_out,
 | 
					    def verifySCLockTx(self, tx_bytes, script_out,
 | 
				
			||||||
                     swap_value,
 | 
					                       swap_value,
 | 
				
			||||||
                     Kal, Kaf,
 | 
					                       Kal, Kaf,
 | 
				
			||||||
                     feerate,
 | 
					                       feerate,
 | 
				
			||||||
                     check_lock_tx_inputs, vkbv=None):
 | 
					                       check_lock_tx_inputs, vkbv=None):
 | 
				
			||||||
        # Verify:
 | 
					        # Verify:
 | 
				
			||||||
        #
 | 
					        #
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -614,7 +637,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        ensure(tx.nVersion == self.txVersion(), 'Bad version')
 | 
					        ensure(tx.nVersion == self.txVersion(), 'Bad version')
 | 
				
			||||||
        ensure(tx.nLockTime == 0, 'Bad nLockTime')  # TODO match txns created by cores
 | 
					        ensure(tx.nLockTime == 0, 'Bad nLockTime')  # TODO match txns created by cores
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script_pk = CScript([OP_0, hashlib.sha256(script_out).digest()])
 | 
					        script_pk = self.getScriptDest(script_out)
 | 
				
			||||||
        locked_n = findOutput(tx, script_pk)
 | 
					        locked_n = findOutput(tx, script_pk)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = tx.vout[locked_n].nValue
 | 
					        locked_coin = tx.vout[locked_n].nValue
 | 
				
			||||||
@ -663,9 +686,9 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return txid, locked_n
 | 
					        return txid, locked_n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockRefundTx(self, tx_bytes, lock_tx_bytes, script_out,
 | 
					    def verifySCLockRefundTx(self, tx_bytes, lock_tx_bytes, script_out,
 | 
				
			||||||
                           prevout_id, prevout_n, prevout_seq, prevout_script,
 | 
					                             prevout_id, prevout_n, prevout_seq, prevout_script,
 | 
				
			||||||
                           Kal, Kaf, csv_val_expect, swap_value, feerate, vkbv=None):
 | 
					                             Kal, Kaf, csv_val_expect, swap_value, feerate, vkbv=None):
 | 
				
			||||||
        # Verify:
 | 
					        # Verify:
 | 
				
			||||||
        #   Must have only one input with correct prevout and sequence
 | 
					        #   Must have only one input with correct prevout and sequence
 | 
				
			||||||
        #   Must have only one output to the p2wsh of the lock refund script
 | 
					        #   Must have only one output to the p2wsh of the lock refund script
 | 
				
			||||||
@ -680,12 +703,12 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        ensure(len(tx.vin) == 1, 'tx doesn\'t have one input')
 | 
					        ensure(len(tx.vin) == 1, 'tx doesn\'t have one input')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ensure(tx.vin[0].nSequence == prevout_seq, 'Bad input nSequence')
 | 
					        ensure(tx.vin[0].nSequence == prevout_seq, 'Bad input nSequence')
 | 
				
			||||||
        ensure(len(tx.vin[0].scriptSig) == 0, 'Input scriptsig not empty')
 | 
					        ensure(tx.vin[0].scriptSig == self.getScriptScriptSig(prevout_script), 'Input scriptsig mismatch')
 | 
				
			||||||
        ensure(tx.vin[0].prevout.hash == b2i(prevout_id) and tx.vin[0].prevout.n == prevout_n, 'Input prevout mismatch')
 | 
					        ensure(tx.vin[0].prevout.hash == b2i(prevout_id) and tx.vin[0].prevout.n == prevout_n, 'Input prevout mismatch')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ensure(len(tx.vout) == 1, 'tx doesn\'t have one output')
 | 
					        ensure(len(tx.vout) == 1, 'tx doesn\'t have one output')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script_pk = CScript([OP_0, hashlib.sha256(script_out).digest()])
 | 
					        script_pk = self.getScriptDest(script_out)
 | 
				
			||||||
        locked_n = findOutput(tx, script_pk)
 | 
					        locked_n = findOutput(tx, script_pk)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = tx.vout[locked_n].nValue
 | 
					        locked_coin = tx.vout[locked_n].nValue
 | 
				
			||||||
@ -712,10 +735,10 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return txid, locked_coin, locked_n
 | 
					        return txid, locked_coin, locked_n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockRefundSpendTx(self, tx_bytes, lock_refund_tx_bytes,
 | 
					    def verifySCLockRefundSpendTx(self, tx_bytes, lock_refund_tx_bytes,
 | 
				
			||||||
                                lock_refund_tx_id, prevout_script,
 | 
					                                  lock_refund_tx_id, prevout_script,
 | 
				
			||||||
                                Kal,
 | 
					                                  Kal,
 | 
				
			||||||
                                prevout_n, prevout_value, feerate, vkbv=None):
 | 
					                                  prevout_n, prevout_value, feerate, vkbv=None):
 | 
				
			||||||
        # Verify:
 | 
					        # Verify:
 | 
				
			||||||
        #   Must have only one input with correct prevout (n is always 0) and sequence
 | 
					        #   Must have only one input with correct prevout (n is always 0) and sequence
 | 
				
			||||||
        #   Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
 | 
					        #   Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
 | 
				
			||||||
@ -728,7 +751,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        ensure(len(tx.vin) == 1, 'tx doesn\'t have one input')
 | 
					        ensure(len(tx.vin) == 1, 'tx doesn\'t have one input')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ensure(tx.vin[0].nSequence == 0, 'Bad input nSequence')
 | 
					        ensure(tx.vin[0].nSequence == 0, 'Bad input nSequence')
 | 
				
			||||||
        ensure(len(tx.vin[0].scriptSig) == 0, 'Input scriptsig not empty')
 | 
					        ensure(tx.vin[0].scriptSig == self.getScriptScriptSig(prevout_script), 'Input scriptsig mismatch')
 | 
				
			||||||
        ensure(tx.vin[0].prevout.hash == b2i(lock_refund_tx_id) and tx.vin[0].prevout.n == 0, 'Input prevout mismatch')
 | 
					        ensure(tx.vin[0].prevout.hash == b2i(lock_refund_tx_id) and tx.vin[0].prevout.n == 0, 'Input prevout mismatch')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ensure(len(tx.vout) == 1, 'tx doesn\'t have one output')
 | 
					        ensure(len(tx.vout) == 1, 'tx doesn\'t have one output')
 | 
				
			||||||
@ -756,9 +779,9 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockSpendTx(self, tx_bytes,
 | 
					    def verifySCLockSpendTx(self, tx_bytes,
 | 
				
			||||||
                          lock_tx_bytes, lock_tx_script,
 | 
					                            lock_tx_bytes, lock_tx_script,
 | 
				
			||||||
                          a_pkhash_f, feerate, vkbv=None):
 | 
					                            a_pkhash_f, feerate, vkbv=None):
 | 
				
			||||||
        # Verify:
 | 
					        # Verify:
 | 
				
			||||||
        #   Must have only one input with correct prevout (n is always 0) and sequence
 | 
					        #   Must have only one input with correct prevout (n is always 0) and sequence
 | 
				
			||||||
        #   Must have only one output with destination and amount
 | 
					        #   Must have only one output with destination and amount
 | 
				
			||||||
@ -774,13 +797,13 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        lock_tx = self.loadTx(lock_tx_bytes)
 | 
					        lock_tx = self.loadTx(lock_tx_bytes)
 | 
				
			||||||
        lock_tx_id = self.getTxid(lock_tx)
 | 
					        lock_tx_id = self.getTxid(lock_tx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        output_script = CScript([OP_0, hashlib.sha256(lock_tx_script).digest()])
 | 
					        output_script = self.getScriptDest(lock_tx_script)
 | 
				
			||||||
        locked_n = findOutput(lock_tx, output_script)
 | 
					        locked_n = findOutput(lock_tx, output_script)
 | 
				
			||||||
        ensure(locked_n is not None, 'Output not found in tx')
 | 
					        ensure(locked_n is not None, 'Output not found in tx')
 | 
				
			||||||
        locked_coin = lock_tx.vout[locked_n].nValue
 | 
					        locked_coin = lock_tx.vout[locked_n].nValue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ensure(tx.vin[0].nSequence == 0, 'Bad input nSequence')
 | 
					        ensure(tx.vin[0].nSequence == 0, 'Bad input nSequence')
 | 
				
			||||||
        ensure(len(tx.vin[0].scriptSig) == 0, 'Input scriptsig not empty')
 | 
					        ensure(tx.vin[0].scriptSig == self.getScriptScriptSig(lock_tx_script), 'Input scriptsig mismatch')
 | 
				
			||||||
        ensure(tx.vin[0].prevout.hash == b2i(lock_tx_id) and tx.vin[0].prevout.n == locked_n, 'Input prevout mismatch')
 | 
					        ensure(tx.vin[0].prevout.hash == b2i(lock_tx_id) and tx.vin[0].prevout.n == locked_n, 'Input prevout mismatch')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ensure(len(tx.vout) == 1, 'tx doesn\'t have one output')
 | 
					        ensure(len(tx.vout) == 1, 'tx doesn\'t have one output')
 | 
				
			||||||
@ -891,7 +914,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
    def getTxOutputPos(self, tx, script):
 | 
					    def getTxOutputPos(self, tx, script):
 | 
				
			||||||
        if isinstance(tx, bytes):
 | 
					        if isinstance(tx, bytes):
 | 
				
			||||||
            tx = self.loadTx(tx)
 | 
					            tx = self.loadTx(tx)
 | 
				
			||||||
        script_pk = CScript([OP_0, hashlib.sha256(script).digest()])
 | 
					        script_pk = self.getScriptDest(script)
 | 
				
			||||||
        return findOutput(tx, script_pk)
 | 
					        return findOutput(tx, script_pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getPubkeyHash(self, K):
 | 
					    def getPubkeyHash(self, K):
 | 
				
			||||||
@ -900,6 +923,9 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
    def getScriptDest(self, script):
 | 
					    def getScriptDest(self, script):
 | 
				
			||||||
        return CScript([OP_0, hashlib.sha256(script).digest()])
 | 
					        return CScript([OP_0, hashlib.sha256(script).digest()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getScriptScriptSig(self, script):
 | 
				
			||||||
 | 
					        return bytes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getPkDest(self, K):
 | 
					    def getPkDest(self, K):
 | 
				
			||||||
        return self.getScriptForPubkeyHash(self.getPubkeyHash(K))
 | 
					        return self.getScriptForPubkeyHash(self.getPubkeyHash(K))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1003,11 +1029,22 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
    def spendBLockTx(self, chain_b_lock_txid, address_to, kbv, kbs, cb_swap_value, b_fee, restore_height):
 | 
					    def spendBLockTx(self, chain_b_lock_txid, address_to, kbv, kbs, cb_swap_value, b_fee, restore_height):
 | 
				
			||||||
        raise ValueError('TODO')
 | 
					        raise ValueError('TODO')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def importWatchOnlyAddress(self, address, label):
 | 
				
			||||||
 | 
					        self.rpc_callback('importaddress', [address, label, False])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isWatchOnlyAddress(self, address):
 | 
				
			||||||
 | 
					        addr_info = self.rpc_callback('getaddressinfo', [address])
 | 
				
			||||||
 | 
					        return addr_info['iswatchonly']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getSCLockScriptAddress(self, lock_script):
 | 
				
			||||||
 | 
					        lock_tx_dest = self.getScriptDest(lock_script)
 | 
				
			||||||
 | 
					        return self.encodeScriptDest(lock_tx_dest)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index=False):
 | 
					    def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index=False):
 | 
				
			||||||
        # Add watchonly address and rescan if required
 | 
					        # Add watchonly address and rescan if required
 | 
				
			||||||
        addr_info = self.rpc_callback('getaddressinfo', [dest_address])
 | 
					
 | 
				
			||||||
        if not addr_info['iswatchonly']:
 | 
					        if not self.isWatchOnlyAddress(dest_address):
 | 
				
			||||||
            ro = self.rpc_callback('importaddress', [dest_address, 'bid', False])
 | 
					            self.importWatchOnlyAddress(dest_address, 'bid')
 | 
				
			||||||
            self._log.info('Imported watch-only addr: {}'.format(dest_address))
 | 
					            self._log.info('Imported watch-only addr: {}'.format(dest_address))
 | 
				
			||||||
            self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
 | 
					            self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
 | 
				
			||||||
            self.rpc_callback('rescanblockchain', [rescan_from])
 | 
					            self.rpc_callback('rescanblockchain', [rescan_from])
 | 
				
			||||||
@ -1082,7 +1119,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
        privkey = PrivateKey(k)
 | 
					        privkey = PrivateKey(k)
 | 
				
			||||||
        return privkey.sign_recoverable(message_hash, hasher=None)[:64]
 | 
					        return privkey.sign_recoverable(message_hash, hasher=None)[:64]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyCompact(self, K, message, sig):
 | 
					    def verifyCompactSig(self, K, message, sig):
 | 
				
			||||||
        message_hash = hashlib.sha256(bytes(message, 'utf-8')).digest()
 | 
					        message_hash = hashlib.sha256(bytes(message, 'utf-8')).digest()
 | 
				
			||||||
        pubkey = PublicKey(K)
 | 
					        pubkey = PublicKey(K)
 | 
				
			||||||
        rv = pubkey.verify_compact(sig, message_hash, hasher=None)
 | 
					        rv = pubkey.verify_compact(sig, message_hash, hasher=None)
 | 
				
			||||||
@ -1090,7 +1127,7 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def verifyMessage(self, address, message, signature, message_magic=None) -> bool:
 | 
					    def verifyMessage(self, address, message, signature, message_magic=None) -> bool:
 | 
				
			||||||
        if message_magic is None:
 | 
					        if message_magic is None:
 | 
				
			||||||
            message_magic = self.chainparams_network()['message_magic']
 | 
					            message_magic = self.chainparams()['message_magic']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        message_bytes = SerialiseNumCompact(len(message_magic)) + bytes(message_magic, 'utf-8') + SerialiseNumCompact(len(message)) + bytes(message, 'utf-8')
 | 
					        message_bytes = SerialiseNumCompact(len(message_magic)) + bytes(message_magic, 'utf-8') + SerialiseNumCompact(len(message)) + bytes(message, 'utf-8')
 | 
				
			||||||
        message_hash = hashlib.sha256(hashlib.sha256(message_bytes).digest()).digest()
 | 
					        message_hash = hashlib.sha256(hashlib.sha256(message_bytes).digest()).digest()
 | 
				
			||||||
@ -1185,9 +1222,61 @@ class BTCInterface(CoinInterface):
 | 
				
			|||||||
    def getBlockWithTxns(self, block_hash):
 | 
					    def getBlockWithTxns(self, block_hash):
 | 
				
			||||||
        return self.rpc_callback('getblock', [block_hash, 2])
 | 
					        return self.rpc_callback('getblock', [block_hash, 2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getUnspentsByAddr(self):
 | 
				
			||||||
 | 
					        unspent_addr = dict()
 | 
				
			||||||
 | 
					        unspent = self.rpc_callback('listunspent')
 | 
				
			||||||
 | 
					        for u in unspent:
 | 
				
			||||||
 | 
					            if u['spendable'] is not True:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            unspent_addr[u['address']] = unspent_addr.get(u['address'], 0) + self.make_int(u['amount'], r=1)
 | 
				
			||||||
 | 
					        return unspent_addr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getUTXOBalance(self, address):
 | 
				
			||||||
 | 
					        num_blocks = self.rpc_callback('getblockcount')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sum_unspent = 0
 | 
				
			||||||
 | 
					        self._log.debug('[rm] scantxoutset start')  # scantxoutset is slow
 | 
				
			||||||
 | 
					        ro = self.rpc_callback('scantxoutset', ['start', ['addr({})'.format(address)]])  # TODO: Use combo(address) where possible
 | 
				
			||||||
 | 
					        self._log.debug('[rm] scantxoutset end')
 | 
				
			||||||
 | 
					        for o in ro['unspents']:
 | 
				
			||||||
 | 
					            sum_unspent += self.make_int(o['amount'])
 | 
				
			||||||
 | 
					        return sum_unspent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProofOfFunds(self, amount_for, extra_commit_bytes):
 | 
				
			||||||
 | 
					        # TODO: Lock unspent and use same output/s to fund bid
 | 
				
			||||||
 | 
					        unspent_addr = self.getUnspentsByAddr()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sign_for_addr = None
 | 
				
			||||||
 | 
					        for addr, value in unspent_addr.items():
 | 
				
			||||||
 | 
					            if value >= amount_for:
 | 
				
			||||||
 | 
					                sign_for_addr = addr
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ensure(sign_for_addr is not None, 'Could not find address with enough funds for proof')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._log.debug('sign_for_addr %s', sign_for_addr)
 | 
				
			||||||
 | 
					        if self._use_segwit:  # TODO: Use isSegwitAddress when scantxoutset can use combo
 | 
				
			||||||
 | 
					            # 'Address does not refer to key' for non p2pkh
 | 
				
			||||||
 | 
					            pkh = self.decodeAddress(sign_for_addr)
 | 
				
			||||||
 | 
					            sign_for_addr = self.pkh_to_address(pkh)
 | 
				
			||||||
 | 
					            self._log.debug('sign_for_addr converted %s', sign_for_addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signature = self.rpc_callback('signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + extra_commit_bytes.hex()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (sign_for_addr, signature)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verifyProofOfFunds(self, address, signature, extra_commit_bytes):
 | 
				
			||||||
 | 
					        passed = self.verifyMessage(address, address + '_swap_proof_' + extra_commit_bytes.hex(), signature)
 | 
				
			||||||
 | 
					        ensure(passed is True, 'Proof of funds signature invalid')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self._use_segwit:
 | 
				
			||||||
 | 
					            address = self.encodeSegwitAddress(decodeAddress(address)[1:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.getUTXOBalance(address)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def testBTCInterface():
 | 
					def testBTCInterface():
 | 
				
			||||||
    print('testBTCInterface')
 | 
					    print('TODO: testBTCInterface')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										172
									
								
								basicswap/interface/firo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								basicswap/interface/firo.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,172 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2022 tecnovert
 | 
				
			||||||
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					from .btc import BTCInterface, find_vout_for_address_from_txobj
 | 
				
			||||||
 | 
					from basicswap.chainparams import Coins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from basicswap.util.address import (
 | 
				
			||||||
 | 
					    decodeAddress,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.contrib.test_framework.script import (
 | 
				
			||||||
 | 
					    CScript,
 | 
				
			||||||
 | 
					    OP_0,
 | 
				
			||||||
 | 
					    OP_DUP,
 | 
				
			||||||
 | 
					    OP_EQUAL,
 | 
				
			||||||
 | 
					    OP_HASH160,
 | 
				
			||||||
 | 
					    OP_CHECKSIG,
 | 
				
			||||||
 | 
					    OP_EQUALVERIFY,
 | 
				
			||||||
 | 
					    hash160,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.contrib.test_framework.messages import (
 | 
				
			||||||
 | 
					    CTransaction,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FIROInterface(BTCInterface):
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def coin_type():
 | 
				
			||||||
 | 
					        return Coins.FIRO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def initialiseWallet(self, key):
 | 
				
			||||||
 | 
					        # load with -hdseed= parameter
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getNewAddress(self, use_segwit, label='swap_receive'):
 | 
				
			||||||
 | 
					        return self.rpc_callback('getnewaddress', [label])
 | 
				
			||||||
 | 
					        # addr_plain = self.rpc_callback('getnewaddress', [label])
 | 
				
			||||||
 | 
					        # return self.rpc_callback('addwitnessaddress', [addr_plain])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decodeAddress(self, address):
 | 
				
			||||||
 | 
					        return decodeAddress(address)[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeSegwitAddress(self, script):
 | 
				
			||||||
 | 
					        raise ValueError('TODO')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decodeSegwitAddress(self, addr):
 | 
				
			||||||
 | 
					        raise ValueError('TODO')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isWatchOnlyAddress(self, address):
 | 
				
			||||||
 | 
					        addr_info = self.rpc_callback('validateaddress', [address])
 | 
				
			||||||
 | 
					        return addr_info['iswatchonly']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getSCLockScriptAddress(self, lock_script):
 | 
				
			||||||
 | 
					        lock_tx_dest = self.getScriptDest(lock_script)
 | 
				
			||||||
 | 
					        address = self.encodeScriptDest(lock_tx_dest)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.isWatchOnlyAddress(address):
 | 
				
			||||||
 | 
					            # Expects P2WSH nested in BIP16_P2SH
 | 
				
			||||||
 | 
					            ro = self.rpc_callback('importaddress', [lock_tx_dest.hex(), 'bid lock', False, True])
 | 
				
			||||||
 | 
					            addr_info = self.rpc_callback('validateaddress', [address])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return address
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getLockTxHeightFiro(self, txid, lock_script, bid_amount, rescan_from, find_index=False):
 | 
				
			||||||
 | 
					        # Add watchonly address and rescan if required
 | 
				
			||||||
 | 
					        lock_tx_dest = self.getScriptDest(lock_script)
 | 
				
			||||||
 | 
					        dest_address = self.encodeScriptDest(lock_tx_dest)
 | 
				
			||||||
 | 
					        if not self.isWatchOnlyAddress(dest_address):
 | 
				
			||||||
 | 
					            self.rpc_callback('importaddress', [lock_tx_dest.hex(), 'bid lock', False, True])
 | 
				
			||||||
 | 
					            self._log.info('Imported watch-only addr: {}'.format(dest_address))
 | 
				
			||||||
 | 
					            self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
 | 
				
			||||||
 | 
					            self.rpc_callback('rescanblockchain', [rescan_from])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return_txid = True if txid is None else False
 | 
				
			||||||
 | 
					        if txid is None:
 | 
				
			||||||
 | 
					            txns = self.rpc_callback('listunspent', [0, 9999999, [dest_address, ]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for tx in txns:
 | 
				
			||||||
 | 
					                if self.make_int(tx['amount']) == bid_amount:
 | 
				
			||||||
 | 
					                    txid = bytes.fromhex(tx['txid'])
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if txid is None:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            tx = self.rpc_callback('gettransaction', [txid.hex()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            block_height = 0
 | 
				
			||||||
 | 
					            if 'blockhash' in tx:
 | 
				
			||||||
 | 
					                block_header = self.rpc_callback('getblockheader', [tx['blockhash']])
 | 
				
			||||||
 | 
					                block_height = block_header['height']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            rv = {
 | 
				
			||||||
 | 
					                'depth': 0 if 'confirmations' not in tx else tx['confirmations'],
 | 
				
			||||||
 | 
					                'height': block_height}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self._log.debug('getLockTxHeight gettransaction failed: %s, %s', txid.hex(), str(e))
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if find_index:
 | 
				
			||||||
 | 
					            tx_obj = self.rpc_callback('decoderawtransaction', [tx['hex']])
 | 
				
			||||||
 | 
					            rv['index'] = find_vout_for_address_from_txobj(tx_obj, dest_address)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if return_txid:
 | 
				
			||||||
 | 
					            rv['txid'] = txid.hex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def createSCLockTx(self, value, Kal, Kaf, vkbv=None):
 | 
				
			||||||
 | 
					        script = self.genScriptLockTxScript(Kal, Kaf)
 | 
				
			||||||
 | 
					        tx = CTransaction()
 | 
				
			||||||
 | 
					        tx.nVersion = self.txVersion()
 | 
				
			||||||
 | 
					        tx.vout.append(self.txoType()(value, self.getScriptDest(script)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return tx.serialize(), script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def fundSCLockTx(self, tx_bytes, feerate, vkbv=None):
 | 
				
			||||||
 | 
					        return self.fundTx(tx_bytes, feerate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def signTxWithWallet(self, tx):
 | 
				
			||||||
 | 
					        rv = self.rpc_callback('signrawtransaction', [tx.hex()])
 | 
				
			||||||
 | 
					        return bytes.fromhex(rv['hex'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def createRawSignedTransaction(self, addr_to, amount):
 | 
				
			||||||
 | 
					        txn = self.rpc_callback('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fee_rate, fee_src = self.get_fee_rate(self._conf_target)
 | 
				
			||||||
 | 
					        self._log.debug(f'Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        options = {
 | 
				
			||||||
 | 
					            'lockUnspents': True,
 | 
				
			||||||
 | 
					            'feeRate': fee_rate,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        txn_funded = self.rpc_callback('fundrawtransaction', [txn, options])['hex']
 | 
				
			||||||
 | 
					        txn_signed = self.rpc_callback('signrawtransaction', [txn_funded])['hex']
 | 
				
			||||||
 | 
					        return txn_signed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getScriptForPubkeyHash(self, pkh):
 | 
				
			||||||
 | 
					        # Return P2WPKH nested in BIP16 P2SH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getScriptDest(self, script):
 | 
				
			||||||
 | 
					        # P2WSH nested in BIP16_P2SH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        script_hash = hashlib.sha256(script).digest()
 | 
				
			||||||
 | 
					        assert len(script_hash) == 32
 | 
				
			||||||
 | 
					        script_hash_hash = hash160(script_hash)
 | 
				
			||||||
 | 
					        assert len(script_hash_hash) == 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CScript([OP_HASH160, script_hash_hash, OP_EQUAL])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeScriptDest(self, script):
 | 
				
			||||||
 | 
					        # Extract hash from script
 | 
				
			||||||
 | 
					        script_hash = script[2:-1]
 | 
				
			||||||
 | 
					        return self.sh_to_address(script_hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getScriptScriptSig(self, script):
 | 
				
			||||||
 | 
					        return CScript([OP_0, hashlib.sha256(script).digest()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def withdrawCoin(self, value, addr_to, subfee):
 | 
				
			||||||
 | 
					        params = [addr_to, value, '', '', subfee]
 | 
				
			||||||
 | 
					        return self.rpc_callback('sendtoaddress', params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getWalletSeedID(self):
 | 
				
			||||||
 | 
					        return self.rpc_callback('getwalletinfo')['hdmasterkeyid']
 | 
				
			||||||
@ -166,7 +166,7 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
            ensure(v['result'] is True, 'verifycommitment failed')
 | 
					            ensure(v['result'] is True, 'verifycommitment failed')
 | 
				
			||||||
        return output_n, blinded_info
 | 
					        return output_n, blinded_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockTx(self, value, Kal, Kaf, vkbv):
 | 
					    def createSCLockTx(self, value, Kal, Kaf, vkbv):
 | 
				
			||||||
        script = self.genScriptLockTxScript(Kal, Kaf)
 | 
					        script = self.genScriptLockTxScript(Kal, Kaf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Nonce is derived from vkbv, ephemeral_key isn't used
 | 
					        # Nonce is derived from vkbv, ephemeral_key isn't used
 | 
				
			||||||
@ -183,7 +183,7 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
        tx_bytes = bytes.fromhex(rv['hex'])
 | 
					        tx_bytes = bytes.fromhex(rv['hex'])
 | 
				
			||||||
        return tx_bytes, script
 | 
					        return tx_bytes, script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fundScriptLockTx(self, tx_bytes, feerate, vkbv):
 | 
					    def fundSCLockTx(self, tx_bytes, feerate, vkbv):
 | 
				
			||||||
        feerate_str = self.format_amount(feerate)
 | 
					        feerate_str = self.format_amount(feerate)
 | 
				
			||||||
        # TODO: unlock unspents if bid cancelled
 | 
					        # TODO: unlock unspents if bid cancelled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -205,7 +205,7 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
        rv = self.rpc_callback('fundrawtransactionfrom', ['blind', tx_hex, {}, outputs_info, options])
 | 
					        rv = self.rpc_callback('fundrawtransactionfrom', ['blind', tx_hex, {}, outputs_info, options])
 | 
				
			||||||
        return bytes.fromhex(rv['hex'])
 | 
					        return bytes.fromhex(rv['hex'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv):
 | 
					    def createSCLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv):
 | 
				
			||||||
        lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
 | 
					        lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
 | 
				
			||||||
        assert (self.getTxid(tx_lock_bytes).hex() == lock_tx_obj['txid'])
 | 
					        assert (self.getTxid(tx_lock_bytes).hex() == lock_tx_obj['txid'])
 | 
				
			||||||
        # Nonce is derived from vkbv, ephemeral_key isn't used
 | 
					        # Nonce is derived from vkbv, ephemeral_key isn't used
 | 
				
			||||||
@ -252,7 +252,7 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return bytes.fromhex(lock_refund_tx_hex), refund_script, refunded_value
 | 
					        return bytes.fromhex(lock_refund_tx_hex), refund_script, refunded_value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockRefundSpendTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_refund_to, tx_fee_rate, vkbv):
 | 
					    def createSCLockRefundSpendTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_refund_to, tx_fee_rate, vkbv):
 | 
				
			||||||
        # Returns the coinA locked coin to the leader
 | 
					        # Returns the coinA locked coin to the leader
 | 
				
			||||||
        # The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
 | 
					        # The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
 | 
				
			||||||
        # If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
 | 
					        # If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
 | 
				
			||||||
@ -297,11 +297,11 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return bytes.fromhex(lock_refund_spend_tx_hex)
 | 
					        return bytes.fromhex(lock_refund_spend_tx_hex)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockTx(self, tx_bytes, script_out,
 | 
					    def verifySCLockTx(self, tx_bytes, script_out,
 | 
				
			||||||
                     swap_value,
 | 
					                       swap_value,
 | 
				
			||||||
                     Kal, Kaf,
 | 
					                       Kal, Kaf,
 | 
				
			||||||
                     feerate,
 | 
					                       feerate,
 | 
				
			||||||
                     check_lock_tx_inputs, vkbv):
 | 
					                       check_lock_tx_inputs, vkbv):
 | 
				
			||||||
        lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
					        lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
				
			||||||
        lock_txid_hex = lock_tx_obj['txid']
 | 
					        lock_txid_hex = lock_tx_obj['txid']
 | 
				
			||||||
        self._log.info('Verifying lock tx: {}.'.format(lock_txid_hex))
 | 
					        self._log.info('Verifying lock tx: {}.'.format(lock_txid_hex))
 | 
				
			||||||
@ -341,9 +341,9 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return bytes.fromhex(lock_txid_hex), lock_output_n
 | 
					        return bytes.fromhex(lock_txid_hex), lock_output_n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockRefundTx(self, tx_bytes, lock_tx_bytes, script_out,
 | 
					    def verifySCLockRefundTx(self, tx_bytes, lock_tx_bytes, script_out,
 | 
				
			||||||
                           prevout_id, prevout_n, prevout_seq, prevout_script,
 | 
					                             prevout_id, prevout_n, prevout_seq, prevout_script,
 | 
				
			||||||
                           Kal, Kaf, csv_val_expect, swap_value, feerate, vkbv):
 | 
					                             Kal, Kaf, csv_val_expect, swap_value, feerate, vkbv):
 | 
				
			||||||
        lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
					        lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
				
			||||||
        lock_refund_txid_hex = lock_refund_tx_obj['txid']
 | 
					        lock_refund_txid_hex = lock_refund_tx_obj['txid']
 | 
				
			||||||
        self._log.info('Verifying lock refund tx: {}.'.format(lock_refund_txid_hex))
 | 
					        self._log.info('Verifying lock refund tx: {}.'.format(lock_refund_txid_hex))
 | 
				
			||||||
@ -399,10 +399,10 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return bytes.fromhex(lock_refund_txid_hex), lock_refund_txo_value, lock_refund_output_n
 | 
					        return bytes.fromhex(lock_refund_txid_hex), lock_refund_txo_value, lock_refund_output_n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockRefundSpendTx(self, tx_bytes, lock_refund_tx_bytes,
 | 
					    def verifySCLockRefundSpendTx(self, tx_bytes, lock_refund_tx_bytes,
 | 
				
			||||||
                                lock_refund_tx_id, prevout_script,
 | 
					                                  lock_refund_tx_id, prevout_script,
 | 
				
			||||||
                                Kal,
 | 
					                                  Kal,
 | 
				
			||||||
                                prevout_n, prevout_value, feerate, vkbv):
 | 
					                                  prevout_n, prevout_value, feerate, vkbv):
 | 
				
			||||||
        lock_refund_spend_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
					        lock_refund_spend_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
				
			||||||
        lock_refund_spend_txid_hex = lock_refund_spend_tx_obj['txid']
 | 
					        lock_refund_spend_txid_hex = lock_refund_spend_tx_obj['txid']
 | 
				
			||||||
        self._log.info('Verifying lock refund spend tx: {}.'.format(lock_refund_spend_txid_hex))
 | 
					        self._log.info('Verifying lock refund spend tx: {}.'.format(lock_refund_spend_txid_hex))
 | 
				
			||||||
@ -460,7 +460,7 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
        ensure(output_n is not None, 'Output not found in tx')
 | 
					        ensure(output_n is not None, 'Output not found in tx')
 | 
				
			||||||
        return output_n
 | 
					        return output_n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockSpendTx(self, tx_lock_bytes, script_lock, pk_dest, tx_fee_rate, vkbv):
 | 
					    def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pk_dest, tx_fee_rate, vkbv):
 | 
				
			||||||
        lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
 | 
					        lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
 | 
				
			||||||
        lock_txid_hex = lock_tx_obj['txid']
 | 
					        lock_txid_hex = lock_tx_obj['txid']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -501,14 +501,14 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
        vsize = lock_spend_tx_obj['vsize']
 | 
					        vsize = lock_spend_tx_obj['vsize']
 | 
				
			||||||
        pay_fee = make_int(lock_spend_tx_obj['vout'][0]['ct_fee'])
 | 
					        pay_fee = make_int(lock_spend_tx_obj['vout'][0]['ct_fee'])
 | 
				
			||||||
        actual_tx_fee_rate = pay_fee * 1000 // vsize
 | 
					        actual_tx_fee_rate = pay_fee * 1000 // vsize
 | 
				
			||||||
        self._log.info('createScriptLockSpendTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
					        self._log.info('createSCLockSpendTx %s:\n    fee_rate, vsize, fee: %ld, %ld, %ld.',
 | 
				
			||||||
                       lock_spend_tx_obj['txid'], actual_tx_fee_rate, vsize, pay_fee)
 | 
					                       lock_spend_tx_obj['txid'], actual_tx_fee_rate, vsize, pay_fee)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return bytes.fromhex(lock_spend_tx_hex)
 | 
					        return bytes.fromhex(lock_spend_tx_hex)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def verifyLockSpendTx(self, tx_bytes,
 | 
					    def verifySCLockSpendTx(self, tx_bytes,
 | 
				
			||||||
                          lock_tx_bytes, lock_tx_script,
 | 
					                            lock_tx_bytes, lock_tx_script,
 | 
				
			||||||
                          a_pk_f, feerate, vkbv):
 | 
					                            a_pk_f, feerate, vkbv):
 | 
				
			||||||
        lock_spend_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
					        lock_spend_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
 | 
				
			||||||
        lock_spend_txid_hex = lock_spend_tx_obj['txid']
 | 
					        lock_spend_txid_hex = lock_spend_tx_obj['txid']
 | 
				
			||||||
        self._log.info('Verifying lock spend tx: {}.'.format(lock_spend_txid_hex))
 | 
					        self._log.info('Verifying lock spend tx: {}.'.format(lock_spend_txid_hex))
 | 
				
			||||||
@ -578,7 +578,7 @@ class PARTInterfaceBlind(PARTInterface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createScriptLockRefundSpendToFTx(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):
 | 
				
			||||||
        # 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_callback('decoderawtransaction', [tx_lock_refund_bytes.hex()])
 | 
					        lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_refund_bytes.hex()])
 | 
				
			||||||
 | 
				
			|||||||
@ -296,7 +296,7 @@ def page_wallet(self, url_split, post_string):
 | 
				
			|||||||
        if show_utxo_groups:
 | 
					        if show_utxo_groups:
 | 
				
			||||||
            utxo_groups = ''
 | 
					            utxo_groups = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            unspent_by_addr = swap_client.getUnspentsByAddr(k)
 | 
					            unspent_by_addr = ci.getUnspentsByAddr()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            sorted_unspent_by_addr = sorted(unspent_by_addr.items(), key=lambda x: x[1], reverse=True)
 | 
					            sorted_unspent_by_addr = sorted(unspent_by_addr.items(), key=lambda x: x[1], reverse=True)
 | 
				
			||||||
            for kv in sorted_unspent_by_addr:
 | 
					            for kv in sorted_unspent_by_addr:
 | 
				
			||||||
 | 
				
			|||||||
@ -53,6 +53,9 @@ PIVX_VERSION_TAG = os.getenv('PIVX_VERSION_TAG', '_scantxoutset')
 | 
				
			|||||||
DASH_VERSION = os.getenv('DASH_VERSION', '18.1.0')
 | 
					DASH_VERSION = os.getenv('DASH_VERSION', '18.1.0')
 | 
				
			||||||
DASH_VERSION_TAG = os.getenv('DASH_VERSION_TAG', '')
 | 
					DASH_VERSION_TAG = os.getenv('DASH_VERSION_TAG', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FIRO_VERSION = os.getenv('FIRO_VERSION', '0.14.11.1')
 | 
				
			||||||
 | 
					FIRO_VERSION_TAG = os.getenv('FIRO_VERSION_TAG', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
known_coins = {
 | 
					known_coins = {
 | 
				
			||||||
    'particl': (PARTICL_VERSION, PARTICL_VERSION_TAG, ('tecnovert',)),
 | 
					    'particl': (PARTICL_VERSION, PARTICL_VERSION_TAG, ('tecnovert',)),
 | 
				
			||||||
@ -62,6 +65,7 @@ known_coins = {
 | 
				
			|||||||
    'monero': (MONERO_VERSION, MONERO_VERSION_TAG, ('binaryfate',)),
 | 
					    'monero': (MONERO_VERSION, MONERO_VERSION_TAG, ('binaryfate',)),
 | 
				
			||||||
    'pivx': (PIVX_VERSION, PIVX_VERSION_TAG, ('tecnovert',)),
 | 
					    'pivx': (PIVX_VERSION, PIVX_VERSION_TAG, ('tecnovert',)),
 | 
				
			||||||
    'dash': (DASH_VERSION, DASH_VERSION_TAG, ('pasta',)),
 | 
					    'dash': (DASH_VERSION, DASH_VERSION_TAG, ('pasta',)),
 | 
				
			||||||
 | 
					    'firo': (FIRO_VERSION, FIRO_VERSION_TAG, ('reuben',)),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
expected_key_ids = {
 | 
					expected_key_ids = {
 | 
				
			||||||
@ -73,6 +77,7 @@ expected_key_ids = {
 | 
				
			|||||||
    'davidburkett38': ('3620E9D387E55666',),
 | 
					    'davidburkett38': ('3620E9D387E55666',),
 | 
				
			||||||
    'fuzzbawls': ('3BDCDA2D87A881D9',),
 | 
					    'fuzzbawls': ('3BDCDA2D87A881D9',),
 | 
				
			||||||
    'pasta': ('52527BEDABE87984',),
 | 
					    'pasta': ('52527BEDABE87984',),
 | 
				
			||||||
 | 
					    'reuben': ('1290A1D0FA7EE109',),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if platform.system() == 'Darwin':
 | 
					if platform.system() == 'Darwin':
 | 
				
			||||||
@ -137,6 +142,12 @@ DASH_ONION_PORT = int(os.getenv('DASH_ONION_PORT', 9999))  # nDefaultPort
 | 
				
			|||||||
DASH_RPC_USER = os.getenv('DASH_RPC_USER', '')
 | 
					DASH_RPC_USER = os.getenv('DASH_RPC_USER', '')
 | 
				
			||||||
DASH_RPC_PWD = os.getenv('DASH_RPC_PWD', '')
 | 
					DASH_RPC_PWD = os.getenv('DASH_RPC_PWD', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FIRO_RPC_HOST = os.getenv('FIRO_RPC_HOST', '127.0.0.1')
 | 
				
			||||||
 | 
					FIRO_RPC_PORT = int(os.getenv('FIRO_RPC_PORT', 8888))
 | 
				
			||||||
 | 
					FIRO_ONION_PORT = int(os.getenv('FIRO_ONION_PORT', 8168))  # nDefaultPort
 | 
				
			||||||
 | 
					FIRO_RPC_USER = os.getenv('FIRO_RPC_USER', '')
 | 
				
			||||||
 | 
					FIRO_RPC_PWD = os.getenv('FIRO_RPC_PWD', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TOR_PROXY_HOST = os.getenv('TOR_PROXY_HOST', '127.0.0.1')
 | 
					TOR_PROXY_HOST = os.getenv('TOR_PROXY_HOST', '127.0.0.1')
 | 
				
			||||||
TOR_PROXY_PORT = int(os.getenv('TOR_PROXY_PORT', 9050))
 | 
					TOR_PROXY_PORT = int(os.getenv('TOR_PROXY_PORT', 9050))
 | 
				
			||||||
TOR_CONTROL_PORT = int(os.getenv('TOR_CONTROL_PORT', 9051))
 | 
					TOR_CONTROL_PORT = int(os.getenv('TOR_CONTROL_PORT', 9051))
 | 
				
			||||||
@ -211,6 +222,19 @@ def downloadBytes(url):
 | 
				
			|||||||
        popConnectionParameters()
 | 
					        popConnectionParameters()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def importPubkeyFromUrls(gpg, pubkeyurls):
 | 
				
			||||||
 | 
					    for url in pubkeyurls:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            logger.info('Importing public key from url: ' + url)
 | 
				
			||||||
 | 
					            rv = gpg.import_keys(downloadBytes(url))
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            logging.warning('Import from url failed: %s', str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for key in rv.fingerprints:
 | 
				
			||||||
 | 
					        gpg.trust_keys(key, 'TRUST_FULLY')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def testTorConnection():
 | 
					def testTorConnection():
 | 
				
			||||||
    test_url = 'https://check.torproject.org/'
 | 
					    test_url = 'https://check.torproject.org/'
 | 
				
			||||||
    logger.info('Testing TOR connection at: ' + test_url)
 | 
					    logger.info('Testing TOR connection at: ' + test_url)
 | 
				
			||||||
@ -278,8 +302,14 @@ def extractCore(coin, version_data, settings, bin_dir, release_path, extra_opts=
 | 
				
			|||||||
    logger.info('extractCore %s v%s%s', coin, version, version_tag)
 | 
					    logger.info('extractCore %s v%s%s', coin, version, version_tag)
 | 
				
			||||||
    extract_core_overwrite = extra_opts.get('extract_core_overwrite', True)
 | 
					    extract_core_overwrite = extra_opts.get('extract_core_overwrite', True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if coin == 'monero':
 | 
					    if coin in ('monero', 'firo'):
 | 
				
			||||||
        bins = ['monerod', 'monero-wallet-rpc']
 | 
					        if coin == 'monero':
 | 
				
			||||||
 | 
					            bins = ['monerod', 'monero-wallet-rpc']
 | 
				
			||||||
 | 
					        elif coin == 'firo':
 | 
				
			||||||
 | 
					            bins = [coin + 'd', coin + '-cli', coin + '-tx']
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise ValueError('Unknown coin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        num_exist = 0
 | 
					        num_exist = 0
 | 
				
			||||||
        for b in bins:
 | 
					        for b in bins:
 | 
				
			||||||
            out_path = os.path.join(bin_dir, b)
 | 
					            out_path = os.path.join(bin_dir, b)
 | 
				
			||||||
@ -412,12 +442,23 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
 | 
				
			|||||||
            release_url = 'https://github.com/dashpay/dash/releases/download/v{}/{}'.format(version + version_tag, release_filename)
 | 
					            release_url = 'https://github.com/dashpay/dash/releases/download/v{}/{}'.format(version + version_tag, release_filename)
 | 
				
			||||||
            assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, major_version)
 | 
					            assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, major_version)
 | 
				
			||||||
            assert_url = 'https://raw.githubusercontent.com/dashpay/gitian.sigs/master/%s-%s/%s/%s' % (version + version_tag, os_dir_name, signing_key_name, assert_filename)
 | 
					            assert_url = 'https://raw.githubusercontent.com/dashpay/gitian.sigs/master/%s-%s/%s/%s' % (version + version_tag, os_dir_name, signing_key_name, assert_filename)
 | 
				
			||||||
 | 
					        elif coin == 'firo':
 | 
				
			||||||
 | 
					            raise ValueError('TODO: scantxoutset release')
 | 
				
			||||||
 | 
					            if BIN_ARCH == 'x86_64-linux-gnu':
 | 
				
			||||||
 | 
					                arch_name = 'linux64'
 | 
				
			||||||
 | 
					                file_ext = 'tar.gz'
 | 
				
			||||||
 | 
					            elif BIN_ARCH == 'osx64':
 | 
				
			||||||
 | 
					                arch_name = 'macos'
 | 
				
			||||||
 | 
					                file_ext = 'dmg'
 | 
				
			||||||
 | 
					                raise ValueError('TODO: Firo - Extract .dmg')
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise ValueError('Firo: Unknown architecture')
 | 
				
			||||||
 | 
					            release_filename = '{}-{}-{}{}.{}'.format('firo', version + version_tag, arch_name, filename_extra, file_ext)
 | 
				
			||||||
 | 
					            release_url = 'https://github.com/firoorg/firo/releases/download/v{}/{}'.format(version + version_tag, release_filename)
 | 
				
			||||||
 | 
					            assert_url = 'https://github.com/firoorg/firo/releases/download/v%s/SHA256SUMS' % (version + version_tag)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            raise ValueError('Unknown coin')
 | 
					            raise ValueError('Unknown coin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_sig_filename = assert_filename + '.sig'
 | 
					 | 
				
			||||||
        assert_sig_url = assert_url + ('.asc' if major_version >= 22 else '.sig')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        release_path = os.path.join(bin_dir, release_filename)
 | 
					        release_path = os.path.join(bin_dir, release_filename)
 | 
				
			||||||
        if not os.path.exists(release_path):
 | 
					        if not os.path.exists(release_path):
 | 
				
			||||||
            downloadFile(release_url, release_path)
 | 
					            downloadFile(release_url, release_path)
 | 
				
			||||||
@ -428,10 +469,12 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
 | 
				
			|||||||
        if not os.path.exists(assert_path):
 | 
					        if not os.path.exists(assert_path):
 | 
				
			||||||
            downloadFile(assert_url, assert_path)
 | 
					            downloadFile(assert_url, assert_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_sig_filename = '{}-{}-{}-build-{}.assert.sig'.format(coin, os_name, version, signing_key_name)
 | 
					        if coin not in ('firo', ):
 | 
				
			||||||
        assert_sig_path = os.path.join(bin_dir, assert_sig_filename)
 | 
					            assert_sig_url = assert_url + ('.asc' if major_version >= 22 else '.sig')
 | 
				
			||||||
        if not os.path.exists(assert_sig_path):
 | 
					            assert_sig_filename = '{}-{}-{}-build-{}.assert.sig'.format(coin, os_name, version, signing_key_name)
 | 
				
			||||||
            downloadFile(assert_sig_url, assert_sig_path)
 | 
					            assert_sig_path = os.path.join(bin_dir, assert_sig_filename)
 | 
				
			||||||
 | 
					            if not os.path.exists(assert_sig_path):
 | 
				
			||||||
 | 
					                downloadFile(assert_sig_url, assert_sig_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hasher = hashlib.sha256()
 | 
					    hasher = hashlib.sha256()
 | 
				
			||||||
    with open(release_path, 'rb') as fp:
 | 
					    with open(release_path, 'rb') as fp:
 | 
				
			||||||
@ -462,17 +505,28 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
 | 
				
			|||||||
                    for key in rv.fingerprints:
 | 
					                    for key in rv.fingerprints:
 | 
				
			||||||
                        gpg.trust_keys(rv.fingerprints[0], 'TRUST_FULLY')
 | 
					                        gpg.trust_keys(rv.fingerprints[0], 'TRUST_FULLY')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if coin == 'pivx':
 | 
				
			||||||
 | 
					        pubkey_filename = '{}_{}.pgp'.format('particl', signing_key_name)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        pubkey_filename = '{}_{}.pgp'.format(coin, signing_key_name)
 | 
				
			||||||
 | 
					    pubkeyurls = [
 | 
				
			||||||
 | 
					        'https://raw.githubusercontent.com/tecnovert/basicswap/master/pgp/keys/' + pubkey_filename,
 | 
				
			||||||
 | 
					        'https://gitlab.com/particl/basicswap/-/raw/master/pgp/keys/' + pubkey_filename,
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    if coin == 'dash':
 | 
				
			||||||
 | 
					        pubkeyurls.append('https://raw.githubusercontent.com/dashpay/dash/master/contrib/gitian-keys/pasta.pgp')
 | 
				
			||||||
    if coin == 'monero':
 | 
					    if coin == 'monero':
 | 
				
			||||||
 | 
					        pubkeyurls.append('https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc')
 | 
				
			||||||
 | 
					    if coin == 'firo':
 | 
				
			||||||
 | 
					        pubkeyurls.append('https://firo.org/reuben.asc')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if coin in ('monero', 'firo'):
 | 
				
			||||||
        with open(assert_path, 'rb') as fp:
 | 
					        with open(assert_path, 'rb') as fp:
 | 
				
			||||||
            verified = gpg.verify_file(fp)
 | 
					            verified = gpg.verify_file(fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not isValidSignature(verified) and verified.username is None:
 | 
					        if not isValidSignature(verified) and verified.username is None:
 | 
				
			||||||
            logger.warning('Signature made by unknown key.')
 | 
					            logger.warning('Signature made by unknown key.')
 | 
				
			||||||
 | 
					            importPubkeyFromUrls(gpg, pubkeyurls)
 | 
				
			||||||
            pubkeyurl = 'https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc'
 | 
					 | 
				
			||||||
            logger.info('Importing public key from url: ' + pubkeyurl)
 | 
					 | 
				
			||||||
            rv = gpg.import_keys(downloadBytes(pubkeyurl))
 | 
					 | 
				
			||||||
            gpg.trust_keys(rv.fingerprints[0], 'TRUST_FULLY')
 | 
					 | 
				
			||||||
            with open(assert_path, 'rb') as fp:
 | 
					            with open(assert_path, 'rb') as fp:
 | 
				
			||||||
                verified = gpg.verify_file(fp)
 | 
					                verified = gpg.verify_file(fp)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
@ -481,28 +535,7 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if not isValidSignature(verified) and verified.username is None:
 | 
					        if not isValidSignature(verified) and verified.username is None:
 | 
				
			||||||
            logger.warning('Signature made by unknown key.')
 | 
					            logger.warning('Signature made by unknown key.')
 | 
				
			||||||
 | 
					            importPubkeyFromUrls(gpg, pubkeyurls)
 | 
				
			||||||
            if coin == 'pivx':
 | 
					 | 
				
			||||||
                filename = '{}_{}.pgp'.format('particl', signing_key_name)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                filename = '{}_{}.pgp'.format(coin, signing_key_name)
 | 
					 | 
				
			||||||
            pubkeyurls = [
 | 
					 | 
				
			||||||
                'https://raw.githubusercontent.com/tecnovert/basicswap/master/pgp/keys/' + filename,
 | 
					 | 
				
			||||||
                'https://gitlab.com/particl/basicswap/-/raw/master/pgp/keys/' + filename,
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            if coin == 'dash':
 | 
					 | 
				
			||||||
                pubkeyurls.append('https://raw.githubusercontent.com/dashpay/dash/master/contrib/gitian-keys/pasta.pgp')
 | 
					 | 
				
			||||||
            for url in pubkeyurls:
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    logger.info('Importing public key from url: ' + url)
 | 
					 | 
				
			||||||
                    rv = gpg.import_keys(downloadBytes(url))
 | 
					 | 
				
			||||||
                    break
 | 
					 | 
				
			||||||
                except Exception as e:
 | 
					 | 
				
			||||||
                    logging.warning('Import from url failed: %s', str(e))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for key in rv.fingerprints:
 | 
					 | 
				
			||||||
                gpg.trust_keys(key, 'TRUST_FULLY')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            with open(assert_sig_path, 'rb') as fp:
 | 
					            with open(assert_sig_path, 'rb') as fp:
 | 
				
			||||||
                verified = gpg.verify_file(fp, assert_path)
 | 
					                verified = gpg.verify_file(fp, assert_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -600,12 +633,13 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
 | 
				
			|||||||
    with open(core_conf_path, 'w') as fp:
 | 
					    with open(core_conf_path, 'w') as fp:
 | 
				
			||||||
        if chain != 'mainnet':
 | 
					        if chain != 'mainnet':
 | 
				
			||||||
            fp.write(chain + '=1\n')
 | 
					            fp.write(chain + '=1\n')
 | 
				
			||||||
            if chain == 'testnet':
 | 
					            if coin != 'firo':
 | 
				
			||||||
                fp.write('[test]\n\n')
 | 
					                if chain == 'testnet':
 | 
				
			||||||
            if chain == 'regtest':
 | 
					                    fp.write('[test]\n\n')
 | 
				
			||||||
                fp.write('[regtest]\n\n')
 | 
					                elif chain == 'regtest':
 | 
				
			||||||
            else:
 | 
					                    fp.write('[regtest]\n\n')
 | 
				
			||||||
                logger.warning('Unknown chain %s', chain)
 | 
					                else:
 | 
				
			||||||
 | 
					                    logger.warning('Unknown chain %s', chain)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if COINS_RPCBIND_IP != '127.0.0.1':
 | 
					        if COINS_RPCBIND_IP != '127.0.0.1':
 | 
				
			||||||
            fp.write('rpcallowip=127.0.0.1\n')
 | 
					            fp.write('rpcallowip=127.0.0.1\n')
 | 
				
			||||||
@ -656,6 +690,13 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
 | 
				
			|||||||
            fp.write('fallbackfee=0.0002\n')
 | 
					            fp.write('fallbackfee=0.0002\n')
 | 
				
			||||||
            if DASH_RPC_USER != '':
 | 
					            if DASH_RPC_USER != '':
 | 
				
			||||||
                fp.write('rpcauth={}:{}${}\n'.format(DASH_RPC_USER, salt, password_to_hmac(salt, DASH_RPC_PWD)))
 | 
					                fp.write('rpcauth={}:{}${}\n'.format(DASH_RPC_USER, salt, password_to_hmac(salt, DASH_RPC_PWD)))
 | 
				
			||||||
 | 
					        elif coin == 'firo':
 | 
				
			||||||
 | 
					            fp.write('prune=4000\n')
 | 
				
			||||||
 | 
					            fp.write('fallbackfee=0.0002\n')
 | 
				
			||||||
 | 
					            fp.write('txindex=0\n')
 | 
				
			||||||
 | 
					            fp.write('usehd=1\n')
 | 
				
			||||||
 | 
					            if FIRO_RPC_USER != '':
 | 
				
			||||||
 | 
					                fp.write('rpcauth={}:{}${}\n'.format(FIRO_RPC_USER, salt, password_to_hmac(salt, FIRO_RPC_PWD)))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            logger.warning('Unknown coin %s', coin)
 | 
					            logger.warning('Unknown coin %s', coin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -886,6 +927,9 @@ def initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings,
 | 
				
			|||||||
                        filename = coin_name + 'd' + ('.exe' if os.name == 'nt' else '')
 | 
					                        filename = coin_name + 'd' + ('.exe' if os.name == 'nt' else '')
 | 
				
			||||||
                        coin_args = ['-nofindpeers', '-nostaking'] if c == Coins.PART else []
 | 
					                        coin_args = ['-nofindpeers', '-nostaking'] if c == Coins.PART else []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if c == Coins.FIRO:
 | 
				
			||||||
 | 
					                            coin_args += ['-hdseed={}'.format(swap_client.getWalletKey(Coins.FIRO, 1).hex())]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, daemon_args + coin_args))
 | 
					                        daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, daemon_args + coin_args))
 | 
				
			||||||
                        swap_client.setDaemonPID(c, daemons[-1].pid)
 | 
					                        swap_client.setDaemonPID(c, daemons[-1].pid)
 | 
				
			||||||
                swap_client.setCoinRunParams(c)
 | 
					                swap_client.setCoinRunParams(c)
 | 
				
			||||||
@ -1173,7 +1217,7 @@ def main():
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        'dash': {
 | 
					        'dash': {
 | 
				
			||||||
            'connection_type': 'rpc' if 'dash' in with_coins else 'none',
 | 
					            'connection_type': 'rpc' if 'dash' in with_coins else 'none',
 | 
				
			||||||
            'manage_daemon': True if ('dash' in with_coins and PIVX_RPC_HOST == '127.0.0.1') else False,
 | 
					            'manage_daemon': True if ('dash' in with_coins and DASH_RPC_HOST == '127.0.0.1') else False,
 | 
				
			||||||
            'rpchost': DASH_RPC_HOST,
 | 
					            'rpchost': DASH_RPC_HOST,
 | 
				
			||||||
            'rpcport': DASH_RPC_PORT + port_offset,
 | 
					            'rpcport': DASH_RPC_PORT + port_offset,
 | 
				
			||||||
            'onionport': DASH_ONION_PORT + port_offset,
 | 
					            'onionport': DASH_ONION_PORT + port_offset,
 | 
				
			||||||
@ -1185,6 +1229,21 @@ def main():
 | 
				
			|||||||
            'conf_target': 2,
 | 
					            'conf_target': 2,
 | 
				
			||||||
            'core_version_group': 18,
 | 
					            'core_version_group': 18,
 | 
				
			||||||
            'chain_lookups': 'local',
 | 
					            'chain_lookups': 'local',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'firo': {
 | 
				
			||||||
 | 
					            'connection_type': 'rpc' if 'firo' in with_coins else 'none',
 | 
				
			||||||
 | 
					            'manage_daemon': True if ('firo' in with_coins and FIRO_RPC_HOST == '127.0.0.1') else False,
 | 
				
			||||||
 | 
					            'rpchost': FIRO_RPC_HOST,
 | 
				
			||||||
 | 
					            'rpcport': FIRO_RPC_PORT + port_offset,
 | 
				
			||||||
 | 
					            'onionport': FIRO_ONION_PORT + port_offset,
 | 
				
			||||||
 | 
					            'datadir': os.getenv('FIRO_DATA_DIR', os.path.join(data_dir, 'firo')),
 | 
				
			||||||
 | 
					            'bindir': os.path.join(bin_dir, 'firo'),
 | 
				
			||||||
 | 
					            'use_segwit': False,
 | 
				
			||||||
 | 
					            'use_csv': True,
 | 
				
			||||||
 | 
					            'blocks_confirmed': 1,
 | 
				
			||||||
 | 
					            'conf_target': 2,
 | 
				
			||||||
 | 
					            'core_version_group': 18,
 | 
				
			||||||
 | 
					            'chain_lookups': 'local',
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1203,6 +1262,9 @@ def main():
 | 
				
			|||||||
    if DASH_RPC_USER != '':
 | 
					    if DASH_RPC_USER != '':
 | 
				
			||||||
        chainclients['dash']['rpcuser'] = DASH_RPC_USER
 | 
					        chainclients['dash']['rpcuser'] = DASH_RPC_USER
 | 
				
			||||||
        chainclients['dash']['rpcpassword'] = DASH_RPC_PWD
 | 
					        chainclients['dash']['rpcpassword'] = DASH_RPC_PWD
 | 
				
			||||||
 | 
					    if FIRO_RPC_USER != '':
 | 
				
			||||||
 | 
					        chainclients['firo']['rpcuser'] = FIRO_RPC_USER
 | 
				
			||||||
 | 
					        chainclients['firo']['rpcpassword'] = FIRO_RPC_PWD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
 | 
					    chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								pgp/keys/firo_reuben.pgp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pgp/keys/firo_reuben.pgp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					-----BEGIN PGP PUBLIC KEY BLOCK-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mDMEX7UxaxYJKwYBBAHaRw8BAQdAjb4i983N4ysLmcn6RyeTwctpB2EppSc7qJ6l
 | 
				
			||||||
 | 
					yb0pezm0IXJldWJlbkBmaXJvLm9yZyA8cmV1YmVuQGZpcm8ub3JnPoiPBBAWCgAg
 | 
				
			||||||
 | 
					BQJftTFrBgsJBwgDAgQVCAoCBBYCAQACGQECGwMCHgEAIQkQEpCh0Pp+4QkWIQQB
 | 
				
			||||||
 | 
					hkVNY+g9he+R3k4SkKHQ+n7hCaMKAP9pYkzGWBNRZyvLnUVob9mV+1rOQfNM0T8p
 | 
				
			||||||
 | 
					Pmj9rIl+fgEAw8ae8Suhotv9DawP90ehFNUNUwxKz4b2zJgzz5Y7ewO4OARftTFr
 | 
				
			||||||
 | 
					EgorBgEEAZdVAQUBAQdAi86WcrR9ArfA48pJ6hFiPilSfYLd7vZJ4UgeN/I6kB4D
 | 
				
			||||||
 | 
					AQgHiHgEGBYIAAkFAl+1MWsCGwwAIQkQEpCh0Pp+4QkWIQQBhkVNY+g9he+R3k4S
 | 
				
			||||||
 | 
					kKHQ+n7hCdfMAP49okBRnhH7n4VLmfdygZUDJyfMSPG4CBC+wL2igQMqBAEAjnAf
 | 
				
			||||||
 | 
					VjbDqrZ5bf6eBbSEblyyQBtKvlARiNUu1oNsogw=
 | 
				
			||||||
 | 
					=PWmb
 | 
				
			||||||
 | 
					-----END PGP PUBLIC KEY BLOCK-----
 | 
				
			||||||
@ -308,6 +308,10 @@ def delay_for(delay_event, delay_for=60):
 | 
				
			|||||||
    delay_event.wait(delay_for)
 | 
					    delay_event.wait(delay_for)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_boolean(s):
 | 
				
			||||||
 | 
					    return s.lower() in ['1', 'true']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def make_rpc_func(node_id, base_rpc_port=BASE_RPC_PORT):
 | 
					def make_rpc_func(node_id, base_rpc_port=BASE_RPC_PORT):
 | 
				
			||||||
    node_id = node_id
 | 
					    node_id = node_id
 | 
				
			||||||
    auth = 'test{0}:test_pass{0}'.format(node_id)
 | 
					    auth = 'test{0}:test_pass{0}'.format(node_id)
 | 
				
			||||||
 | 
				
			|||||||
@ -528,13 +528,13 @@ class Test(unittest.TestCase):
 | 
				
			|||||||
    def test_08_withdrawal(self):
 | 
					    def test_08_withdrawal(self):
 | 
				
			||||||
        logging.info('---------- Test DASH withdrawals')
 | 
					        logging.info('---------- Test DASH withdrawals')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pivx_addr = dashRpc('getnewaddress \"Withdrawal test\"')
 | 
					        addr = dashRpc('getnewaddress \"Withdrawal test\"')
 | 
				
			||||||
        wallets0 = read_json_api(TEST_HTTP_PORT + 0, 'wallets')
 | 
					        wallets = read_json_api(TEST_HTTP_PORT + 0, 'wallets')
 | 
				
			||||||
        assert (float(wallets0['DASH']['balance']) > 100)
 | 
					        assert (float(wallets['DASH']['balance']) > 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post_json = {
 | 
					        post_json = {
 | 
				
			||||||
            'value': 100,
 | 
					            'value': 100,
 | 
				
			||||||
            'address': pivx_addr,
 | 
					            'address': addr,
 | 
				
			||||||
            'subfee': False,
 | 
					            'subfee': False,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/dash/withdraw'.format(TEST_HTTP_PORT + 0), post_json))
 | 
					        json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/dash/withdraw'.format(TEST_HTTP_PORT + 0), post_json))
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										465
									
								
								tests/basicswap/extended/test_firo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								tests/basicswap/extended/test_firo.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,465 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2022 tecnovert
 | 
				
			||||||
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import basicswap.config as cfg
 | 
				
			||||||
 | 
					from basicswap.basicswap import (
 | 
				
			||||||
 | 
					    Coins,
 | 
				
			||||||
 | 
					    TxStates,
 | 
				
			||||||
 | 
					    SwapTypes,
 | 
				
			||||||
 | 
					    BidStates,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.basicswap_util import (
 | 
				
			||||||
 | 
					    TxLockTypes,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.util import (
 | 
				
			||||||
 | 
					    COIN,
 | 
				
			||||||
 | 
					    make_int,
 | 
				
			||||||
 | 
					    format_amount,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.rpc import (
 | 
				
			||||||
 | 
					    callrpc_cli,
 | 
				
			||||||
 | 
					    waitForRPC,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from tests.basicswap.common import (
 | 
				
			||||||
 | 
					    stopDaemons,
 | 
				
			||||||
 | 
					    wait_for_bid,
 | 
				
			||||||
 | 
					    make_rpc_func,
 | 
				
			||||||
 | 
					    read_json_api,
 | 
				
			||||||
 | 
					    post_json_req,
 | 
				
			||||||
 | 
					    TEST_HTTP_PORT,
 | 
				
			||||||
 | 
					    wait_for_offer,
 | 
				
			||||||
 | 
					    wait_for_in_progress,
 | 
				
			||||||
 | 
					    wait_for_bid_tx_state,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.contrib.test_framework.messages import (
 | 
				
			||||||
 | 
					    FromHex,
 | 
				
			||||||
 | 
					    CTransaction,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from bin.basicswap_run import startDaemon
 | 
				
			||||||
 | 
					from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
 | 
				
			||||||
 | 
					from tests.basicswap.test_xmr import BaseTest, test_delay_event, callnoderpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FIRO_BASE_PORT = 34832
 | 
				
			||||||
 | 
					FIRO_BASE_RPC_PORT = 35832
 | 
				
			||||||
 | 
					FIRO_BASE_ZMQ_PORT = 36832
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def firoCli(cmd, node_id=0):
 | 
				
			||||||
 | 
					    return callrpc_cli(cfg.FIRO_BINDIR, os.path.join(cfg.TEST_DATADIRS, 'firo_' + str(node_id)), 'regtest', cmd, cfg.FIRO_CLI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prepareDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_port, base_rpc_port, num_nodes=3):
 | 
				
			||||||
 | 
					    node_dir = os.path.join(datadir, dir_prefix + str(node_id))
 | 
				
			||||||
 | 
					    if not os.path.exists(node_dir):
 | 
				
			||||||
 | 
					        os.makedirs(node_dir)
 | 
				
			||||||
 | 
					    cfg_file_path = os.path.join(node_dir, conf_file)
 | 
				
			||||||
 | 
					    if os.path.exists(cfg_file_path):
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    with open(cfg_file_path, 'w+') as fp:
 | 
				
			||||||
 | 
					        fp.write('regtest=1\n')
 | 
				
			||||||
 | 
					        fp.write('port=' + str(base_p2p_port + node_id) + '\n')
 | 
				
			||||||
 | 
					        fp.write('rpcport=' + str(base_rpc_port + node_id) + '\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        salt = generate_salt(16)
 | 
				
			||||||
 | 
					        fp.write('rpcauth={}:{}${}\n'.format('test' + str(node_id), salt, password_to_hmac(salt, 'test_pass' + str(node_id))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.write('daemon=0\n')
 | 
				
			||||||
 | 
					        fp.write('dandelion=0\n')
 | 
				
			||||||
 | 
					        fp.write('printtoconsole=0\n')
 | 
				
			||||||
 | 
					        fp.write('server=1\n')
 | 
				
			||||||
 | 
					        fp.write('discover=0\n')
 | 
				
			||||||
 | 
					        fp.write('listenonion=0\n')
 | 
				
			||||||
 | 
					        fp.write('bind=127.0.0.1\n')
 | 
				
			||||||
 | 
					        fp.write('findpeers=0\n')
 | 
				
			||||||
 | 
					        fp.write('debug=1\n')
 | 
				
			||||||
 | 
					        fp.write('debugexclude=libevent\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.write('fallbackfee=0.01\n')
 | 
				
			||||||
 | 
					        fp.write('acceptnonstdtxn=0\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # qa/rpc-tests/segwit.py
 | 
				
			||||||
 | 
					        fp.write('prematurewitness=1\n')
 | 
				
			||||||
 | 
					        fp.write('walletprematurewitness=1\n')
 | 
				
			||||||
 | 
					        fp.write('blockversion=4\n')
 | 
				
			||||||
 | 
					        fp.write('promiscuousmempoolflags=517\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(0, num_nodes):
 | 
				
			||||||
 | 
					            if node_id == i:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            fp.write('addnode=127.0.0.1:{}\n'.format(base_p2p_port + i))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return node_dir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Test(BaseTest):
 | 
				
			||||||
 | 
					    __test__ = True
 | 
				
			||||||
 | 
					    firo_daemons = []
 | 
				
			||||||
 | 
					    firo_addr = None
 | 
				
			||||||
 | 
					    test_coin_from = Coins.FIRO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test_atomic = True
 | 
				
			||||||
 | 
					    test_xmr = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpClass(cls):
 | 
				
			||||||
 | 
					        cls.start_ltc_nodes = False
 | 
				
			||||||
 | 
					        cls.start_xmr_nodes = False
 | 
				
			||||||
 | 
					        super(Test, cls).setUpClass()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def prepareExtraDataDir(cls, i):
 | 
				
			||||||
 | 
					        if not cls.restore_instance:
 | 
				
			||||||
 | 
					            data_dir = prepareDataDir(cfg.TEST_DATADIRS, i, 'firo.conf', 'firo_', base_p2p_port=FIRO_BASE_PORT, base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					            if os.path.exists(os.path.join(cfg.FIRO_BINDIR, 'firo-wallet')):
 | 
				
			||||||
 | 
					                callrpc_cli(cfg.FIRO_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'firo-wallet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cls.firo_daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, 'firo_' + str(i)), cfg.FIRO_BINDIR, cfg.FIROD))
 | 
				
			||||||
 | 
					        logging.info('Started %s %d', cfg.FIROD, cls.part_daemons[-1].pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        waitForRPC(make_rpc_func(i, base_rpc_port=FIRO_BASE_RPC_PORT))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def addPIDInfo(cls, sc, i):
 | 
				
			||||||
 | 
					        sc.setDaemonPID(Coins.FIRO, cls.firo_daemons[i].pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def prepareExtraCoins(cls):
 | 
				
			||||||
 | 
					        if cls.restore_instance:
 | 
				
			||||||
 | 
					            void_block_rewards_pubkey = cls.getRandomPubkey()
 | 
				
			||||||
 | 
					            cls.firo_addr = cls.swap_clients[0].ci(Coins.FIRO).pubkey_to_address(void_block_rewards_pubkey)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            num_blocks = 400
 | 
				
			||||||
 | 
					            cls.firo_addr = callnoderpc(0, 'getnewaddress', ['mining_addr'], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					            # cls.firo_addr = callnoderpc(0, 'addwitnessaddress', [cls.firo_addr], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					            logging.info('Mining %d Firo blocks to %s', num_blocks, cls.firo_addr)
 | 
				
			||||||
 | 
					            callnoderpc(0, 'generatetoaddress', [num_blocks, cls.firo_addr], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            firo_addr1 = callnoderpc(1, 'getnewaddress', ['initial addr'], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					            # firo_addr1 = callnoderpc(1, 'addwitnessaddress', [firo_addr1], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					            for i in range(5):
 | 
				
			||||||
 | 
					                callnoderpc(0, 'sendtoaddress', [firo_addr1, 1000], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Set future block rewards to nowhere (a random address), so wallet amounts stay constant
 | 
				
			||||||
 | 
					            void_block_rewards_pubkey = cls.getRandomPubkey()
 | 
				
			||||||
 | 
					            cls.firo_addr = cls.swap_clients[0].ci(Coins.FIRO).pubkey_to_address(void_block_rewards_pubkey)
 | 
				
			||||||
 | 
					            num_blocks = 100
 | 
				
			||||||
 | 
					            logging.info('Mining %d Firo blocks to %s', num_blocks, cls.firo_addr)
 | 
				
			||||||
 | 
					            callnoderpc(0, 'generatetoaddress', [num_blocks, cls.firo_addr], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def tearDownClass(cls):
 | 
				
			||||||
 | 
					        logging.info('Finalising FIRO Test')
 | 
				
			||||||
 | 
					        super(Test, cls).tearDownClass()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stopDaemons(cls.firo_daemons)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def addCoinSettings(cls, settings, datadir, node_id):
 | 
				
			||||||
 | 
					        settings['chainclients']['firo'] = {
 | 
				
			||||||
 | 
					            'connection_type': 'rpc',
 | 
				
			||||||
 | 
					            'manage_daemon': False,
 | 
				
			||||||
 | 
					            'rpcport': FIRO_BASE_RPC_PORT + node_id,
 | 
				
			||||||
 | 
					            'rpcuser': 'test' + str(node_id),
 | 
				
			||||||
 | 
					            'rpcpassword': 'test_pass' + str(node_id),
 | 
				
			||||||
 | 
					            'datadir': os.path.join(datadir, 'firo_' + str(node_id)),
 | 
				
			||||||
 | 
					            'bindir': cfg.FIRO_BINDIR,
 | 
				
			||||||
 | 
					            'use_csv': True,
 | 
				
			||||||
 | 
					            'use_segwit': False,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def coins_loop(cls):
 | 
				
			||||||
 | 
					        super(Test, cls).coins_loop()
 | 
				
			||||||
 | 
					        callnoderpc(0, 'generatetoaddress', [1, cls.firo_addr], base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getBalance(self, js_wallets):
 | 
				
			||||||
 | 
					        return float(js_wallets[self.test_coin_from.name]['balance']) + float(js_wallets[self.test_coin_from.name]['unconfirmed'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getXmrBalance(self, js_wallets):
 | 
				
			||||||
 | 
					        return float(js_wallets[Coins.XMR.name]['unconfirmed']) + float(js_wallets[Coins.XMR.name]['balance'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def callnoderpc(self, method, params=[], wallet=None, node_id=0):
 | 
				
			||||||
 | 
					        return callnoderpc(node_id, method, params, wallet, base_rpc_port=FIRO_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_01_firo(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} segwit'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Segwit is not currently enabled:
 | 
				
			||||||
 | 
					        https://github.com/firoorg/firo/blob/master/src/validation.cpp#L4425
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Txns spending segwit utxos don't get mined.
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_plain = firoCli('getnewaddress \"segwit test\"')
 | 
				
			||||||
 | 
					        addr_witness = firoCli(f'addwitnessaddress {addr_plain}')
 | 
				
			||||||
 | 
					        addr_witness_info = firoCli(f'validateaddress {addr_witness}')
 | 
				
			||||||
 | 
					        txid = firoCli(f'sendtoaddress {addr_witness} 1.0')
 | 
				
			||||||
 | 
					        assert len(txid) == 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [1, self.firo_addr])
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        TODO: Add back when segwit is active
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('scantxoutset', ['start', ['addr({})'.format(addr_witness)]])
 | 
				
			||||||
 | 
					        assert (len(ro['unspents']) == 1)
 | 
				
			||||||
 | 
					        assert (ro['unspents'][0]['txid'] == txid)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_wallet = firoCli(f'gettransaction {txid}')
 | 
				
			||||||
 | 
					        tx_hex = tx_wallet['hex']
 | 
				
			||||||
 | 
					        tx = firoCli(f'decoderawtransaction {tx_hex}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prevout_n = -1
 | 
				
			||||||
 | 
					        for txo in tx['vout']:
 | 
				
			||||||
 | 
					            if addr_witness in txo['scriptPubKey']['addresses']:
 | 
				
			||||||
 | 
					                prevout_n = txo['n']
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        assert prevout_n > -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_funded = firoCli(f'createrawtransaction [{{\\"txid\\":\\"{txid}\\",\\"vout\\":{prevout_n}}}] {{\\"{addr_plain}\\":0.99}}')
 | 
				
			||||||
 | 
					        tx_signed = firoCli(f'signrawtransaction {tx_funded}')['hex']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Add scriptsig for txids to match
 | 
				
			||||||
 | 
					        decoded_tx = CTransaction()
 | 
				
			||||||
 | 
					        decoded_tx = FromHex(decoded_tx, tx_funded)
 | 
				
			||||||
 | 
					        decoded_tx.vin[0].scriptSig = bytes.fromhex('16' + addr_witness_info['hex'])
 | 
				
			||||||
 | 
					        txid_with_scriptsig = decoded_tx.rehash()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_funded_decoded = firoCli(f'decoderawtransaction {tx_funded}')
 | 
				
			||||||
 | 
					        tx_signed_decoded = firoCli(f'decoderawtransaction {tx_signed}')
 | 
				
			||||||
 | 
					        assert tx_funded_decoded['txid'] != tx_signed_decoded['txid']
 | 
				
			||||||
 | 
					        assert txid_with_scriptsig == tx_signed_decoded['txid']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_02_part_coin(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test PART to {}'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        if not self.test_atomic:
 | 
				
			||||||
 | 
					            logging.warning('Skipping test')
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(Coins.PART, self.test_coin_from, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[1].listOffers()
 | 
				
			||||||
 | 
					        assert (len(offers) == 1)
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_in_progress(test_delay_event, swap_clients[1], bid_id, sent=True)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800)
 | 
				
			||||||
 | 
					        js_1 = read_json_api(1801)
 | 
				
			||||||
 | 
					        assert (js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert (js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_03_coin_part(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} to PART'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[1].postOffer(self.test_coin_from, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[0], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[0].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[1].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_in_progress(test_delay_event, swap_clients[0], bid_id, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800)
 | 
				
			||||||
 | 
					        js_1 = read_json_api(1801)
 | 
				
			||||||
 | 
					        assert (js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert (js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_04_coin_btc(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} to BTC'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(self.test_coin_from, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[1].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_in_progress(test_delay_event, swap_clients[1], bid_id, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, wait_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0bid = read_json_api(1800, 'bids/{}'.format(bid_id.hex()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800)
 | 
				
			||||||
 | 
					        js_1 = read_json_api(1801)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert (js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert (js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_05_refund(self):
 | 
				
			||||||
 | 
					        # Seller submits initiate txn, buyer doesn't respond
 | 
				
			||||||
 | 
					        logging.info('---------- Test refund, {} to BTC'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(self.test_coin_from, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST,
 | 
				
			||||||
 | 
					                                             TxLockTypes.SEQUENCE_LOCK_BLOCKS, 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[1].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[1].abandonBid(bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.BID_ABANDONED, sent=True, wait_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800)
 | 
				
			||||||
 | 
					        js_1 = read_json_api(1801)
 | 
				
			||||||
 | 
					        assert (js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert (js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_06_self_bid(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test same client, BTC to {}'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0_before = read_json_api(1800)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(self.test_coin_from, Coins.BTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[0], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[0].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid_tx_state(test_delay_event, swap_clients[0], bid_id, TxStates.TX_REDEEMED, TxStates.TX_REDEEMED, wait_for=60)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800)
 | 
				
			||||||
 | 
					        assert (js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert (js_0['num_recv_bids'] == js_0_before['num_recv_bids'] + 1 and js_0['num_sent_bids'] == js_0_before['num_sent_bids'] + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_07_error(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test error, BTC to {}, set fee above bid value'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0_before = read_json_api(1800)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(self.test_coin_from, Coins.BTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[0], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[0].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_08_withdrawal(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} withdrawals'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr = self.callnoderpc('getnewaddress', ['Withdrawal test', ])
 | 
				
			||||||
 | 
					        wallets = read_json_api(TEST_HTTP_PORT + 0, 'wallets')
 | 
				
			||||||
 | 
					        assert (float(wallets[self.test_coin_from.name]['balance']) > 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        post_json = {
 | 
				
			||||||
 | 
					            'value': 100,
 | 
				
			||||||
 | 
					            'address': addr,
 | 
				
			||||||
 | 
					            'subfee': False,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/{}/withdraw'.format(TEST_HTTP_PORT + 0, self.test_coin_from.name.lower()), post_json))
 | 
				
			||||||
 | 
					        assert (len(json_rv['txid']) == 64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_101_full_swap(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} to XMR'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        if not self.test_xmr:
 | 
				
			||||||
 | 
					            logging.warning('Skipping test')
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800, 'wallets')
 | 
				
			||||||
 | 
					        node0_from_before = self.getBalance(js_0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_1 = read_json_api(1801, 'wallets')
 | 
				
			||||||
 | 
					        node1_from_before = self.getBalance(js_1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0_xmr = read_json_api(1800, 'wallets/xmr')
 | 
				
			||||||
 | 
					        js_1_xmr = read_json_api(1801, 'wallets/xmr')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        amt_swap = make_int(random.uniform(0.1, 2.0), scale=8, r=1)
 | 
				
			||||||
 | 
					        rate_swap = make_int(random.uniform(0.2, 20.0), scale=12, r=1)
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(self.test_coin_from, Coins.XMR, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP)
 | 
				
			||||||
 | 
					        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers(filters={'offer_id': offer_id})
 | 
				
			||||||
 | 
					        offer = offers[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptXmrBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
 | 
				
			||||||
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        amount_from = float(format_amount(amt_swap, 8))
 | 
				
			||||||
 | 
					        js_1 = read_json_api(1801, 'wallets')
 | 
				
			||||||
 | 
					        node1_from_after = self.getBalance(js_1)
 | 
				
			||||||
 | 
					        assert (node1_from_after > node1_from_before + (amount_from - 0.05))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = read_json_api(1800, 'wallets')
 | 
				
			||||||
 | 
					        node0_from_after = self.getBalance(js_0)
 | 
				
			||||||
 | 
					        # TODO: Discard block rewards
 | 
				
			||||||
 | 
					        # assert (node0_from_after < node0_from_before - amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0_xmr_after = read_json_api(1800, 'wallets/xmr')
 | 
				
			||||||
 | 
					        js_1_xmr_after = read_json_api(1801, 'wallets/xmr')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scale_from = 8
 | 
				
			||||||
 | 
					        amount_to = int((amt_swap * rate_swap) // (10 ** scale_from))
 | 
				
			||||||
 | 
					        amount_to_float = float(format_amount(amount_to, 12))
 | 
				
			||||||
 | 
					        node1_xmr_after = float(js_1_xmr_after['unconfirmed']) + float(js_1_xmr_after['balance'])
 | 
				
			||||||
 | 
					        node1_xmr_before = float(js_1_xmr['unconfirmed']) + float(js_1_xmr['balance'])
 | 
				
			||||||
 | 
					        assert (node1_xmr_after > node1_xmr_before + (amount_to_float - 0.02))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
@ -538,13 +538,13 @@ class Test(unittest.TestCase):
 | 
				
			|||||||
    def test_08_withdrawal(self):
 | 
					    def test_08_withdrawal(self):
 | 
				
			||||||
        logging.info('---------- Test PIVX withdrawals')
 | 
					        logging.info('---------- Test PIVX withdrawals')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pivx_addr = pivxRpc('getnewaddress \"Withdrawal test\"')
 | 
					        addr = pivxRpc('getnewaddress \"Withdrawal test\"')
 | 
				
			||||||
        wallets0 = read_json_api(TEST_HTTP_PORT + 0, 'wallets')
 | 
					        wallets = read_json_api(TEST_HTTP_PORT + 0, 'wallets')
 | 
				
			||||||
        assert (float(wallets0['PIVX']['balance']) > 100)
 | 
					        assert (float(wallets['PIVX']['balance']) > 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        post_json = {
 | 
					        post_json = {
 | 
				
			||||||
            'value': 100,
 | 
					            'value': 100,
 | 
				
			||||||
            'address': pivx_addr,
 | 
					            'address': addr,
 | 
				
			||||||
            'subfee': False,
 | 
					            'subfee': False,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/pivx/withdraw'.format(TEST_HTTP_PORT + 0), post_json))
 | 
					        json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/pivx/withdraw'.format(TEST_HTTP_PORT + 0), post_json))
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,7 @@ from basicswap.rpc import (
 | 
				
			|||||||
    callrpc,
 | 
					    callrpc,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from tests.basicswap.common import (
 | 
					from tests.basicswap.common import (
 | 
				
			||||||
 | 
					    make_boolean,
 | 
				
			||||||
    read_json_api,
 | 
					    read_json_api,
 | 
				
			||||||
    waitForServer,
 | 
					    waitForServer,
 | 
				
			||||||
    BASE_RPC_PORT,
 | 
					    BASE_RPC_PORT,
 | 
				
			||||||
@ -46,10 +47,6 @@ from tests.basicswap.common_xmr import (
 | 
				
			|||||||
import bin.basicswap_run as runSystem
 | 
					import bin.basicswap_run as runSystem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def make_boolean(s):
 | 
					 | 
				
			||||||
    return s.lower() in ['1', 'true']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test_path = os.path.expanduser(os.getenv('TEST_PATH', '/tmp/test_persistent'))
 | 
					test_path = os.path.expanduser(os.getenv('TEST_PATH', '/tmp/test_persistent'))
 | 
				
			||||||
RESET_TEST = make_boolean(os.getenv('RESET_TEST', 'false'))
 | 
					RESET_TEST = make_boolean(os.getenv('RESET_TEST', 'false'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -27,39 +27,217 @@ from tests.basicswap.common import (
 | 
				
			|||||||
    read_json_api,
 | 
					    read_json_api,
 | 
				
			||||||
    wait_for_offer,
 | 
					    wait_for_offer,
 | 
				
			||||||
    wait_for_none_active,
 | 
					    wait_for_none_active,
 | 
				
			||||||
 | 
					    BTC_BASE_RPC_PORT,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.contrib.test_framework.messages import (
 | 
				
			||||||
from .test_xmr import BaseTest, test_delay_event
 | 
					    ToHex,
 | 
				
			||||||
 | 
					    FromHex,
 | 
				
			||||||
 | 
					    CTxIn,
 | 
				
			||||||
 | 
					    COutPoint,
 | 
				
			||||||
 | 
					    CTransaction,
 | 
				
			||||||
 | 
					    CTxInWitness,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.contrib.test_framework.script import (
 | 
				
			||||||
 | 
					    CScript,
 | 
				
			||||||
 | 
					    OP_CHECKLOCKTIMEVERIFY,
 | 
				
			||||||
 | 
					    OP_CHECKSEQUENCEVERIFY,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from .test_xmr import BaseTest, test_delay_event, callnoderpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger()
 | 
					logger = logging.getLogger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Test(BaseTest):
 | 
					class Test(BaseTest):
 | 
				
			||||||
    __test__ = True
 | 
					    __test__ = True
 | 
				
			||||||
 | 
					    test_coin_from = Coins.BTC
 | 
				
			||||||
    @classmethod
 | 
					    start_ltc_nodes = False
 | 
				
			||||||
    def setUpClass(cls):
 | 
					 | 
				
			||||||
        if not hasattr(cls, 'test_coin_from'):
 | 
					 | 
				
			||||||
            cls.test_coin_from = Coins.BTC
 | 
					 | 
				
			||||||
        if not hasattr(cls, 'start_ltc_nodes'):
 | 
					 | 
				
			||||||
            cls.start_ltc_nodes = False
 | 
					 | 
				
			||||||
        if not hasattr(cls, 'start_pivx_nodes'):
 | 
					 | 
				
			||||||
            cls.start_pivx_nodes = False
 | 
					 | 
				
			||||||
        super(Test, cls).setUpClass()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def tearDownClass(cls):
 | 
					 | 
				
			||||||
        logging.info('Finalising BTC Test')
 | 
					 | 
				
			||||||
        super(Test, cls).tearDownClass()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getBalance(self, js_wallets):
 | 
					    def getBalance(self, js_wallets):
 | 
				
			||||||
        return float(js_wallets[self.test_coin_from.name]['balance']) + float(js_wallets[self.test_coin_from.name]['unconfirmed'])
 | 
					        return float(js_wallets[self.test_coin_from.name]['balance']) + float(js_wallets[self.test_coin_from.name]['unconfirmed'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getXmrBalance(self, js_wallets):
 | 
					    def callnoderpc(self, method, params=[], wallet=None, node_id=0):
 | 
				
			||||||
        return float(js_wallets[Coins.XMR.name]['unconfirmed']) + float(js_wallets[Coins.XMR.name]['balance'])
 | 
					        return callnoderpc(node_id, method, params, wallet, base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_001_nested_segwit(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} p2sh nested segwit'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_p2sh_segwit = self.callnoderpc('getnewaddress', ['segwit test', 'p2sh-segwit'])
 | 
				
			||||||
 | 
					        addr_info = self.callnoderpc('getaddressinfo', [addr_p2sh_segwit, ])
 | 
				
			||||||
 | 
					        assert addr_info['script'] == 'witness_v0_keyhash'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendtoaddress', [addr_p2sh_segwit, 1.0])
 | 
				
			||||||
 | 
					        assert len(txid) == 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [1, self.btc_addr])
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('scantxoutset', ['start', ['addr({})'.format(addr_p2sh_segwit)]])
 | 
				
			||||||
 | 
					        assert (len(ro['unspents']) == 1)
 | 
				
			||||||
 | 
					        assert (ro['unspents'][0]['txid'] == txid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_wallet = self.callnoderpc('gettransaction', [txid, ])['hex']
 | 
				
			||||||
 | 
					        tx = self.callnoderpc('decoderawtransaction', [tx_wallet, ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prevout_n = -1
 | 
				
			||||||
 | 
					        for txo in tx['vout']:
 | 
				
			||||||
 | 
					            if addr_p2sh_segwit == txo['scriptPubKey']['address']:
 | 
				
			||||||
 | 
					                prevout_n = txo['n']
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        assert prevout_n > -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_funded = self.callnoderpc('createrawtransaction', [[{'txid': txid, 'vout': prevout_n}], {addr_p2sh_segwit: 0.99}])
 | 
				
			||||||
 | 
					        tx_signed = self.callnoderpc('signrawtransactionwithwallet', [tx_funded, ])['hex']
 | 
				
			||||||
 | 
					        tx_funded_decoded = self.callnoderpc('decoderawtransaction', [tx_funded, ])
 | 
				
			||||||
 | 
					        tx_signed_decoded = self.callnoderpc('decoderawtransaction', [tx_signed, ])
 | 
				
			||||||
 | 
					        assert tx_funded_decoded['txid'] != tx_signed_decoded['txid']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Add scriptsig for txids to match
 | 
				
			||||||
 | 
					        addr_p2sh_segwit_info = self.callnoderpc('getaddressinfo', [addr_p2sh_segwit, ])
 | 
				
			||||||
 | 
					        decoded_tx = FromHex(CTransaction(), tx_funded)
 | 
				
			||||||
 | 
					        decoded_tx.vin[0].scriptSig = bytes.fromhex('16' + addr_p2sh_segwit_info['hex'])
 | 
				
			||||||
 | 
					        txid_with_scriptsig = decoded_tx.rehash()
 | 
				
			||||||
 | 
					        assert txid_with_scriptsig == tx_signed_decoded['txid']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_002_native_segwit(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} p2sh native segwit'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_segwit = self.callnoderpc('getnewaddress', ['segwit test', 'bech32'])
 | 
				
			||||||
 | 
					        addr_info = self.callnoderpc('getaddressinfo', [addr_segwit, ])
 | 
				
			||||||
 | 
					        assert addr_info['iswitness'] is True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendtoaddress', [addr_segwit, 1.0])
 | 
				
			||||||
 | 
					        assert len(txid) == 64
 | 
				
			||||||
 | 
					        tx_wallet = self.callnoderpc('gettransaction', [txid, ])['hex']
 | 
				
			||||||
 | 
					        tx = self.callnoderpc('decoderawtransaction', [tx_wallet, ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [1, self.btc_addr])
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('scantxoutset', ['start', ['addr({})'.format(addr_segwit)]])
 | 
				
			||||||
 | 
					        assert (len(ro['unspents']) == 1)
 | 
				
			||||||
 | 
					        assert (ro['unspents'][0]['txid'] == txid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prevout_n = -1
 | 
				
			||||||
 | 
					        for txo in tx['vout']:
 | 
				
			||||||
 | 
					            if addr_segwit == txo['scriptPubKey']['address']:
 | 
				
			||||||
 | 
					                prevout_n = txo['n']
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        assert prevout_n > -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_funded = self.callnoderpc('createrawtransaction', [[{'txid': txid, 'vout': prevout_n}], {addr_segwit: 0.99}])
 | 
				
			||||||
 | 
					        tx_signed = self.callnoderpc('signrawtransactionwithwallet', [tx_funded, ])['hex']
 | 
				
			||||||
 | 
					        tx_funded_decoded = self.callnoderpc('decoderawtransaction', [tx_funded, ])
 | 
				
			||||||
 | 
					        tx_signed_decoded = self.callnoderpc('decoderawtransaction', [tx_signed, ])
 | 
				
			||||||
 | 
					        assert tx_funded_decoded['txid'] == tx_signed_decoded['txid']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_003_cltv(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} cltv'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        ci = self.swap_clients[0].ci(self.test_coin_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        chain_height = self.callnoderpc('getblockcount')
 | 
				
			||||||
 | 
					        script = CScript([chain_height + 3, OP_CHECKLOCKTIMEVERIFY, ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        script_dest = ci.getScriptDest(script)
 | 
				
			||||||
 | 
					        tx = CTransaction()
 | 
				
			||||||
 | 
					        tx.nVersion = ci.txVersion()
 | 
				
			||||||
 | 
					        tx.vout.append(ci.txoType()(ci.make_int(1.1), script_dest))
 | 
				
			||||||
 | 
					        tx_hex = ToHex(tx)
 | 
				
			||||||
 | 
					        tx_funded = self.callnoderpc('fundrawtransaction', [tx_hex])
 | 
				
			||||||
 | 
					        utxo_pos = 0 if tx_funded['changepos'] == 1 else 1
 | 
				
			||||||
 | 
					        tx_signed = self.callnoderpc('signrawtransactionwithwallet', [tx_funded['hex'], ])['hex']
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendrawtransaction', [tx_signed, ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_out = self.callnoderpc('getnewaddress', ['csv test', 'bech32'])
 | 
				
			||||||
 | 
					        pkh = ci.decodeSegwitAddress(addr_out)
 | 
				
			||||||
 | 
					        script_out = ci.getScriptForPubkeyHash(pkh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_spend = CTransaction()
 | 
				
			||||||
 | 
					        tx_spend.nVersion = ci.txVersion()
 | 
				
			||||||
 | 
					        tx_spend.nLockTime = chain_height + 3
 | 
				
			||||||
 | 
					        tx_spend.vin.append(CTxIn(COutPoint(int(txid, 16), utxo_pos)))
 | 
				
			||||||
 | 
					        tx_spend.vout.append(ci.txoType()(ci.make_int(1.0999), script_out))
 | 
				
			||||||
 | 
					        tx_spend.wit.vtxinwit.append(CTxInWitness())
 | 
				
			||||||
 | 
					        tx_spend.wit.vtxinwit[0].scriptWitness.stack = [script, ]
 | 
				
			||||||
 | 
					        tx_spend_hex = ToHex(tx_spend)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            txid = self.callnoderpc('sendrawtransaction', [tx_spend_hex, ])
 | 
				
			||||||
 | 
					            assert False, 'Should fail'
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            assert ('non-final' in str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [49, self.btc_addr])
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendrawtransaction', [tx_spend_hex, ])
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [1, self.btc_addr])
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('listreceivedbyaddress', [0, ])
 | 
				
			||||||
 | 
					        sum_addr = 0
 | 
				
			||||||
 | 
					        for entry in ro:
 | 
				
			||||||
 | 
					            if entry['address'] == addr_out:
 | 
				
			||||||
 | 
					                sum_addr += entry['amount']
 | 
				
			||||||
 | 
					        assert (sum_addr == 1.0999)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_004_csv(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} csv'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					        ci = self.swap_clients[0].ci(self.test_coin_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        script = CScript([3, OP_CHECKSEQUENCEVERIFY, ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        script_dest = ci.getScriptDest(script)
 | 
				
			||||||
 | 
					        tx = CTransaction()
 | 
				
			||||||
 | 
					        tx.nVersion = ci.txVersion()
 | 
				
			||||||
 | 
					        tx.vout.append(ci.txoType()(ci.make_int(1.1), script_dest))
 | 
				
			||||||
 | 
					        tx_hex = ToHex(tx)
 | 
				
			||||||
 | 
					        tx_funded = self.callnoderpc('fundrawtransaction', [tx_hex])
 | 
				
			||||||
 | 
					        utxo_pos = 0 if tx_funded['changepos'] == 1 else 1
 | 
				
			||||||
 | 
					        tx_signed = self.callnoderpc('signrawtransactionwithwallet', [tx_funded['hex'], ])['hex']
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendrawtransaction', [tx_signed, ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_out = self.callnoderpc('getnewaddress', ['csv test', 'bech32'])
 | 
				
			||||||
 | 
					        pkh = ci.decodeSegwitAddress(addr_out)
 | 
				
			||||||
 | 
					        script_out = ci.getScriptForPubkeyHash(pkh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tx_spend = CTransaction()
 | 
				
			||||||
 | 
					        tx_spend.nVersion = ci.txVersion()
 | 
				
			||||||
 | 
					        tx_spend.vin.append(CTxIn(COutPoint(int(txid, 16), utxo_pos),
 | 
				
			||||||
 | 
					                            nSequence=3))
 | 
				
			||||||
 | 
					        tx_spend.vout.append(ci.txoType()(ci.make_int(1.0999), script_out))
 | 
				
			||||||
 | 
					        tx_spend.wit.vtxinwit.append(CTxInWitness())
 | 
				
			||||||
 | 
					        tx_spend.wit.vtxinwit[0].scriptWitness.stack = [script, ]
 | 
				
			||||||
 | 
					        tx_spend_hex = ToHex(tx_spend)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            txid = self.callnoderpc('sendrawtransaction', [tx_spend_hex, ])
 | 
				
			||||||
 | 
					            assert False, 'Should fail'
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            assert ('non-BIP68-final' in str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [3, self.btc_addr])
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendrawtransaction', [tx_spend_hex, ])
 | 
				
			||||||
 | 
					        self.callnoderpc('generatetoaddress', [1, self.btc_addr])
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('listreceivedbyaddress', [0, ])
 | 
				
			||||||
 | 
					        sum_addr = 0
 | 
				
			||||||
 | 
					        for entry in ro:
 | 
				
			||||||
 | 
					            if entry['address'] == addr_out:
 | 
				
			||||||
 | 
					                sum_addr += entry['amount']
 | 
				
			||||||
 | 
					        assert (sum_addr == 1.0999)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_005_watchonly(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} watchonly'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr = self.callnoderpc('getnewaddress', ['watchonly test', 'bech32'])
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('importaddress', [addr, '', False], node_id=1)
 | 
				
			||||||
 | 
					        txid = self.callnoderpc('sendtoaddress', [addr, 1.0])
 | 
				
			||||||
 | 
					        tx_hex = self.callnoderpc('getrawtransaction', [txid, ])
 | 
				
			||||||
 | 
					        self.callnoderpc('sendrawtransaction', [tx_hex, ], node_id=1)
 | 
				
			||||||
 | 
					        ro = self.callnoderpc('gettransaction', [txid, ], node_id=1)
 | 
				
			||||||
 | 
					        assert (ro['txid'] == txid)
 | 
				
			||||||
 | 
					        balances = self.callnoderpc('getbalances', node_id=1)
 | 
				
			||||||
 | 
					        assert (balances['watchonly']['trusted'] + balances['watchonly']['untrusted_pending'] >= 1.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_006_getblock_verbosity(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} getblock verbosity'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        best_hash = self.callnoderpc('getbestblockhash')
 | 
				
			||||||
 | 
					        block = self.callnoderpc('getblock', [best_hash, 2])
 | 
				
			||||||
 | 
					        assert ('vin' in block['tx'][0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_01_full_swap(self):
 | 
					    def test_01_full_swap(self):
 | 
				
			||||||
        logging.info('---------- Test {} to XMR'.format(str(self.test_coin_from)))
 | 
					        logging.info('---------- Test {} to XMR'.format(self.test_coin_from.name))
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        js_0 = read_json_api(1800, 'wallets')
 | 
					        js_0 = read_json_api(1800, 'wallets')
 | 
				
			||||||
@ -108,7 +286,7 @@ class Test(BaseTest):
 | 
				
			|||||||
        assert (node1_xmr_after > node1_xmr_before + (amount_to_float - 0.02))
 | 
					        assert (node1_xmr_after > node1_xmr_before + (amount_to_float - 0.02))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_02_leader_recover_a_lock_tx(self):
 | 
					    def test_02_leader_recover_a_lock_tx(self):
 | 
				
			||||||
        logging.info('---------- Test {} to XMR leader recovers coin a lock tx'.format(str(self.test_coin_from)))
 | 
					        logging.info('---------- Test {} to XMR leader recovers coin a lock tx'.format(self.test_coin_from.name))
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        js_w0_before = read_json_api(1800, 'wallets')
 | 
					        js_w0_before = read_json_api(1800, 'wallets')
 | 
				
			||||||
@ -143,7 +321,7 @@ class Test(BaseTest):
 | 
				
			|||||||
        # assert (node0_from_before - node0_from_after < 0.02)
 | 
					        # assert (node0_from_before - node0_from_after < 0.02)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_03_follower_recover_a_lock_tx(self):
 | 
					    def test_03_follower_recover_a_lock_tx(self):
 | 
				
			||||||
        logging.info('---------- Test {} to XMR follower recovers coin a lock tx'.format(str(self.test_coin_from)))
 | 
					        logging.info('---------- Test {} to XMR follower recovers coin a lock tx'.format(self.test_coin_from.name))
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        js_w0_before = read_json_api(1800, 'wallets')
 | 
					        js_w0_before = read_json_api(1800, 'wallets')
 | 
				
			||||||
@ -186,7 +364,7 @@ class Test(BaseTest):
 | 
				
			|||||||
        wait_for_none_active(test_delay_event, 1801)
 | 
					        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 {} to XMR follower recovers coin b lock tx'.format(str(self.test_coin_from)))
 | 
					        logging.info('---------- Test {} to XMR follower recovers coin b lock tx'.format(self.test_coin_from.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -212,7 +212,7 @@ class Test(unittest.TestCase):
 | 
				
			|||||||
        pk = ci.getPubkey(vk)
 | 
					        pk = ci.getPubkey(vk)
 | 
				
			||||||
        sig = ci.signCompact(vk, 'test signing message')
 | 
					        sig = ci.signCompact(vk, 'test signing message')
 | 
				
			||||||
        assert (len(sig) == 64)
 | 
					        assert (len(sig) == 64)
 | 
				
			||||||
        ci.verifyCompact(pk, 'test signing message', sig)
 | 
					        ci.verifyCompactSig(pk, 'test signing message', sig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_pubkey_to_address(self):
 | 
					    def test_pubkey_to_address(self):
 | 
				
			||||||
        coin_settings = {'rpcport': 0, 'rpcauth': 'none'}
 | 
					        coin_settings = {'rpcport': 0, 'rpcauth': 'none'}
 | 
				
			||||||
 | 
				
			|||||||
@ -59,6 +59,7 @@ from basicswap.http_server import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
from tests.basicswap.common import (
 | 
					from tests.basicswap.common import (
 | 
				
			||||||
    prepareDataDir,
 | 
					    prepareDataDir,
 | 
				
			||||||
 | 
					    make_boolean,
 | 
				
			||||||
    make_rpc_func,
 | 
					    make_rpc_func,
 | 
				
			||||||
    checkForks,
 | 
					    checkForks,
 | 
				
			||||||
    stopDaemons,
 | 
					    stopDaemons,
 | 
				
			||||||
@ -80,8 +81,6 @@ from tests.basicswap.common import (
 | 
				
			|||||||
    BTC_BASE_RPC_PORT,
 | 
					    BTC_BASE_RPC_PORT,
 | 
				
			||||||
    LTC_BASE_PORT,
 | 
					    LTC_BASE_PORT,
 | 
				
			||||||
    LTC_BASE_RPC_PORT,
 | 
					    LTC_BASE_RPC_PORT,
 | 
				
			||||||
    PIVX_BASE_PORT,
 | 
					 | 
				
			||||||
    PIVX_BASE_RPC_PORT,
 | 
					 | 
				
			||||||
    PREFIX_SECRET_KEY_REGTEST,
 | 
					    PREFIX_SECRET_KEY_REGTEST,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from bin.basicswap_run import startDaemon, startXmrDaemon
 | 
					from bin.basicswap_run import startDaemon, startXmrDaemon
 | 
				
			||||||
@ -93,7 +92,6 @@ NUM_NODES = 3
 | 
				
			|||||||
NUM_XMR_NODES = 3
 | 
					NUM_XMR_NODES = 3
 | 
				
			||||||
NUM_BTC_NODES = 3
 | 
					NUM_BTC_NODES = 3
 | 
				
			||||||
NUM_LTC_NODES = 3
 | 
					NUM_LTC_NODES = 3
 | 
				
			||||||
NUM_PIVX_NODES = 3
 | 
					 | 
				
			||||||
TEST_DIR = cfg.TEST_DATADIRS
 | 
					TEST_DIR = cfg.TEST_DATADIRS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
XMR_BASE_P2P_PORT = 17792
 | 
					XMR_BASE_P2P_PORT = 17792
 | 
				
			||||||
@ -102,6 +100,7 @@ XMR_BASE_ZMQ_PORT = 22792
 | 
				
			|||||||
XMR_BASE_WALLET_RPC_PORT = 23792
 | 
					XMR_BASE_WALLET_RPC_PORT = 23792
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test_delay_event = threading.Event()
 | 
					test_delay_event = threading.Event()
 | 
				
			||||||
 | 
					RESET_TEST = make_boolean(os.getenv('RESET_TEST', 'true'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def prepareXmrDataDir(datadir, node_id, conf_file):
 | 
					def prepareXmrDataDir(datadir, node_id, conf_file):
 | 
				
			||||||
@ -153,7 +152,7 @@ def startXmrWalletRPC(node_dir, bin_dir, wallet_bin, node_id, opts=[]):
 | 
				
			|||||||
    return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir)
 | 
					    return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey, with_coins=set()):
 | 
					def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey, with_coins=set(), cls=None):
 | 
				
			||||||
    basicswap_dir = os.path.join(datadir, 'basicswap_' + str(node_id))
 | 
					    basicswap_dir = os.path.join(datadir, 'basicswap_' + str(node_id))
 | 
				
			||||||
    if not os.path.exists(basicswap_dir):
 | 
					    if not os.path.exists(basicswap_dir):
 | 
				
			||||||
        os.makedirs(basicswap_dir)
 | 
					        os.makedirs(basicswap_dir)
 | 
				
			||||||
@ -229,17 +228,8 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey, with_c
 | 
				
			|||||||
            'use_segwit': True,
 | 
					            'use_segwit': True,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if Coins.PIVX in with_coins:
 | 
					    if cls:
 | 
				
			||||||
        settings['chainclients']['pivx'] = {
 | 
					        cls.addCoinSettings(settings, datadir, node_id)
 | 
				
			||||||
            'connection_type': 'rpc',
 | 
					 | 
				
			||||||
            'manage_daemon': False,
 | 
					 | 
				
			||||||
            'rpcport': PIVX_BASE_RPC_PORT + node_id,
 | 
					 | 
				
			||||||
            'rpcuser': 'test' + str(node_id),
 | 
					 | 
				
			||||||
            'rpcpassword': 'test_pass' + str(node_id),
 | 
					 | 
				
			||||||
            'datadir': os.path.join(datadir, 'pivx_' + str(node_id)),
 | 
					 | 
				
			||||||
            'bindir': cfg.PIVX_BINDIR,
 | 
					 | 
				
			||||||
            'use_segwit': False,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with open(settings_path, 'w') as fp:
 | 
					    with open(settings_path, 'w') as fp:
 | 
				
			||||||
        json.dump(settings, fp, indent=4)
 | 
					        json.dump(settings, fp, indent=4)
 | 
				
			||||||
@ -253,10 +243,6 @@ 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)
 | 
					    return callrpc_cli(cfg.LITECOIN_BINDIR, os.path.join(TEST_DIR, 'ltc_' + str(node_id)), 'regtest', cmd, cfg.LITECOIN_CLI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def pivxCli(cmd, node_id=0):
 | 
					 | 
				
			||||||
    return callrpc_cli(cfg.PIVX_BINDIR, os.path.join(TEST_DIR, 'pivx_' + str(node_id)), 'regtest', cmd, cfg.PIVX_CLI)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def signal_handler(sig, frame):
 | 
					def signal_handler(sig, frame):
 | 
				
			||||||
    logging.info('signal {} detected.'.format(sig))
 | 
					    logging.info('signal {} detected.'.format(sig))
 | 
				
			||||||
    test_delay_event.set()
 | 
					    test_delay_event.set()
 | 
				
			||||||
@ -298,14 +284,7 @@ def run_coins_loop(cls):
 | 
				
			|||||||
    while not test_delay_event.is_set():
 | 
					    while not test_delay_event.is_set():
 | 
				
			||||||
        pause_event.wait()
 | 
					        pause_event.wait()
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if cls.btc_addr is not None:
 | 
					            cls.coins_loop()
 | 
				
			||||||
                btcCli('generatetoaddress 1 {}'.format(cls.btc_addr))
 | 
					 | 
				
			||||||
            if cls.ltc_addr is not None:
 | 
					 | 
				
			||||||
                ltcCli('generatetoaddress 1 {}'.format(cls.ltc_addr))
 | 
					 | 
				
			||||||
            if cls.pivx_addr is not None:
 | 
					 | 
				
			||||||
                pivxCli('generatetoaddress 1 {}'.format(cls.pivx_addr))
 | 
					 | 
				
			||||||
            if cls.xmr_addr is not None:
 | 
					 | 
				
			||||||
                callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': 1})
 | 
					 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            logging.warning('run_coins_loop ' + str(e))
 | 
					            logging.warning('run_coins_loop ' + str(e))
 | 
				
			||||||
        test_delay_event.wait(1.0)
 | 
					        test_delay_event.wait(1.0)
 | 
				
			||||||
@ -320,34 +299,34 @@ def run_loop(cls):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class BaseTest(unittest.TestCase):
 | 
					class BaseTest(unittest.TestCase):
 | 
				
			||||||
    __test__ = False
 | 
					    __test__ = False
 | 
				
			||||||
 | 
					    update_thread = None
 | 
				
			||||||
 | 
					    coins_update_thread = None
 | 
				
			||||||
 | 
					    http_threads = []
 | 
				
			||||||
 | 
					    swap_clients = []
 | 
				
			||||||
 | 
					    part_daemons = []
 | 
				
			||||||
 | 
					    btc_daemons = []
 | 
				
			||||||
 | 
					    ltc_daemons = []
 | 
				
			||||||
 | 
					    xmr_daemons = []
 | 
				
			||||||
 | 
					    xmr_wallet_auth = []
 | 
				
			||||||
 | 
					    restore_instance = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    start_ltc_nodes = False
 | 
				
			||||||
 | 
					    start_xmr_nodes = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xmr_addr = None
 | 
				
			||||||
 | 
					    btc_addr = None
 | 
				
			||||||
 | 
					    ltc_addr = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def getRandomPubkey(cls):
 | 
				
			||||||
 | 
					        eckey = ECKey()
 | 
				
			||||||
 | 
					        eckey.generate()
 | 
				
			||||||
 | 
					        return eckey.get_pubkey().get_bytes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def setUpClass(cls):
 | 
					    def setUpClass(cls):
 | 
				
			||||||
        if not hasattr(cls, 'start_ltc_nodes'):
 | 
					 | 
				
			||||||
            cls.start_ltc_nodes = False
 | 
					 | 
				
			||||||
        if not hasattr(cls, 'start_pivx_nodes'):
 | 
					 | 
				
			||||||
            cls.start_pivx_nodes = False
 | 
					 | 
				
			||||||
        if not hasattr(cls, 'start_xmr_nodes'):
 | 
					 | 
				
			||||||
            cls.start_xmr_nodes = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        random.seed(time.time())
 | 
					        random.seed(time.time())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cls.update_thread = None
 | 
					 | 
				
			||||||
        cls.coins_update_thread = None
 | 
					 | 
				
			||||||
        cls.http_threads = []
 | 
					 | 
				
			||||||
        cls.swap_clients = []
 | 
					 | 
				
			||||||
        cls.part_daemons = []
 | 
					 | 
				
			||||||
        cls.btc_daemons = []
 | 
					 | 
				
			||||||
        cls.ltc_daemons = []
 | 
					 | 
				
			||||||
        cls.pivx_daemons = []
 | 
					 | 
				
			||||||
        cls.xmr_daemons = []
 | 
					 | 
				
			||||||
        cls.xmr_wallet_auth = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        cls.xmr_addr = None
 | 
					 | 
				
			||||||
        cls.btc_addr = None
 | 
					 | 
				
			||||||
        cls.ltc_addr = None
 | 
					 | 
				
			||||||
        cls.pivx_addr = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logger.propagate = False
 | 
					        logger.propagate = False
 | 
				
			||||||
        logger.handlers = []
 | 
					        logger.handlers = []
 | 
				
			||||||
        logger.setLevel(logging.INFO)  # DEBUG shows many messages from requests.post
 | 
					        logger.setLevel(logging.INFO)  # DEBUG shows many messages from requests.post
 | 
				
			||||||
@ -356,16 +335,24 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
        stream_stdout.setFormatter(formatter)
 | 
					        stream_stdout.setFormatter(formatter)
 | 
				
			||||||
        logger.addHandler(stream_stdout)
 | 
					        logger.addHandler(stream_stdout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        diagrams_dir = 'doc/protocols/sequence_diagrams'
 | 
				
			||||||
 | 
					        cls.states_bidder = extract_states_from_xu_file(os.path.join(diagrams_dir, 'xmr.bidder.alt.xu'), 'B')
 | 
				
			||||||
 | 
					        cls.states_offerer = extract_states_from_xu_file(os.path.join(diagrams_dir, 'xmr.offerer.alt.xu'), 'O')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if os.path.isdir(TEST_DIR):
 | 
					        if os.path.isdir(TEST_DIR):
 | 
				
			||||||
            logging.info('Removing ' + TEST_DIR)
 | 
					            if RESET_TEST:
 | 
				
			||||||
            for name in os.listdir(TEST_DIR):
 | 
					                logging.info('Removing ' + TEST_DIR)
 | 
				
			||||||
                if name == 'pivx-params':
 | 
					                for name in os.listdir(TEST_DIR):
 | 
				
			||||||
                    continue
 | 
					                    if name == 'pivx-params':
 | 
				
			||||||
                fullpath = os.path.join(TEST_DIR, name)
 | 
					                        continue
 | 
				
			||||||
                if os.path.isdir(fullpath):
 | 
					                    fullpath = os.path.join(TEST_DIR, name)
 | 
				
			||||||
                    shutil.rmtree(fullpath)
 | 
					                    if os.path.isdir(fullpath):
 | 
				
			||||||
                else:
 | 
					                        shutil.rmtree(fullpath)
 | 
				
			||||||
                    os.remove(fullpath)
 | 
					                    else:
 | 
				
			||||||
 | 
					                        os.remove(fullpath)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                logging.info('Restoring instance from ' + TEST_DIR)
 | 
				
			||||||
 | 
					                cls.restore_instance = True
 | 
				
			||||||
        if not os.path.exists(TEST_DIR):
 | 
					        if not os.path.exists(TEST_DIR):
 | 
				
			||||||
            os.makedirs(TEST_DIR)
 | 
					            os.makedirs(TEST_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -373,39 +360,38 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
        cls.stream_fp.setFormatter(formatter)
 | 
					        cls.stream_fp.setFormatter(formatter)
 | 
				
			||||||
        logger.addHandler(cls.stream_fp)
 | 
					        logger.addHandler(cls.stream_fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        diagrams_dir = 'doc/protocols/sequence_diagrams'
 | 
					 | 
				
			||||||
        cls.states_bidder = extract_states_from_xu_file(os.path.join(diagrams_dir, 'xmr.bidder.alt.xu'), 'B')
 | 
					 | 
				
			||||||
        cls.states_offerer = extract_states_from_xu_file(os.path.join(diagrams_dir, 'xmr.offerer.alt.xu'), 'O')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            logging.info('Preparing coin nodes.')
 | 
					            logging.info('Preparing coin nodes.')
 | 
				
			||||||
            for i in range(NUM_NODES):
 | 
					            for i in range(NUM_NODES):
 | 
				
			||||||
                data_dir = prepareDataDir(TEST_DIR, i, 'particl.conf', 'part_')
 | 
					                if not cls.restore_instance:
 | 
				
			||||||
                if os.path.exists(os.path.join(cfg.PARTICL_BINDIR, 'particl-wallet')):
 | 
					                    data_dir = prepareDataDir(TEST_DIR, i, 'particl.conf', 'part_')
 | 
				
			||||||
                    callrpc_cli(cfg.PARTICL_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'particl-wallet')
 | 
					                    if os.path.exists(os.path.join(cfg.PARTICL_BINDIR, 'particl-wallet')):
 | 
				
			||||||
 | 
					                        callrpc_cli(cfg.PARTICL_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'particl-wallet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                cls.part_daemons.append(startDaemon(os.path.join(TEST_DIR, 'part_' + str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD))
 | 
					                cls.part_daemons.append(startDaemon(os.path.join(TEST_DIR, 'part_' + str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD))
 | 
				
			||||||
                logging.info('Started %s %d', cfg.PARTICLD, cls.part_daemons[-1].pid)
 | 
					                logging.info('Started %s %d', cfg.PARTICLD, cls.part_daemons[-1].pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for i in range(NUM_NODES):
 | 
					            if not cls.restore_instance:
 | 
				
			||||||
                # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync
 | 
					                for i in range(NUM_NODES):
 | 
				
			||||||
                rpc = make_rpc_func(i)
 | 
					                    # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync
 | 
				
			||||||
                waitForRPC(rpc)
 | 
					                    rpc = make_rpc_func(i)
 | 
				
			||||||
                if i == 0:
 | 
					                    waitForRPC(rpc)
 | 
				
			||||||
                    rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
 | 
					                    if i == 0:
 | 
				
			||||||
                elif i == 1:
 | 
					                        rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
 | 
				
			||||||
                    rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true'])
 | 
					                    elif i == 1:
 | 
				
			||||||
                    rpc('getnewextaddress', ['lblExtTest'])
 | 
					                        rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true'])
 | 
				
			||||||
                    rpc('rescanblockchain')
 | 
					                        rpc('getnewextaddress', ['lblExtTest'])
 | 
				
			||||||
                else:
 | 
					                        rpc('rescanblockchain')
 | 
				
			||||||
                    rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
 | 
					                    else:
 | 
				
			||||||
                # Lower output split threshold for more stakeable outputs
 | 
					                        rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
 | 
				
			||||||
                rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
					                    # Lower output split threshold for more stakeable outputs
 | 
				
			||||||
 | 
					                    rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for i in range(NUM_BTC_NODES):
 | 
					            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)
 | 
					                if not cls.restore_instance:
 | 
				
			||||||
                if os.path.exists(os.path.join(cfg.BITCOIN_BINDIR, 'bitcoin-wallet')):
 | 
					                    data_dir = prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
                    callrpc_cli(cfg.BITCOIN_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'bitcoin-wallet')
 | 
					                    if os.path.exists(os.path.join(cfg.BITCOIN_BINDIR, 'bitcoin-wallet')):
 | 
				
			||||||
 | 
					                        callrpc_cli(cfg.BITCOIN_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'bitcoin-wallet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                cls.btc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'btc_' + str(i)), cfg.BITCOIN_BINDIR, cfg.BITCOIND))
 | 
					                cls.btc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'btc_' + str(i)), cfg.BITCOIN_BINDIR, cfg.BITCOIND))
 | 
				
			||||||
                logging.info('Started %s %d', cfg.BITCOIND, cls.part_daemons[-1].pid)
 | 
					                logging.info('Started %s %d', cfg.BITCOIND, cls.part_daemons[-1].pid)
 | 
				
			||||||
@ -414,29 +400,20 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if cls.start_ltc_nodes:
 | 
					            if cls.start_ltc_nodes:
 | 
				
			||||||
                for i in range(NUM_LTC_NODES):
 | 
					                for i in range(NUM_LTC_NODES):
 | 
				
			||||||
                    data_dir = prepareDataDir(TEST_DIR, i, 'litecoin.conf', 'ltc_', base_p2p_port=LTC_BASE_PORT, base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					                    if not cls.restore_instance:
 | 
				
			||||||
                    if os.path.exists(os.path.join(cfg.LITECOIN_BINDIR, 'litecoin-wallet')):
 | 
					                        data_dir = prepareDataDir(TEST_DIR, i, 'litecoin.conf', 'ltc_', base_p2p_port=LTC_BASE_PORT, base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
                        callrpc_cli(cfg.LITECOIN_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'litecoin-wallet')
 | 
					                        if os.path.exists(os.path.join(cfg.LITECOIN_BINDIR, 'litecoin-wallet')):
 | 
				
			||||||
 | 
					                            callrpc_cli(cfg.LITECOIN_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'litecoin-wallet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    cls.ltc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'ltc_' + str(i)), cfg.LITECOIN_BINDIR, cfg.LITECOIND))
 | 
					                    cls.ltc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'ltc_' + str(i)), cfg.LITECOIN_BINDIR, cfg.LITECOIND))
 | 
				
			||||||
                    logging.info('Started %s %d', cfg.LITECOIND, cls.part_daemons[-1].pid)
 | 
					                    logging.info('Started %s %d', cfg.LITECOIND, cls.part_daemons[-1].pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    waitForRPC(make_rpc_func(i, base_rpc_port=LTC_BASE_RPC_PORT))
 | 
					                    waitForRPC(make_rpc_func(i, base_rpc_port=LTC_BASE_RPC_PORT))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if cls.start_pivx_nodes:
 | 
					 | 
				
			||||||
                for i in range(NUM_PIVX_NODES):
 | 
					 | 
				
			||||||
                    data_dir = prepareDataDir(TEST_DIR, i, 'pivx.conf', 'pivx_', base_p2p_port=PIVX_BASE_PORT, base_rpc_port=PIVX_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
                    if os.path.exists(os.path.join(cfg.PIVX_BINDIR, 'pivx-wallet')):
 | 
					 | 
				
			||||||
                        callrpc_cli(cfg.PIVX_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'pivx-wallet')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    cls.pivx_daemons.append(startDaemon(os.path.join(TEST_DIR, 'pivx_' + str(i)), cfg.PIVX_BINDIR, cfg.PIVXD))
 | 
					 | 
				
			||||||
                    logging.info('Started %s %d', cfg.PIVXD, cls.part_daemons[-1].pid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    waitForRPC(make_rpc_func(i, base_rpc_port=PIVX_BASE_RPC_PORT))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if cls.start_xmr_nodes:
 | 
					            if cls.start_xmr_nodes:
 | 
				
			||||||
                for i in range(NUM_XMR_NODES):
 | 
					                for i in range(NUM_XMR_NODES):
 | 
				
			||||||
                    prepareXmrDataDir(TEST_DIR, i, 'monerod.conf')
 | 
					                    if not cls.restore_instance:
 | 
				
			||||||
 | 
					                        prepareXmrDataDir(TEST_DIR, i, 'monerod.conf')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    cls.xmr_daemons.append(startXmrDaemon(os.path.join(TEST_DIR, 'xmr_' + str(i)), cfg.XMR_BINDIR, cfg.XMRD))
 | 
					                    cls.xmr_daemons.append(startXmrDaemon(os.path.join(TEST_DIR, 'xmr_' + str(i)), cfg.XMR_BINDIR, cfg.XMRD))
 | 
				
			||||||
                    logging.info('Started %s %d', cfg.XMRD, cls.xmr_daemons[-1].pid)
 | 
					                    logging.info('Started %s %d', cfg.XMRD, cls.xmr_daemons[-1].pid)
 | 
				
			||||||
@ -450,14 +427,20 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    waitForXMRWallet(i, cls.xmr_wallet_auth[i])
 | 
					                    waitForXMRWallet(i, cls.xmr_wallet_auth[i])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    cls.callxmrnodewallet(cls, i, 'create_wallet', {'filename': 'testwallet', 'language': 'English'})
 | 
					                    if not cls.restore_instance:
 | 
				
			||||||
 | 
					                        cls.callxmrnodewallet(cls, i, 'create_wallet', {'filename': 'testwallet', 'language': 'English'})
 | 
				
			||||||
                    cls.callxmrnodewallet(cls, i, 'open_wallet', {'filename': 'testwallet'})
 | 
					                    cls.callxmrnodewallet(cls, i, 'open_wallet', {'filename': 'testwallet'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for i in range(NUM_NODES):
 | 
				
			||||||
 | 
					                # Hook for descendant classes
 | 
				
			||||||
 | 
					                cls.prepareExtraDataDir(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            logging.info('Preparing swap clients.')
 | 
					            logging.info('Preparing swap clients.')
 | 
				
			||||||
            eckey = ECKey()
 | 
					            if not cls.restore_instance:
 | 
				
			||||||
            eckey.generate()
 | 
					                eckey = ECKey()
 | 
				
			||||||
            cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
 | 
					                eckey.generate()
 | 
				
			||||||
            cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()
 | 
					                cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
 | 
				
			||||||
 | 
					                cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for i in range(NUM_NODES):
 | 
					            for i in range(NUM_NODES):
 | 
				
			||||||
                start_nodes = set()
 | 
					                start_nodes = set()
 | 
				
			||||||
@ -465,13 +448,15 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
                    start_nodes.add(Coins.LTC)
 | 
					                    start_nodes.add(Coins.LTC)
 | 
				
			||||||
                if cls.start_xmr_nodes:
 | 
					                if cls.start_xmr_nodes:
 | 
				
			||||||
                    start_nodes.add(Coins.XMR)
 | 
					                    start_nodes.add(Coins.XMR)
 | 
				
			||||||
                if cls.start_pivx_nodes:
 | 
					                if not cls.restore_instance:
 | 
				
			||||||
                    start_nodes.add(Coins.PIVX)
 | 
					                    prepare_swapclient_dir(TEST_DIR, i, cls.network_key, cls.network_pubkey, start_nodes, cls)
 | 
				
			||||||
                prepare_swapclient_dir(TEST_DIR, i, cls.network_key, cls.network_pubkey, start_nodes)
 | 
					 | 
				
			||||||
                basicswap_dir = os.path.join(os.path.join(TEST_DIR, 'basicswap_' + str(i)))
 | 
					                basicswap_dir = os.path.join(os.path.join(TEST_DIR, 'basicswap_' + str(i)))
 | 
				
			||||||
                settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
 | 
					                settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
 | 
				
			||||||
                with open(settings_path) as fs:
 | 
					                with open(settings_path) as fs:
 | 
				
			||||||
                    settings = json.load(fs)
 | 
					                    settings = json.load(fs)
 | 
				
			||||||
 | 
					                    if cls.restore_instance and i == 1:
 | 
				
			||||||
 | 
					                        cls.network_key = settings['network_key']
 | 
				
			||||||
 | 
					                        cls.network_pubkey = settings['network_pubkey']
 | 
				
			||||||
                fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
 | 
					                fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
 | 
				
			||||||
                sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
 | 
					                sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
 | 
				
			||||||
                sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].pid)
 | 
					                sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].pid)
 | 
				
			||||||
@ -479,6 +464,7 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if cls.start_ltc_nodes:
 | 
					                if cls.start_ltc_nodes:
 | 
				
			||||||
                    sc.setDaemonPID(Coins.LTC, cls.ltc_daemons[i].pid)
 | 
					                    sc.setDaemonPID(Coins.LTC, cls.ltc_daemons[i].pid)
 | 
				
			||||||
 | 
					                cls.addPIDInfo(sc, i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                sc.start()
 | 
					                sc.start()
 | 
				
			||||||
                if cls.start_xmr_nodes:
 | 
					                if cls.start_xmr_nodes:
 | 
				
			||||||
@ -490,74 +476,75 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
                t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
 | 
					                t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
 | 
				
			||||||
                cls.http_threads.append(t)
 | 
					                cls.http_threads.append(t)
 | 
				
			||||||
                t.start()
 | 
					                t.start()
 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Set future block rewards to nowhere (a random address), so wallet amounts stay constant
 | 
					            # Set future block rewards to nowhere (a random address), so wallet amounts stay constant
 | 
				
			||||||
            eckey = ECKey()
 | 
					            void_block_rewards_pubkey = cls.getRandomPubkey()
 | 
				
			||||||
            eckey.generate()
 | 
					            if cls.restore_instance:
 | 
				
			||||||
            void_block_rewards_pubkey = eckey.get_pubkey().get_bytes()
 | 
					                cls.btc_addr = cls.swap_clients[0].ci(Coins.BTC).pubkey_to_segwit_address(void_block_rewards_pubkey)
 | 
				
			||||||
 | 
					                if cls.start_ltc_nodes:
 | 
				
			||||||
 | 
					                    cls.ltc_addr = cls.swap_clients[0].ci(Coins.LTC).pubkey_to_address(void_block_rewards_pubkey)
 | 
				
			||||||
 | 
					                if cls.start_xmr_nodes:
 | 
				
			||||||
 | 
					                    cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                cls.btc_addr = callnoderpc(0, 'getnewaddress', ['mining_addr', 'bech32'], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					                num_blocks = 400  # Mine enough to activate segwit
 | 
				
			||||||
 | 
					                logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
				
			||||||
 | 
					                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.btc_addr], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cls.btc_addr = callnoderpc(0, 'getnewaddress', ['mining_addr', 'bech32'], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
					                btc_addr1 = callnoderpc(1, 'getnewaddress', ['initial addr'], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
            num_blocks = 400  # Mine enough to activate segwit
 | 
					                for i in range(5):
 | 
				
			||||||
            logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
					                    callnoderpc(0, 'sendtoaddress', [btc_addr1, 100], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
            callnoderpc(0, 'generatetoaddress', [num_blocks, cls.btc_addr], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Switch addresses so wallet amounts stay constant
 | 
					 | 
				
			||||||
            num_blocks = 100
 | 
					 | 
				
			||||||
            cls.btc_addr = cls.swap_clients[0].ci(Coins.BTC).pubkey_to_segwit_address(void_block_rewards_pubkey)
 | 
					 | 
				
			||||||
            logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
					 | 
				
			||||||
            callnoderpc(0, 'generatetoaddress', [num_blocks, cls.btc_addr], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            checkForks(callnoderpc(0, 'getblockchaininfo', base_rpc_port=BTC_BASE_RPC_PORT))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if cls.start_ltc_nodes:
 | 
					 | 
				
			||||||
                num_blocks = 400
 | 
					 | 
				
			||||||
                cls.ltc_addr = callnoderpc(0, 'getnewaddress', ['mining_addr', 'bech32'], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
                logging.info('Mining %d Litecoin blocks to %s', num_blocks, cls.ltc_addr)
 | 
					 | 
				
			||||||
                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.ltc_addr], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                num_blocks = 31
 | 
					 | 
				
			||||||
                cls.ltc_addr = cls.swap_clients[0].ci(Coins.LTC).pubkey_to_address(void_block_rewards_pubkey)
 | 
					 | 
				
			||||||
                logging.info('Mining %d Litecoin blocks to %s', num_blocks, cls.ltc_addr)
 | 
					 | 
				
			||||||
                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.ltc_addr], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # https://github.com/litecoin-project/litecoin/issues/807
 | 
					 | 
				
			||||||
                # Block 432 is when MWEB activates. It requires a peg-in. You'll need to generate an mweb address and send some coins to it. Then it will allow you to mine the next block.
 | 
					 | 
				
			||||||
                mweb_addr = callnoderpc(2, 'getnewaddress', ['mweb_addr', 'mweb'], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
                callnoderpc(0, 'sendtoaddress', [mweb_addr, 1], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                num_blocks = 69
 | 
					 | 
				
			||||||
                cls.ltc_addr = cls.swap_clients[0].ci(Coins.LTC).pubkey_to_address(void_block_rewards_pubkey)
 | 
					 | 
				
			||||||
                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.ltc_addr], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                checkForks(callnoderpc(0, 'getblockchaininfo', base_rpc_port=LTC_BASE_RPC_PORT))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if cls.start_pivx_nodes:
 | 
					 | 
				
			||||||
                num_blocks = 400
 | 
					 | 
				
			||||||
                cls.pivx_addr = callnoderpc(0, 'getnewaddress', ['mining_addr'], base_rpc_port=PIVX_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
                logging.info('Mining %d PIVX blocks to %s', num_blocks, cls.pivx_addr)
 | 
					 | 
				
			||||||
                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.pivx_addr], base_rpc_port=PIVX_BASE_RPC_PORT)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Switch addresses so wallet amounts stay constant
 | 
					                # Switch addresses so wallet amounts stay constant
 | 
				
			||||||
                num_blocks = 100
 | 
					                num_blocks = 100
 | 
				
			||||||
                cls.pivx_addr = cls.swap_clients[0].ci(Coins.PIVX).pubkey_to_address(void_block_rewards_pubkey)
 | 
					                cls.btc_addr = cls.swap_clients[0].ci(Coins.BTC).pubkey_to_segwit_address(void_block_rewards_pubkey)
 | 
				
			||||||
                logging.info('Mining %d PIVX blocks to %s', num_blocks, cls.pivx_addr)
 | 
					                logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
				
			||||||
                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.pivx_addr], base_rpc_port=PIVX_BASE_RPC_PORT)
 | 
					                callnoderpc(0, 'generatetoaddress', [num_blocks, cls.btc_addr], base_rpc_port=BTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            num_blocks = 100
 | 
					                checkForks(callnoderpc(0, 'getblockchaininfo', base_rpc_port=BTC_BASE_RPC_PORT))
 | 
				
			||||||
            if cls.start_xmr_nodes:
 | 
					 | 
				
			||||||
                cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
 | 
					 | 
				
			||||||
                if callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'] < num_blocks:
 | 
					 | 
				
			||||||
                    logging.info('Mining %d Monero blocks to %s.', num_blocks, cls.xmr_addr)
 | 
					 | 
				
			||||||
                    callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': num_blocks})
 | 
					 | 
				
			||||||
                logging.info('XMR blocks: %d', callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            logging.info('Adding anon outputs')
 | 
					                if cls.start_ltc_nodes:
 | 
				
			||||||
            outputs = []
 | 
					                    num_blocks = 400
 | 
				
			||||||
            for i in range(8):
 | 
					                    cls.ltc_addr = callnoderpc(0, 'getnewaddress', ['mining_addr', 'bech32'], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
                sx_addr = callnoderpc(1, 'getnewstealthaddress')
 | 
					                    logging.info('Mining %d Litecoin blocks to %s', num_blocks, cls.ltc_addr)
 | 
				
			||||||
                outputs.append({'address': sx_addr, 'amount': 0.5})
 | 
					                    callnoderpc(0, 'generatetoaddress', [num_blocks, cls.ltc_addr], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
            for i in range(6):
 | 
					
 | 
				
			||||||
                callnoderpc(0, 'sendtypeto', ['part', 'anon', outputs])
 | 
					                    num_blocks = 31
 | 
				
			||||||
 | 
					                    cls.ltc_addr = cls.swap_clients[0].ci(Coins.LTC).pubkey_to_address(void_block_rewards_pubkey)
 | 
				
			||||||
 | 
					                    logging.info('Mining %d Litecoin blocks to %s', num_blocks, cls.ltc_addr)
 | 
				
			||||||
 | 
					                    callnoderpc(0, 'generatetoaddress', [num_blocks, cls.ltc_addr], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # https://github.com/litecoin-project/litecoin/issues/807
 | 
				
			||||||
 | 
					                    # Block 432 is when MWEB activates. It requires a peg-in. You'll need to generate an mweb address and send some coins to it. Then it will allow you to mine the next block.
 | 
				
			||||||
 | 
					                    mweb_addr = callnoderpc(2, 'getnewaddress', ['mweb_addr', 'mweb'], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					                    callnoderpc(0, 'sendtoaddress', [mweb_addr, 1], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    num_blocks = 69
 | 
				
			||||||
 | 
					                    cls.ltc_addr = cls.swap_clients[0].ci(Coins.LTC).pubkey_to_address(void_block_rewards_pubkey)
 | 
				
			||||||
 | 
					                    callnoderpc(0, 'generatetoaddress', [num_blocks, cls.ltc_addr], base_rpc_port=LTC_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    checkForks(callnoderpc(0, 'getblockchaininfo', base_rpc_port=LTC_BASE_RPC_PORT))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                num_blocks = 100
 | 
				
			||||||
 | 
					                if cls.start_xmr_nodes:
 | 
				
			||||||
 | 
					                    cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
 | 
				
			||||||
 | 
					                    if callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'] < num_blocks:
 | 
				
			||||||
 | 
					                        logging.info('Mining %d Monero blocks to %s.', num_blocks, cls.xmr_addr)
 | 
				
			||||||
 | 
					                        callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': num_blocks})
 | 
				
			||||||
 | 
					                    logging.info('XMR blocks: %d', callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                logging.info('Adding anon outputs')
 | 
				
			||||||
 | 
					                outputs = []
 | 
				
			||||||
 | 
					                for i in range(8):
 | 
				
			||||||
 | 
					                    sx_addr = callnoderpc(1, 'getnewstealthaddress')
 | 
				
			||||||
 | 
					                    outputs.append({'address': sx_addr, 'amount': 0.5})
 | 
				
			||||||
 | 
					                for i in range(6):
 | 
				
			||||||
 | 
					                    callnoderpc(0, 'sendtypeto', ['part', 'anon', outputs])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                part_addr1 = callnoderpc(1, 'getnewaddress', ['initial addr'])
 | 
				
			||||||
 | 
					                part_addr2 = callnoderpc(1, 'getnewaddress', ['initial addr 2'])
 | 
				
			||||||
 | 
					                callnoderpc(0, 'sendtypeto', ['part', 'part', [{'address': part_addr1, 'amount': 100}, {'address': part_addr2, 'amount': 100}]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cls.prepareExtraCoins()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            logging.info('Starting update thread.')
 | 
					            logging.info('Starting update thread.')
 | 
				
			||||||
            signal.signal(signal.SIGINT, signal_handler)
 | 
					            signal.signal(signal.SIGINT, signal_handler)
 | 
				
			||||||
@ -599,13 +586,40 @@ class BaseTest(unittest.TestCase):
 | 
				
			|||||||
        stopDaemons(cls.part_daemons)
 | 
					        stopDaemons(cls.part_daemons)
 | 
				
			||||||
        stopDaemons(cls.btc_daemons)
 | 
					        stopDaemons(cls.btc_daemons)
 | 
				
			||||||
        stopDaemons(cls.ltc_daemons)
 | 
					        stopDaemons(cls.ltc_daemons)
 | 
				
			||||||
        stopDaemons(cls.pivx_daemons)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super(BaseTest, cls).tearDownClass()
 | 
					        super(BaseTest, cls).tearDownClass()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def addCoinSettings(cls, settings, datadir, node_id):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def prepareExtraDataDir(cls, i):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def addPIDInfo(cls, sc, i):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def prepareExtraCoins(cls):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def coins_loop(cls):
 | 
				
			||||||
 | 
					        if cls.btc_addr is not None:
 | 
				
			||||||
 | 
					            btcCli('generatetoaddress 1 {}'.format(cls.btc_addr))
 | 
				
			||||||
 | 
					        if cls.ltc_addr is not None:
 | 
				
			||||||
 | 
					            ltcCli('generatetoaddress 1 {}'.format(cls.ltc_addr))
 | 
				
			||||||
 | 
					        if cls.xmr_addr is not None:
 | 
				
			||||||
 | 
					            callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': 1})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def callxmrnodewallet(self, node_id, method, params=None):
 | 
					    def callxmrnodewallet(self, node_id, method, params=None):
 | 
				
			||||||
        return callrpc_xmr(XMR_BASE_WALLET_RPC_PORT + node_id, self.xmr_wallet_auth[node_id], method, params)
 | 
					        return callrpc_xmr(XMR_BASE_WALLET_RPC_PORT + node_id, self.xmr_wallet_auth[node_id], method, params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getXmrBalance(self, js_wallets):
 | 
				
			||||||
 | 
					        return float(js_wallets[Coins.XMR.name]['unconfirmed']) + float(js_wallets[Coins.XMR.name]['balance'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Test(BaseTest):
 | 
					class Test(BaseTest):
 | 
				
			||||||
    __test__ = True
 | 
					    __test__ = True
 | 
				
			||||||
@ -617,9 +631,9 @@ class Test(BaseTest):
 | 
				
			|||||||
        logging.info('---------- Test PART to XMR')
 | 
					        logging.info('---------- Test PART to XMR')
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        start_xmr_amount = self.getXmrBalance(read_json_api(1800, 'wallets'))
 | 
				
			||||||
        js_1 = read_json_api(1801, 'wallets')
 | 
					        js_1 = read_json_api(1801, 'wallets')
 | 
				
			||||||
        assert (make_int(js_1[Coins.XMR.name]['balance'], scale=12) > 0)
 | 
					        assert (self.getXmrBalance(js_1) > 0.0)
 | 
				
			||||||
        assert (make_int(js_1[Coins.XMR.name]['unconfirmed'], scale=12) > 0)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 100 * COIN, 0.11 * XMR_COIN, 100 * COIN, SwapTypes.XMR_SWAP)
 | 
					        offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 100 * COIN, 0.11 * XMR_COIN, 100 * COIN, SwapTypes.XMR_SWAP)
 | 
				
			||||||
        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
					        wait_for_offer(test_delay_event, swap_clients[1], offer_id)
 | 
				
			||||||
@ -640,8 +654,9 @@ class Test(BaseTest):
 | 
				
			|||||||
        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
 | 
					        wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        js_0_end = read_json_api(1800, 'wallets')
 | 
					        js_0_end = read_json_api(1800, 'wallets')
 | 
				
			||||||
        end_xmr = float(js_0_end['XMR']['balance']) + float(js_0_end['XMR']['unconfirmed'])
 | 
					        end_xmr_amount = self.getXmrBalance(js_0_end)
 | 
				
			||||||
        assert (end_xmr > 10.9 and end_xmr < 11.0)
 | 
					        xmr_amount_diff = end_xmr_amount - start_xmr_amount
 | 
				
			||||||
 | 
					        assert (xmr_amount_diff > 10.9 and xmr_amount_diff < 11.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bid_id_hex = bid_id.hex()
 | 
					        bid_id_hex = bid_id.hex()
 | 
				
			||||||
        path = f'bids/{bid_id_hex}/states'
 | 
					        path = f'bids/{bid_id_hex}/states'
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user