XMR successful swap works.
This commit is contained in:
		
							parent
							
								
									eb6bd444c4
								
							
						
					
					
						commit
						0e2011e085
					
				@ -59,6 +59,7 @@ from .messages_pb2 import (
 | 
			
		||||
    XmrSplitMessage,
 | 
			
		||||
    XmrBidLockTxSigsMessage,
 | 
			
		||||
    XmrBidLockSpendTxMessage,
 | 
			
		||||
    XmrBidSecretMessage,
 | 
			
		||||
)
 | 
			
		||||
from .db import (
 | 
			
		||||
    CURRENT_DB_VERSION,
 | 
			
		||||
@ -80,6 +81,8 @@ from .db import (
 | 
			
		||||
from .explorers import ExplorerInsight, ExplorerBitAps, ExplorerChainz
 | 
			
		||||
import basicswap.config as cfg
 | 
			
		||||
from .base import BaseApp
 | 
			
		||||
from .ecc_util import (
 | 
			
		||||
    b2i, i2b)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MIN_OFFER_VALID_TIME = 60 * 10
 | 
			
		||||
@ -94,11 +97,12 @@ class MessageTypes(IntEnum):
 | 
			
		||||
    BID_ACCEPT = auto()
 | 
			
		||||
 | 
			
		||||
    XMR_OFFER = auto()
 | 
			
		||||
    XMR_BID = auto()
 | 
			
		||||
    XMR_BID_FL = auto()
 | 
			
		||||
    XMR_BID_SPLIT = auto()
 | 
			
		||||
    XMR_BID_ACCEPT = auto()
 | 
			
		||||
    XMR_BID_ACCEPT_LF = auto()
 | 
			
		||||
    XMR_BID_TXN_SIGS_FL = auto()
 | 
			
		||||
    XMR_BID_LOCK_REFUND_SPEND_TX_LF = auto()
 | 
			
		||||
    XMR_BID_LOCK_SPEND_TX_LF = auto()
 | 
			
		||||
    XMR_BID_SECRET_LF = auto()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SwapTypes(IntEnum):
 | 
			
		||||
@ -125,6 +129,11 @@ class BidStates(IntEnum):
 | 
			
		||||
    SWAP_PARTICIPATING = auto()     # Participate txn validated
 | 
			
		||||
    SWAP_COMPLETED = auto()         # All swap txns spent
 | 
			
		||||
    XMR_SWAP_SCRIPT_COIN_LOCKED = auto()
 | 
			
		||||
    XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX = auto()
 | 
			
		||||
    XMR_SWAP_NOSCRIPT_COIN_LOCKED = auto()
 | 
			
		||||
    XMR_SWAP_SECRET_SHARED = auto()
 | 
			
		||||
    XMR_SWAP_SCRIPT_TX_REDEEMED = auto()
 | 
			
		||||
    XMR_SWAP_NOSCRIPT_TX_REDEEMED = auto()
 | 
			
		||||
    SWAP_DELAYING = auto()
 | 
			
		||||
    SWAP_TIMEDOUT = auto()
 | 
			
		||||
    BID_ABANDONED = auto()          # Bid will no longer be processed
 | 
			
		||||
@ -148,14 +157,20 @@ class TxTypes(IntEnum):
 | 
			
		||||
    PTX_REFUND = auto()
 | 
			
		||||
 | 
			
		||||
    XMR_SWAP_A_LOCK = auto()
 | 
			
		||||
    XMR_SWAP_A_LOCK_SPEND = auto()
 | 
			
		||||
    XMR_SWAP_A_LOCK_REFUND = auto()
 | 
			
		||||
    XMR_SWAP_A_LOCK_REFUND_SPEND = auto()
 | 
			
		||||
    XMR_SWAP_B_LOCK = auto()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EventTypes(IntEnum):
 | 
			
		||||
    ACCEPT_BID = auto()
 | 
			
		||||
    SIGN_XMR_SWAP_LOCK_TX_A = auto()
 | 
			
		||||
    SEND_XMR_SWAP_LOCK_TX_A = auto()
 | 
			
		||||
    SEND_XMR_SWAP_LOCK_TX_B = auto()
 | 
			
		||||
    SEND_XMR_SECRET = auto()
 | 
			
		||||
    REDEEM_XMR_SWAP_LOCK_TX_A = auto()  # Follower
 | 
			
		||||
    REDEEM_XMR_SWAP_LOCK_TX_B = auto()  # Leader
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XmrSplitMsgTypes(IntEnum):
 | 
			
		||||
@ -487,7 +502,10 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        elif coin == Coins.NMC:
 | 
			
		||||
            return NMCInterface(self.coin_clients[coin], self.chain)
 | 
			
		||||
        elif coin == Coins.XMR:
 | 
			
		||||
            return XMRInterface(self.coin_clients[coin], self.chain)
 | 
			
		||||
            xmr_i = XMRInterface(self.coin_clients[coin], self.chain)
 | 
			
		||||
            chain_client_settings = self.getChainClientSettings(coin)
 | 
			
		||||
            xmr_i.setWalletFilename(chain_client_settings['walletfile'])
 | 
			
		||||
            return xmr_i
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError('Unknown coin type')
 | 
			
		||||
 | 
			
		||||
@ -1076,6 +1094,10 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            session.add(bid.participate_tx)
 | 
			
		||||
        if bid.xmr_a_lock_tx:
 | 
			
		||||
            session.add(bid.xmr_a_lock_tx)
 | 
			
		||||
        if bid.xmr_b_lock_tx:
 | 
			
		||||
            session.add(bid.xmr_b_lock_tx)
 | 
			
		||||
        if bid.xmr_a_lock_spend_tx:
 | 
			
		||||
            session.add(bid.xmr_a_lock_spend_tx)
 | 
			
		||||
        if xmr_swap is not None:
 | 
			
		||||
            session.add(xmr_swap)
 | 
			
		||||
 | 
			
		||||
@ -1208,6 +1230,20 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            session.remove()
 | 
			
		||||
            self.mxDB.release()
 | 
			
		||||
 | 
			
		||||
    def loadBidTxns(self, bid, session):
 | 
			
		||||
        for stx in session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid.bid_id)):
 | 
			
		||||
            if stx.tx_type == TxTypes.ITX:
 | 
			
		||||
                bid.initiate_tx = stx
 | 
			
		||||
            elif stx.tx_type == TxTypes.PTX:
 | 
			
		||||
                bid.participate_tx = stx
 | 
			
		||||
            elif stx.tx_type == TxTypes.XMR_SWAP_A_LOCK:
 | 
			
		||||
                bid.xmr_a_lock_tx = stx
 | 
			
		||||
            elif stx.tx_type == TxTypes.XMR_SWAP_A_LOCK_SPEND:
 | 
			
		||||
                bid.xmr_a_lock_spend_tx = stx
 | 
			
		||||
            elif stx.tx_type == TxTypes.XMR_SWAP_B_LOCK:
 | 
			
		||||
                bid.xmr_b_lock_tx = stx
 | 
			
		||||
            else:
 | 
			
		||||
                self.log.warning('Unknown transaction type: {}'.format(stx.tx_type))
 | 
			
		||||
    def getXmrBid(self, bid_id, sent=False):
 | 
			
		||||
        self.mxDB.acquire()
 | 
			
		||||
        try:
 | 
			
		||||
@ -1216,6 +1252,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            xmr_swap = None
 | 
			
		||||
            if bid:
 | 
			
		||||
                xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid_id).first()
 | 
			
		||||
                self.loadBidTxns(bid, session)
 | 
			
		||||
            return bid, xmr_swap
 | 
			
		||||
        finally:
 | 
			
		||||
            session.close()
 | 
			
		||||
@ -1242,8 +1279,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            session = scoped_session(self.session_factory)
 | 
			
		||||
            bid = session.query(Bid).filter_by(bid_id=bid_id).first()
 | 
			
		||||
            if bid:
 | 
			
		||||
                bid.initiate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.ITX)).first()
 | 
			
		||||
                bid.participate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.PTX)).first()
 | 
			
		||||
                self.loadBidTxns(bid, session)
 | 
			
		||||
            return bid
 | 
			
		||||
        finally:
 | 
			
		||||
            session.close()
 | 
			
		||||
@ -1399,10 +1435,16 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            kaf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 3)
 | 
			
		||||
            karf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 4)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.vkbvf = kbvf
 | 
			
		||||
            xmr_swap.pkbvf = ci_to.getPubkey(kbvf)
 | 
			
		||||
            xmr_swap.pkbsf = ci_to.getPubkey(kbsf)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.pkaf = ci_from.getPubkey(kaf)
 | 
			
		||||
            xmr_swap.pkarf = ci_from.getPubkey(karf)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.kbsf_dleag = ci_to.proveDLEAG(kbsf)
 | 
			
		||||
            xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33]
 | 
			
		||||
            assert(xmr_swap.pkasf == ci_from.getPubkey(kbsf))
 | 
			
		||||
 | 
			
		||||
            msg_buf.pkaf = xmr_swap.pkaf
 | 
			
		||||
            msg_buf.pkarf = xmr_swap.pkarf
 | 
			
		||||
@ -1410,7 +1452,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            msg_buf.kbsf_dleag = xmr_swap.kbsf_dleag[:16000]
 | 
			
		||||
 | 
			
		||||
            bid_bytes = msg_buf.SerializeToString()
 | 
			
		||||
            payload_hex = str.format('{:02x}', MessageTypes.XMR_BID) + bid_bytes.hex()
 | 
			
		||||
            payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_FL) + bid_bytes.hex()
 | 
			
		||||
 | 
			
		||||
            if addr_send_from is None:
 | 
			
		||||
                bid_addr = self.callrpc('getnewaddress')
 | 
			
		||||
@ -1469,7 +1511,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            session.close()
 | 
			
		||||
            session.remove()
 | 
			
		||||
 | 
			
		||||
            self.log.info('Sent XMR_BID %s', xmr_swap.bid_id.hex())
 | 
			
		||||
            self.log.info('Sent XMR_BID_FL %s', xmr_swap.bid_id.hex())
 | 
			
		||||
            return xmr_swap.bid_id
 | 
			
		||||
        finally:
 | 
			
		||||
            self.mxDB.release()
 | 
			
		||||
@ -1496,7 +1538,6 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            ci_from = self.ci(coin_from)
 | 
			
		||||
            ci_to = self.ci(coin_to)
 | 
			
		||||
 | 
			
		||||
            self.log.debug('[rm] acceptXmrBid bid.created_at %d', bid.created_at)
 | 
			
		||||
            if xmr_swap.contract_count is None:
 | 
			
		||||
                xmr_swap.contract_count = self.getNewContractId()
 | 
			
		||||
 | 
			
		||||
@ -1508,6 +1549,14 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4)
 | 
			
		||||
            karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.vkbvl = kbvl
 | 
			
		||||
            xmr_swap.pkbvl = ci_to.getPubkey(kbvl)
 | 
			
		||||
            xmr_swap.pkbsl = ci_to.getPubkey(kbsl)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.vkbv = ci_to.sumKeys(kbvl, xmr_swap.vkbvf)
 | 
			
		||||
            xmr_swap.pkbv = ci_to.sumPubkeys(xmr_swap.pkbvl, xmr_swap.pkbvf)
 | 
			
		||||
            xmr_swap.pkbs = ci_to.sumPubkeys(xmr_swap.pkbsl, xmr_swap.pkbsf)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.sh = hashlib.sha256(contract_secret).digest()
 | 
			
		||||
            xmr_swap.pkal = ci_from.getPubkey(kal)
 | 
			
		||||
            xmr_swap.pkarl = ci_from.getPubkey(karl)
 | 
			
		||||
@ -1525,7 +1574,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            xmr_swap.a_lock_tx = ci_from.fundTx(xmr_swap.a_lock_tx, xmr_offer.a_fee_rate)
 | 
			
		||||
 | 
			
		||||
            xmr_swap.a_lock_tx_id = ci_from.getTxHash(xmr_swap.a_lock_tx)
 | 
			
		||||
            xmr_swap.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_tx, xmr_swap.a_lock_tx_script,
 | 
			
		||||
@ -1563,8 +1612,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            msg_buf.al_lock_refund_tx_sig = xmr_swap.al_lock_refund_tx_sig
 | 
			
		||||
 | 
			
		||||
            msg_bytes = msg_buf.SerializeToString()
 | 
			
		||||
            self.log.debug('[rm] acceptXmrBid len(msg_bytes) %d', len(msg_bytes))
 | 
			
		||||
            payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_ACCEPT) + msg_bytes.hex()
 | 
			
		||||
            payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_ACCEPT_LF) + msg_bytes.hex()
 | 
			
		||||
            options = {'decodehex': True, 'ttl_is_seconds': True}
 | 
			
		||||
            msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
 | 
			
		||||
            ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
 | 
			
		||||
@ -1604,7 +1652,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
 | 
			
		||||
            #self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
            # Add to swaps_in_progress only when waiting on txns
 | 
			
		||||
            self.log.info('Sent XMR_BID_ACCEPT %s', bid_id.hex())
 | 
			
		||||
            self.log.info('Sent XMR_BID_ACCEPT_LF %s', bid_id.hex())
 | 
			
		||||
            return bid_id
 | 
			
		||||
        finally:
 | 
			
		||||
            self.mxDB.release()
 | 
			
		||||
@ -2138,7 +2186,155 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def checkXmrBidState(self, bid_id, bid, offer):
 | 
			
		||||
        pass
 | 
			
		||||
        rv = False
 | 
			
		||||
        state = BidStates(bid.state)
 | 
			
		||||
        if state == BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX:
 | 
			
		||||
            if bid.xmr_a_lock_tx is None:
 | 
			
		||||
                return
 | 
			
		||||
            ci_from = self.ci(Coins(offer.coin_from))
 | 
			
		||||
            ci_to = self.ci(Coins(offer.coin_to))
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                self.mxDB.acquire()
 | 
			
		||||
                session = scoped_session(self.session_factory)
 | 
			
		||||
                xmr_offer = session.query(XmrOffer).filter_by(offer_id=offer.offer_id).first()
 | 
			
		||||
                assert(xmr_offer), 'XMR offer not found: {}.'.format(offer.offer_id.hex())
 | 
			
		||||
                xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid.bid_id).first()
 | 
			
		||||
                assert(xmr_swap), 'XMR swap not found: {}.'.format(bid.bid_id.hex())
 | 
			
		||||
 | 
			
		||||
                # TODO: Timeout waiting for transactions
 | 
			
		||||
 | 
			
		||||
                a_lock_tx_dest = ci_from.getScriptDest(xmr_swap.a_lock_tx_script)
 | 
			
		||||
                utxos = ci_from.getOutput(bid.xmr_a_lock_tx.txid, a_lock_tx_dest, bid.amount)
 | 
			
		||||
 | 
			
		||||
                if len(utxos) < 1:
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
                if len(utxos) > 1:
 | 
			
		||||
                    raise ValueError('Too many outputs for chain A lock tx')
 | 
			
		||||
 | 
			
		||||
                utxo = utxos[0]
 | 
			
		||||
                if utxo['depth'] >= ci_from.blocks_confirmed:
 | 
			
		||||
                    bid.xmr_a_lock_tx.setState(TxStates.TX_CONFIRMED)
 | 
			
		||||
                    bid.setState(BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED)
 | 
			
		||||
                    self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
 | 
			
		||||
                    if bid.was_sent:
 | 
			
		||||
                        delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept)
 | 
			
		||||
                        self.log.info('Sending xmr swap chain B lock tx for bid %s in %d seconds', bid_id.hex(), delay)
 | 
			
		||||
                        self.createEventInSession(delay, EventTypes.SEND_XMR_SWAP_LOCK_TX_B, bid_id, session)
 | 
			
		||||
                        #bid.setState(BidStates.SWAP_DELAYING)
 | 
			
		||||
 | 
			
		||||
                    session.commit()
 | 
			
		||||
 | 
			
		||||
                session.close()
 | 
			
		||||
                session.remove()
 | 
			
		||||
            except Exception as ex:
 | 
			
		||||
                session.close()
 | 
			
		||||
                session.remove()
 | 
			
		||||
                raise ex
 | 
			
		||||
            finally:
 | 
			
		||||
                self.mxDB.release()
 | 
			
		||||
        elif state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED:
 | 
			
		||||
            if bid.was_sent and bid.xmr_b_lock_tx is None:
 | 
			
		||||
                return
 | 
			
		||||
            ci_from = self.ci(Coins(offer.coin_from))
 | 
			
		||||
            ci_to = self.ci(Coins(offer.coin_to))
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                self.mxDB.acquire()
 | 
			
		||||
                session = scoped_session(self.session_factory)
 | 
			
		||||
                xmr_offer = session.query(XmrOffer).filter_by(offer_id=offer.offer_id).first()
 | 
			
		||||
                assert(xmr_offer), 'XMR offer not found: {}.'.format(offer.offer_id.hex())
 | 
			
		||||
                xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid.bid_id).first()
 | 
			
		||||
                assert(xmr_swap), 'XMR swap not found: {}.'.format(bid.bid_id.hex())
 | 
			
		||||
 | 
			
		||||
                rv = ci_to.findTxB(xmr_swap.vkbv, xmr_swap.pkbs, bid.amount_to, ci_to.blocks_confirmed, xmr_swap.b_restore_height)
 | 
			
		||||
 | 
			
		||||
                if rv is not None:
 | 
			
		||||
 | 
			
		||||
                    if bid.xmr_b_lock_tx is None:
 | 
			
		||||
                        b_lock_tx_id = bytes.fromhex(rv['txid'])
 | 
			
		||||
                        bid.xmr_b_lock_tx = SwapTx(
 | 
			
		||||
                            bid_id=bid_id,
 | 
			
		||||
                            tx_type=TxTypes.XMR_SWAP_B_LOCK,
 | 
			
		||||
                            txid=b_lock_tx_id,
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                    bid.xmr_b_lock_tx.setState(TxStates.TX_CONFIRMED)
 | 
			
		||||
                    bid.setState(BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED)
 | 
			
		||||
                    self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
 | 
			
		||||
                    if bid.was_received:
 | 
			
		||||
                        delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept)
 | 
			
		||||
                        self.log.info('Releasing xmr swap secret for bid %s in %d seconds', bid_id.hex(), delay)
 | 
			
		||||
                        self.createEventInSession(delay, EventTypes.SEND_XMR_SECRET, bid_id, session)
 | 
			
		||||
 | 
			
		||||
                    session.commit()
 | 
			
		||||
 | 
			
		||||
                session.close()
 | 
			
		||||
                session.remove()
 | 
			
		||||
            except Exception as ex:
 | 
			
		||||
                session.close()
 | 
			
		||||
                session.remove()
 | 
			
		||||
                raise ex
 | 
			
		||||
            finally:
 | 
			
		||||
                self.mxDB.release()
 | 
			
		||||
            # Waiting for initiate txn to be confirmed in 'from' chain
 | 
			
		||||
            #initiate_txnid_hex = bid.initiate_tx.txid.hex()
 | 
			
		||||
            #p2sh = self.getScriptAddress(coin_from, bid.initiate_tx.script)
 | 
			
		||||
        elif state == BidStates.XMR_SWAP_SECRET_SHARED:
 | 
			
		||||
            # Wait for script spend tx to confirm
 | 
			
		||||
            ci_from = self.ci(Coins(offer.coin_from))
 | 
			
		||||
            ci_to = self.ci(Coins(offer.coin_to))
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                self.mxDB.acquire()
 | 
			
		||||
                session = scoped_session(self.session_factory)
 | 
			
		||||
                xmr_offer = session.query(XmrOffer).filter_by(offer_id=offer.offer_id).first()
 | 
			
		||||
                assert(xmr_offer), 'XMR offer not found: {}.'.format(offer.offer_id.hex())
 | 
			
		||||
                xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid.bid_id).first()
 | 
			
		||||
                assert(xmr_swap), 'XMR swap not found: {}.'.format(bid.bid_id.hex())
 | 
			
		||||
 | 
			
		||||
                # TODO: Use explorer to get tx / block hash for getrawtransaction
 | 
			
		||||
                rv = ci_from.getTransaction(xmr_swap.a_lock_spend_tx_id)
 | 
			
		||||
                if rv is not None:
 | 
			
		||||
                    xmr_swap.a_lock_spend_tx = rv
 | 
			
		||||
 | 
			
		||||
                    #bid.xmr_a_lock_spend_tx.setState(TxStates.TX_CONFIRMED)
 | 
			
		||||
                    bid.setState(BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED)  # TODO: Wait for confirmation?
 | 
			
		||||
 | 
			
		||||
                    if not bid.was_received:
 | 
			
		||||
                        rv = True  # Remove from return False
 | 
			
		||||
                        bid.setState(BidStates.SWAP_COMPLETED)
 | 
			
		||||
                    self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
                    if bid.was_received:
 | 
			
		||||
                        delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept)
 | 
			
		||||
                        self.log.info('Redeeming coin b lock tx for bid %s in %d seconds', bid_id.hex(), delay)
 | 
			
		||||
                        self.createEventInSession(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session)
 | 
			
		||||
 | 
			
		||||
                    session.commit()
 | 
			
		||||
 | 
			
		||||
                session.close()
 | 
			
		||||
                session.remove()
 | 
			
		||||
            except Exception as ex:
 | 
			
		||||
                session.close()
 | 
			
		||||
                session.remove()
 | 
			
		||||
                raise ex
 | 
			
		||||
            finally:
 | 
			
		||||
                self.mxDB.release()
 | 
			
		||||
        elif state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED:
 | 
			
		||||
            ci_to = self.ci(Coins(offer.coin_to))
 | 
			
		||||
            txid_hex = bid.xmr_b_lock_tx.spend_txid.hex()
 | 
			
		||||
 | 
			
		||||
            rv = ci_to.findTxnByHash(txid_hex)
 | 
			
		||||
            if rv is not None:
 | 
			
		||||
                rv = True  # Remove from return False
 | 
			
		||||
                bid.setState(BidStates.SWAP_COMPLETED)
 | 
			
		||||
                self.saveBid(bid_id, bid)
 | 
			
		||||
 | 
			
		||||
        return rv
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def checkBidState(self, bid_id, bid, offer):
 | 
			
		||||
        # assert(self.mxDB.locked())
 | 
			
		||||
@ -2464,6 +2660,14 @@ class BasicSwap(BaseApp):
 | 
			
		||||
                        self.sendXmrBidTxnSigsFtoL(row.linked_id, session)
 | 
			
		||||
                    elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_A:
 | 
			
		||||
                        self.sendXmrBidCoinALockTx(row.linked_id, session)
 | 
			
		||||
                    elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_B:
 | 
			
		||||
                        self.sendXmrBidCoinBLockTx(row.linked_id, session)
 | 
			
		||||
                    elif row.event_type == EventTypes.SEND_XMR_SECRET:
 | 
			
		||||
                        self.sendXmrBidSecret(row.linked_id, session)
 | 
			
		||||
                    elif row.event_type == EventTypes.REDEEM_XMR_SWAP_LOCK_TX_A:
 | 
			
		||||
                        self.redeemXmrBidCoinALockTx(row.linked_id, session)
 | 
			
		||||
                    elif row.event_type == EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B:
 | 
			
		||||
                        self.redeemXmrBidCoinBLockTx(row.linked_id, session)
 | 
			
		||||
                    else:
 | 
			
		||||
                        self.log.warning('Unknown event type: %d', row.event_type)
 | 
			
		||||
                except Exception as ex:
 | 
			
		||||
@ -2808,6 +3012,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        bid.setState(BidStates.BID_RECEIVED)
 | 
			
		||||
        self.saveBidInSession(bid.bid_id, bid, session, xmr_swap)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def receiveXmrBidAccept(self, bid, session):
 | 
			
		||||
        # Follower receiving MSG1F and MSG2F
 | 
			
		||||
        self.log.debug('Receiving xmr bid accept %s', bid.bid_id.hex())
 | 
			
		||||
@ -2843,6 +3048,10 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        if not ci_to.verifyPubkey(xmr_swap.pkbsl):
 | 
			
		||||
            raise ValueError('Invalid coin b pubkey.')
 | 
			
		||||
 | 
			
		||||
        xmr_swap.vkbv = ci_to.sumKeys(xmr_swap.vkbvl, xmr_swap.vkbvf)
 | 
			
		||||
        xmr_swap.pkbv = ci_to.sumPubkeys(xmr_swap.pkbvl, xmr_swap.pkbvf)
 | 
			
		||||
        xmr_swap.pkbs = ci_to.sumPubkeys(xmr_swap.pkbsl, xmr_swap.pkbsf)
 | 
			
		||||
 | 
			
		||||
        if not ci_from.verifyPubkey(xmr_swap.pkal):
 | 
			
		||||
            raise ValueError('Invalid pubkey.')
 | 
			
		||||
        if not ci_from.verifyPubkey(xmr_swap.pkarl):
 | 
			
		||||
@ -2857,6 +3066,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        self.createEventInSession(delay, EventTypes.SIGN_XMR_SWAP_LOCK_TX_A, bid.bid_id, session)
 | 
			
		||||
 | 
			
		||||
    def processXmrBid(self, msg):
 | 
			
		||||
        # MSG1L
 | 
			
		||||
        self.log.debug('Processing xmr bid msg %s', msg['msgid'])
 | 
			
		||||
        now = int(time.time())
 | 
			
		||||
        bid_bytes = bytes.fromhex(msg['hex'][2:-2])
 | 
			
		||||
@ -2873,6 +3083,8 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(offer_id.hex())
 | 
			
		||||
        coin_from = Coins(offer.coin_from)
 | 
			
		||||
        coin_to = Coins(offer.coin_to)
 | 
			
		||||
        ci_from = self.ci(coin_from)
 | 
			
		||||
        ci_to = self.ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        logging.debug('TODO: xmr bid validation')
 | 
			
		||||
 | 
			
		||||
@ -2899,6 +3111,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
                pkaf=bid_data.pkaf,
 | 
			
		||||
                pkarf=bid_data.pkarf,
 | 
			
		||||
                vkbvf=bid_data.kbvf,
 | 
			
		||||
                pkbvf=ci_to.getPubkey(bid_data.kbvf),
 | 
			
		||||
                kbsf_dleag=bid_data.kbsf_dleag,
 | 
			
		||||
                b_restore_height=self.ci(coin_to).getChainHeight(),
 | 
			
		||||
            )
 | 
			
		||||
@ -2942,6 +3155,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            xmr_swap.pkal = msg_data.pkal
 | 
			
		||||
            xmr_swap.pkarl = msg_data.pkarl
 | 
			
		||||
            xmr_swap.vkbvl = msg_data.kbvl
 | 
			
		||||
            xmr_swap.pkbvl = ci_to.getPubkey(msg_data.kbvl)
 | 
			
		||||
            xmr_swap.kbsl_dleag = msg_data.kbsl_dleag
 | 
			
		||||
 | 
			
		||||
            xmr_swap.a_lock_tx = msg_data.a_lock_tx
 | 
			
		||||
@ -2961,7 +3175,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
                xmr_swap.pkarl, xmr_swap.pkarf,
 | 
			
		||||
                check_a_lock_tx_inputs
 | 
			
		||||
            )
 | 
			
		||||
            xmr_swap.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)
 | 
			
		||||
 | 
			
		||||
            lock_refund_tx_id, xmr_swap.a_swap_refund_value = ci_from.verifyLockRefundTx(
 | 
			
		||||
                xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
 | 
			
		||||
@ -3008,7 +3222,6 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        try:
 | 
			
		||||
            karf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4)
 | 
			
		||||
 | 
			
		||||
            print('[rm] xmr_swap.a_swap_refund_value', xmr_swap.a_swap_refund_value)
 | 
			
		||||
            xmr_swap.af_lock_refund_spend_tx_esig = ci_from.signTxOtVES(karf, xmr_swap.pkasl, xmr_swap.a_lock_refund_spend_tx, 0, xmr_swap.a_lock_refund_tx_script, xmr_swap.a_swap_refund_value)
 | 
			
		||||
            xmr_swap.af_lock_refund_tx_sig = ci_from.signTx(karf, xmr_swap.a_lock_refund_tx, 0, xmr_swap.a_lock_tx_script, bid.amount)
 | 
			
		||||
 | 
			
		||||
@ -3018,8 +3231,8 @@ class BasicSwap(BaseApp):
 | 
			
		||||
                af_lock_refund_tx_sig=xmr_swap.af_lock_refund_tx_sig
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            mag_bytes = msg_buf.SerializeToString()
 | 
			
		||||
            payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_TXN_SIGS_FL) + mag_bytes.hex()
 | 
			
		||||
            msg_bytes = msg_buf.SerializeToString()
 | 
			
		||||
            payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_TXN_SIGS_FL) + msg_bytes.hex()
 | 
			
		||||
 | 
			
		||||
            options = {'decodehex': True, 'ttl_is_seconds': True}
 | 
			
		||||
            # TODO: set msg_valid based on bid / offer parameters
 | 
			
		||||
@ -3029,7 +3242,16 @@ class BasicSwap(BaseApp):
 | 
			
		||||
 | 
			
		||||
            self.log.info('Sent XMR_BID_TXN_SIGS_FL %s', xmr_swap.coin_a_lock_tx_sigs_l_msg_id.hex())
 | 
			
		||||
 | 
			
		||||
            bid.setState(BidStates.BID_ACCEPTED)
 | 
			
		||||
            a_lock_tx_id = ci_from.getTxHash(xmr_swap.a_lock_tx)
 | 
			
		||||
            self.log.debug('Waiting for lock txn %s to %s chain for bid %s', a_lock_tx_id.hex(), chainparams[coin_from]['name'], bid_id.hex())
 | 
			
		||||
            bid.xmr_a_lock_tx = SwapTx(
 | 
			
		||||
                bid_id=bid_id,
 | 
			
		||||
                tx_type=TxTypes.XMR_SWAP_A_LOCK,
 | 
			
		||||
                txid=a_lock_tx_id,
 | 
			
		||||
            )
 | 
			
		||||
            bid.xmr_a_lock_tx.setState(TxStates.TX_NONE)
 | 
			
		||||
 | 
			
		||||
            bid.setState(BidStates.BID_ACCEPTED)  # XMR
 | 
			
		||||
            self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
            self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
@ -3059,6 +3281,7 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            xmr_swap.dest_af,
 | 
			
		||||
            xmr_offer.a_fee_rate)
 | 
			
		||||
 | 
			
		||||
        xmr_swap.a_lock_spend_tx_id = ci_from.getTxHash(xmr_swap.a_lock_spend_tx)
 | 
			
		||||
        xmr_swap.al_lock_spend_tx_esig = ci_from.signTxOtVES(kal, xmr_swap.pkasf, xmr_swap.a_lock_spend_tx, 0, xmr_swap.a_lock_tx_script, bid.amount)  # self.a_swap_value
 | 
			
		||||
 | 
			
		||||
        msg_buf = XmrBidLockSpendTxMessage(
 | 
			
		||||
@ -3066,8 +3289,8 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            a_lock_spend_tx=xmr_swap.a_lock_spend_tx,
 | 
			
		||||
            al_lock_spend_tx_esig=xmr_swap.al_lock_spend_tx_esig)
 | 
			
		||||
 | 
			
		||||
        mag_bytes = msg_buf.SerializeToString()
 | 
			
		||||
        payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_REFUND_SPEND_TX_LF) + mag_bytes.hex()
 | 
			
		||||
        msg_bytes = msg_buf.SerializeToString()
 | 
			
		||||
        payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_SPEND_TX_LF) + msg_bytes.hex()
 | 
			
		||||
 | 
			
		||||
        options = {'decodehex': True, 'ttl_is_seconds': True}
 | 
			
		||||
        # TODO: set msg_valid based on bid / offer parameters
 | 
			
		||||
@ -3089,10 +3312,164 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        )
 | 
			
		||||
        bid.xmr_a_lock_tx.setState(TxStates.TX_SENT)
 | 
			
		||||
 | 
			
		||||
        bid.setState(BidStates.BID_ACCEPTED)
 | 
			
		||||
        bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX)
 | 
			
		||||
        self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def sendXmrBidCoinBLockTx(self, bid_id, session):
 | 
			
		||||
        # Follower sending coin B lock tx
 | 
			
		||||
        self.log.debug('Sending coin B lock tx for xmr bid %s', bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = self.getXmrBid(bid_id)
 | 
			
		||||
        assert(bid), 'Bid not found: {}.'.format(bid_id.hex())
 | 
			
		||||
        assert(xmr_swap), 'XMR swap not found: {}.'.format(bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=False)
 | 
			
		||||
        assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        coin_from = Coins(offer.coin_from)
 | 
			
		||||
        coin_to = Coins(offer.coin_to)
 | 
			
		||||
        ci_from = self.ci(coin_from)
 | 
			
		||||
        ci_to = self.ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate)
 | 
			
		||||
 | 
			
		||||
        self.log.debug('Submitted lock txn %s to %s chain for bid %s', b_lock_tx_id.hex(), chainparams[coin_to]['name'], bid_id.hex())
 | 
			
		||||
        bid.xmr_b_lock_tx = SwapTx(
 | 
			
		||||
            bid_id=bid_id,
 | 
			
		||||
            tx_type=TxTypes.XMR_SWAP_B_LOCK,
 | 
			
		||||
            txid=b_lock_tx_id,
 | 
			
		||||
        )
 | 
			
		||||
        bid.xmr_b_lock_tx.setState(TxStates.TX_NONE)
 | 
			
		||||
 | 
			
		||||
        self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
 | 
			
		||||
        # Update copy of bid in swaps_in_progress
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def sendXmrBidSecret(self, bid_id, session):
 | 
			
		||||
        # Leader sending lock tx a release secret (MSG5F)
 | 
			
		||||
        self.log.debug('Sending bid secret for xmr bid %s', bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = self.getXmrBid(bid_id)
 | 
			
		||||
        assert(bid), 'Bid not found: {}.'.format(bid_id.hex())
 | 
			
		||||
        assert(xmr_swap), 'XMR swap not found: {}.'.format(bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=False)
 | 
			
		||||
        assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        coin_from = Coins(offer.coin_from)
 | 
			
		||||
        coin_to = Coins(offer.coin_to)
 | 
			
		||||
 | 
			
		||||
        contract_secret = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1)
 | 
			
		||||
 | 
			
		||||
        msg_buf = XmrBidSecretMessage(
 | 
			
		||||
            bid_msg_id=bid_id,
 | 
			
		||||
            secret_value=contract_secret)
 | 
			
		||||
 | 
			
		||||
        msg_bytes = msg_buf.SerializeToString()
 | 
			
		||||
        payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_SECRET_LF) + msg_bytes.hex()
 | 
			
		||||
 | 
			
		||||
        options = {'decodehex': True, 'ttl_is_seconds': True}
 | 
			
		||||
        # TODO: set msg_valid based on bid / offer parameters
 | 
			
		||||
        msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
 | 
			
		||||
        ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
 | 
			
		||||
        xmr_swap.coin_a_lock_refund_spend_tx_msg_id = bytes.fromhex(ro['msgid'])
 | 
			
		||||
 | 
			
		||||
        bid.setState(BidStates.XMR_SWAP_SECRET_SHARED)
 | 
			
		||||
        self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
        # Update copy of bid in swaps_in_progress
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def redeemXmrBidCoinALockTx(self, bid_id, session):
 | 
			
		||||
        # Follower redeeming A lock tx
 | 
			
		||||
        self.log.debug('Redeeming coin A lock tx for xmr bid %s', bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = self.getXmrBid(bid_id)
 | 
			
		||||
        assert(bid), 'Bid not found: {}.'.format(bid_id.hex())
 | 
			
		||||
        assert(xmr_swap), 'XMR swap not found: {}.'.format(bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=False)
 | 
			
		||||
        assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        coin_from = Coins(offer.coin_from)
 | 
			
		||||
        coin_to = Coins(offer.coin_to)
 | 
			
		||||
        ci_from = self.ci(coin_from)
 | 
			
		||||
        ci_to = self.ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True)
 | 
			
		||||
        kaf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3)
 | 
			
		||||
 | 
			
		||||
        al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig)
 | 
			
		||||
        v = ci_from.verifyTxSig(xmr_swap.a_lock_spend_tx, al_lock_spend_sig, xmr_swap.pkal, 0, xmr_swap.a_lock_tx_script, bid.amount)
 | 
			
		||||
        assert(v)
 | 
			
		||||
 | 
			
		||||
        af_lock_spend_sig = ci_from.signTx(kaf, xmr_swap.a_lock_spend_tx, 0, xmr_swap.a_lock_tx_script, bid.amount)
 | 
			
		||||
        v = ci_from.verifyTxSig(xmr_swap.a_lock_spend_tx, af_lock_spend_sig, xmr_swap.pkaf, 0, xmr_swap.a_lock_tx_script, bid.amount)
 | 
			
		||||
        assert(v)
 | 
			
		||||
 | 
			
		||||
        witness_stack = [
 | 
			
		||||
            b'',
 | 
			
		||||
            al_lock_spend_sig,
 | 
			
		||||
            af_lock_spend_sig,
 | 
			
		||||
            xmr_swap.sv,
 | 
			
		||||
            bytes((1,)),
 | 
			
		||||
            xmr_swap.a_lock_tx_script,
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        xmr_swap.a_lock_spend_tx = ci_from.setTxSignature(xmr_swap.a_lock_spend_tx, witness_stack)
 | 
			
		||||
 | 
			
		||||
        txid = bytes.fromhex(ci_from.publishTx(xmr_swap.a_lock_spend_tx))
 | 
			
		||||
        self.log.debug('Submitted lock spend txn %s to %s chain for bid %s', txid.hex(), chainparams[coin_from]['name'], bid_id.hex())
 | 
			
		||||
        bid.xmr_a_lock_spend_tx = SwapTx(
 | 
			
		||||
            bid_id=bid_id,
 | 
			
		||||
            tx_type=TxTypes.XMR_SWAP_A_LOCK_SPEND,
 | 
			
		||||
            txid=txid,
 | 
			
		||||
        )
 | 
			
		||||
        bid.xmr_a_lock_spend_tx.setState(TxStates.TX_NONE)
 | 
			
		||||
 | 
			
		||||
        self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
        # Update copy of bid in swaps_in_progress
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def redeemXmrBidCoinBLockTx(self, bid_id, session):
 | 
			
		||||
        # Leader redeeming B lock tx
 | 
			
		||||
        self.log.debug('Redeeming coin B lock tx for xmr bid %s', bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = self.getXmrBid(bid_id)
 | 
			
		||||
        assert(bid), 'Bid not found: {}.'.format(bid_id.hex())
 | 
			
		||||
        assert(xmr_swap), 'XMR swap not found: {}.'.format(bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=False)
 | 
			
		||||
        assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        coin_from = Coins(offer.coin_from)
 | 
			
		||||
        coin_to = Coins(offer.coin_to)
 | 
			
		||||
        ci_from = self.ci(coin_from)
 | 
			
		||||
        ci_to = self.ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        # Extract the leader's decrypted signature and use it to recover the follower's privatekey
 | 
			
		||||
        xmr_swap.al_lock_spend_tx_sig = ci_from.extractLeaderSig(xmr_swap.a_lock_spend_tx)
 | 
			
		||||
 | 
			
		||||
        xmr_swap.kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf)
 | 
			
		||||
        assert(xmr_swap.kbsf is not None)
 | 
			
		||||
 | 
			
		||||
        kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True)
 | 
			
		||||
 | 
			
		||||
        vkbs = ci_to.sumKeys(kbsl, xmr_swap.kbsf)
 | 
			
		||||
        Kbs_test = ci_to.getPubkey(vkbs)
 | 
			
		||||
        print('Kbs_test', Kbs_test.hex())
 | 
			
		||||
        print('Kbs', xmr_swap.pkbsf.hex())
 | 
			
		||||
 | 
			
		||||
        address_to = ci_to.getMainWalletAddress()
 | 
			
		||||
 | 
			
		||||
        txid = ci_to.spendBLockTx(address_to, xmr_swap.vkbv, vkbs, bid.amount_to, xmr_offer.b_fee_rate, xmr_swap.b_restore_height)
 | 
			
		||||
 | 
			
		||||
        bid.xmr_b_lock_tx.spend_txid = txid
 | 
			
		||||
        self.saveBidInSession(bid_id, bid, session, xmr_swap)
 | 
			
		||||
        # Update copy of bid in swaps_in_progress
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def processXmrBidCoinALockSigs(self, msg):
 | 
			
		||||
        # Leader processing MSG3L
 | 
			
		||||
        self.log.debug('Processing xmr coin a follower lock sigs msg %s', msg['msgid'])
 | 
			
		||||
@ -3123,7 +3500,6 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True)
 | 
			
		||||
            xmr_swap.af_lock_refund_spend_tx_sig = ci_from.decryptOtVES(kbsl, xmr_swap.af_lock_refund_spend_tx_esig)
 | 
			
		||||
 | 
			
		||||
            print('[rm] xmr_swap.a_swap_refund_value', xmr_swap.a_swap_refund_value)
 | 
			
		||||
            v = ci_from.verifyTxSig(xmr_swap.a_lock_refund_spend_tx, xmr_swap.af_lock_refund_spend_tx_sig, xmr_swap.pkarf, 0, xmr_swap.a_lock_refund_tx_script, xmr_swap.a_swap_refund_value)
 | 
			
		||||
            assert(v), 'Invalid signature for lock refund spend txn'
 | 
			
		||||
 | 
			
		||||
@ -3138,6 +3514,54 @@ class BasicSwap(BaseApp):
 | 
			
		||||
                traceback.print_exc()
 | 
			
		||||
            self.setBidError(bid_id, bid, str(ex))
 | 
			
		||||
 | 
			
		||||
    def processXmrBidLockSpendTx(self, msg):
 | 
			
		||||
        # Follower receiving MSG4F
 | 
			
		||||
        self.log.debug('Processing xmr bid lock spend tx msg %s', msg['msgid'])
 | 
			
		||||
        now = int(time.time())
 | 
			
		||||
        msg_bytes = bytes.fromhex(msg['hex'][2:-2])
 | 
			
		||||
        msg_data = XmrBidLockSpendTxMessage()
 | 
			
		||||
        msg_data.ParseFromString(msg_bytes)
 | 
			
		||||
 | 
			
		||||
        assert(len(msg_data.bid_msg_id) == 28), 'Bad bid_msg_id length'
 | 
			
		||||
        bid_id = msg_data.bid_msg_id
 | 
			
		||||
 | 
			
		||||
        bid, xmr_swap = self.getXmrBid(bid_id)
 | 
			
		||||
        assert(bid), 'Bid not found: {}.'.format(bid_id.hex())
 | 
			
		||||
        assert(xmr_swap), 'XMR swap not found: {}.'.format(bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=False)
 | 
			
		||||
        assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        coin_from = Coins(offer.coin_from)
 | 
			
		||||
        coin_to = Coins(offer.coin_to)
 | 
			
		||||
        ci_from = self.ci(coin_from)
 | 
			
		||||
        ci_to = self.ci(coin_to)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            xmr_swap.a_lock_spend_tx = msg_data.a_lock_spend_tx
 | 
			
		||||
            xmr_swap.a_lock_spend_tx_id = ci_from.getTxHash(xmr_swap.a_lock_spend_tx)
 | 
			
		||||
            xmr_swap.al_lock_spend_tx_esig = msg_data.al_lock_spend_tx_esig
 | 
			
		||||
 | 
			
		||||
            ci_from.verifyLockSpendTx(
 | 
			
		||||
                xmr_swap.a_lock_spend_tx,
 | 
			
		||||
                xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
 | 
			
		||||
                xmr_swap.dest_af, xmr_offer.a_fee_rate)
 | 
			
		||||
 | 
			
		||||
            v = ci_from.verifyTxOtVES(
 | 
			
		||||
                xmr_swap.a_lock_spend_tx, xmr_swap.al_lock_spend_tx_esig,
 | 
			
		||||
                xmr_swap.pkal, xmr_swap.pkasf, 0, xmr_swap.a_lock_tx_script, bid.amount)
 | 
			
		||||
            assert(v), 'verifyTxOtVES failed'
 | 
			
		||||
 | 
			
		||||
            bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX)
 | 
			
		||||
            self.saveBid(bid_id, bid, xmr_swap=xmr_swap)
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            if self.debug:
 | 
			
		||||
                traceback.print_exc()
 | 
			
		||||
            self.setBidError(bid_id, bid, str(ex))
 | 
			
		||||
 | 
			
		||||
        # Update copy of bid in swaps_in_progress
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def processXmrSplitMessage(self, msg):
 | 
			
		||||
        self.log.debug('Processing xmr split msg %s', msg['msgid'])
 | 
			
		||||
        now = int(time.time())
 | 
			
		||||
@ -3157,6 +3581,35 @@ class BasicSwap(BaseApp):
 | 
			
		||||
            dbr.created_at = now
 | 
			
		||||
            self.saveToDB(dbr)
 | 
			
		||||
 | 
			
		||||
    def processXmrSecretMessage(self, msg):
 | 
			
		||||
        self.log.debug('Processing xmr secret msg %s', msg['msgid'])
 | 
			
		||||
        now = int(time.time())
 | 
			
		||||
        msg_bytes = bytes.fromhex(msg['hex'][2:-2])
 | 
			
		||||
        msg_data = XmrBidSecretMessage()
 | 
			
		||||
        msg_data.ParseFromString(msg_bytes)
 | 
			
		||||
 | 
			
		||||
        # Validate data
 | 
			
		||||
        assert(len(msg_data.bid_msg_id) == 28), 'Bad msg_id length'
 | 
			
		||||
 | 
			
		||||
        bid_id = msg_data.bid_msg_id
 | 
			
		||||
        bid, xmr_swap = self.getXmrBid(bid_id)
 | 
			
		||||
        assert(bid), 'Bid not found: {}.'.format(bid_id.hex())
 | 
			
		||||
        assert(xmr_swap), 'XMR swap not found: {}.'.format(bid_id.hex())
 | 
			
		||||
 | 
			
		||||
        offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=False)
 | 
			
		||||
        assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
        assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
 | 
			
		||||
 | 
			
		||||
        xmr_swap.sv = msg_data.secret_value
 | 
			
		||||
 | 
			
		||||
        delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept)
 | 
			
		||||
        self.log.info('Redeeming coin A lock tx for xmr bid %s in %d seconds', bid_id.hex(), delay)
 | 
			
		||||
        self.createEvent(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_A, bid_id)
 | 
			
		||||
 | 
			
		||||
        bid.setState(BidStates.XMR_SWAP_SECRET_SHARED)
 | 
			
		||||
        self.saveBid(bid_id, bid, xmr_swap=xmr_swap)
 | 
			
		||||
        self.swaps_in_progress[bid_id] = (bid, offer)
 | 
			
		||||
 | 
			
		||||
    def processMsg(self, msg):
 | 
			
		||||
        self.mxDB.acquire()
 | 
			
		||||
        try:
 | 
			
		||||
@ -3169,14 +3622,18 @@ class BasicSwap(BaseApp):
 | 
			
		||||
                self.processBid(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.BID_ACCEPT:
 | 
			
		||||
                self.processBidAccept(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID:
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_FL:
 | 
			
		||||
                self.processXmrBid(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_ACCEPT:
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_ACCEPT_LF:
 | 
			
		||||
                self.processXmrBidAccept(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_TXN_SIGS_FL:
 | 
			
		||||
                self.processXmrBidCoinALockSigs(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_LOCK_SPEND_TX_LF:
 | 
			
		||||
                self.processXmrBidLockSpendTx(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_SPLIT:
 | 
			
		||||
                self.processXmrSplitMessage(msg)
 | 
			
		||||
            elif msg_type == MessageTypes.XMR_BID_SECRET_LF:
 | 
			
		||||
                self.processXmrSecretMessage(msg)
 | 
			
		||||
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            self.log.error('processMsg %s', str(ex))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										348
									
								
								basicswap/contrib/Keccak.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								basicswap/contrib/Keccak.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,348 @@
 | 
			
		||||
#! /usr/bin/pythonw
 | 
			
		||||
# The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
 | 
			
		||||
# questions, please refer to our website: http://keccak.noekeon.org/
 | 
			
		||||
# 
 | 
			
		||||
# Implementation by Renaud Bauvin,
 | 
			
		||||
# hereby denoted as "the implementer".
 | 
			
		||||
# 
 | 
			
		||||
# To the extent possible under law, the implementer has waived all copyright
 | 
			
		||||
# and related or neighboring rights to the source code in this file.
 | 
			
		||||
# http://creativecommons.org/publicdomain/zero/1.0/
 | 
			
		||||
 | 
			
		||||
import math
 | 
			
		||||
 | 
			
		||||
class KeccakError(Exception):
 | 
			
		||||
    """Class of error used in the Keccak implementation
 | 
			
		||||
 | 
			
		||||
    Use: raise KeccakError.KeccakError("Text to be displayed")"""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, value):
 | 
			
		||||
        self.value = value
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return repr(self.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Keccak:
 | 
			
		||||
    """
 | 
			
		||||
    Class implementing the Keccak sponge function
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, b=1600):
 | 
			
		||||
        """Constructor:
 | 
			
		||||
 | 
			
		||||
        b: parameter b, must be 25, 50, 100, 200, 400, 800 or 1600 (default value)"""
 | 
			
		||||
        self.setB(b)
 | 
			
		||||
 | 
			
		||||
    def setB(self,b):
 | 
			
		||||
        """Set the value of the parameter b (and thus w,l and nr)
 | 
			
		||||
 | 
			
		||||
        b: parameter b, must be choosen among [25, 50, 100, 200, 400, 800, 1600]
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if b not in [25, 50, 100, 200, 400, 800, 1600]:
 | 
			
		||||
            raise KeccakError.KeccakError('b value not supported - use 25, 50, 100, 200, 400, 800 or 1600')
 | 
			
		||||
 | 
			
		||||
        # Update all the parameters based on the used value of b
 | 
			
		||||
        self.b=b
 | 
			
		||||
        self.w=b//25
 | 
			
		||||
        self.l=int(math.log(self.w,2))
 | 
			
		||||
        self.nr=12+2*self.l
 | 
			
		||||
 | 
			
		||||
    # Constants
 | 
			
		||||
 | 
			
		||||
    ## Round constants
 | 
			
		||||
    RC=[0x0000000000000001,
 | 
			
		||||
        0x0000000000008082,
 | 
			
		||||
        0x800000000000808A,
 | 
			
		||||
        0x8000000080008000,
 | 
			
		||||
        0x000000000000808B,
 | 
			
		||||
        0x0000000080000001,
 | 
			
		||||
        0x8000000080008081,
 | 
			
		||||
        0x8000000000008009,
 | 
			
		||||
        0x000000000000008A,
 | 
			
		||||
        0x0000000000000088,
 | 
			
		||||
        0x0000000080008009,
 | 
			
		||||
        0x000000008000000A,
 | 
			
		||||
        0x000000008000808B,
 | 
			
		||||
        0x800000000000008B,
 | 
			
		||||
        0x8000000000008089,
 | 
			
		||||
        0x8000000000008003,
 | 
			
		||||
        0x8000000000008002,
 | 
			
		||||
        0x8000000000000080,
 | 
			
		||||
        0x000000000000800A,
 | 
			
		||||
        0x800000008000000A,
 | 
			
		||||
        0x8000000080008081,
 | 
			
		||||
        0x8000000000008080,
 | 
			
		||||
        0x0000000080000001,
 | 
			
		||||
        0x8000000080008008]
 | 
			
		||||
 | 
			
		||||
    ## Rotation offsets
 | 
			
		||||
    r=[[0,    36,     3,    41,    18]    ,
 | 
			
		||||
       [1,    44,    10,    45,     2]    ,
 | 
			
		||||
       [62,    6,    43,    15,    61]    ,
 | 
			
		||||
       [28,   55,    25,    21,    56]    ,
 | 
			
		||||
       [27,   20,    39,     8,    14]    ]
 | 
			
		||||
 | 
			
		||||
    ## Generic utility functions
 | 
			
		||||
 | 
			
		||||
    def rot(self,x,n):
 | 
			
		||||
        """Bitwise rotation (to the left) of n bits considering the \
 | 
			
		||||
        string of bits is w bits long"""
 | 
			
		||||
 | 
			
		||||
        n = n%self.w
 | 
			
		||||
        return ((x>>(self.w-n))+(x<<n))%(1<<self.w)
 | 
			
		||||
 | 
			
		||||
    def fromHexStringToLane(self, string):
 | 
			
		||||
        """Convert a string of bytes written in hexadecimal to a lane value"""
 | 
			
		||||
 | 
			
		||||
        #Check that the string has an even number of characters i.e. whole number of bytes
 | 
			
		||||
        if len(string)%2!=0:
 | 
			
		||||
            raise KeccakError.KeccakError("The provided string does not end with a full byte")
 | 
			
		||||
 | 
			
		||||
        #Perform the modification
 | 
			
		||||
        temp=''
 | 
			
		||||
        nrBytes=len(string)//2
 | 
			
		||||
        for i in range(nrBytes):
 | 
			
		||||
            offset=(nrBytes-i-1)*2
 | 
			
		||||
            temp+=string[offset:offset+2]
 | 
			
		||||
        return int(temp, 16)
 | 
			
		||||
 | 
			
		||||
    def fromLaneToHexString(self, lane):
 | 
			
		||||
        """Convert a lane value to a string of bytes written in hexadecimal"""
 | 
			
		||||
 | 
			
		||||
        laneHexBE = (("%%0%dX" % (self.w//4)) % lane)
 | 
			
		||||
        #Perform the modification
 | 
			
		||||
        temp=''
 | 
			
		||||
        nrBytes=len(laneHexBE)//2
 | 
			
		||||
        for i in range(nrBytes):
 | 
			
		||||
            offset=(nrBytes-i-1)*2
 | 
			
		||||
            temp+=laneHexBE[offset:offset+2]
 | 
			
		||||
        return temp.upper()
 | 
			
		||||
 | 
			
		||||
    def printState(self, state, info):
 | 
			
		||||
        """Print on screen the state of the sponge function preceded by \
 | 
			
		||||
        string info
 | 
			
		||||
 | 
			
		||||
        state: state of the sponge function
 | 
			
		||||
        info: a string of characters used as identifier"""
 | 
			
		||||
 | 
			
		||||
        print("Current value of state: %s" % (info))
 | 
			
		||||
        for y in range(5):
 | 
			
		||||
            line=[]
 | 
			
		||||
            for x in range(5):
 | 
			
		||||
                 line.append(hex(state[x][y]))
 | 
			
		||||
            print('\t%s' % line)
 | 
			
		||||
 | 
			
		||||
    ### Conversion functions String <-> Table (and vice-versa)
 | 
			
		||||
 | 
			
		||||
    def convertStrToTable(self,string):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        #Check that input paramaters
 | 
			
		||||
        if self.w%8!= 0:
 | 
			
		||||
            raise KeccakError("w is not a multiple of 8")
 | 
			
		||||
        if len(string)!=2*(self.b)//8:
 | 
			
		||||
            raise KeccakError.KeccakError("string can't be divided in 25 blocks of w bits\
 | 
			
		||||
            i.e. string must have exactly b bits")
 | 
			
		||||
 | 
			
		||||
        #Convert
 | 
			
		||||
        output=[[0,0,0,0,0],
 | 
			
		||||
                [0,0,0,0,0],
 | 
			
		||||
                [0,0,0,0,0],
 | 
			
		||||
                [0,0,0,0,0],
 | 
			
		||||
                [0,0,0,0,0]]
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            for y in range(5):
 | 
			
		||||
                offset=2*((5*y+x)*self.w)//8
 | 
			
		||||
                output[x][y]=self.fromHexStringToLane(string[offset:offset+(2*self.w//8)])
 | 
			
		||||
        return output
 | 
			
		||||
 | 
			
		||||
    def convertTableToStr(self,table):
 | 
			
		||||
 | 
			
		||||
        #Check input format
 | 
			
		||||
        if self.w%8!= 0:
 | 
			
		||||
            raise KeccakError.KeccakError("w is not a multiple of 8")
 | 
			
		||||
        if (len(table)!=5) or (False in [len(row)==5 for row in table]):
 | 
			
		||||
            raise KeccakError.KeccakError("table must b")
 | 
			
		||||
 | 
			
		||||
        #Convert
 | 
			
		||||
        output=['']*25
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            for y in range(5):
 | 
			
		||||
                output[5*y+x]=self.fromLaneToHexString(table[x][y])
 | 
			
		||||
        output =''.join(output).upper()
 | 
			
		||||
        return output
 | 
			
		||||
 | 
			
		||||
    def Round(self,A,RCfixed):
 | 
			
		||||
        """Perform one round of computation as defined in the Keccak-f permutation
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        #Initialisation of temporary variables
 | 
			
		||||
        B=[[0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0]]
 | 
			
		||||
        C= [0,0,0,0,0]
 | 
			
		||||
        D= [0,0,0,0,0]
 | 
			
		||||
 | 
			
		||||
        #Theta step
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            C[x] = A[x][0]^A[x][1]^A[x][2]^A[x][3]^A[x][4]
 | 
			
		||||
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            D[x] = C[(x-1)%5]^self.rot(C[(x+1)%5],1)
 | 
			
		||||
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            for y in range(5):
 | 
			
		||||
                A[x][y] = A[x][y]^D[x]
 | 
			
		||||
 | 
			
		||||
        #Rho and Pi steps
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
          for y in range(5):
 | 
			
		||||
                B[y][(2*x+3*y)%5] = self.rot(A[x][y], self.r[x][y])
 | 
			
		||||
 | 
			
		||||
        #Chi step
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            for y in range(5):
 | 
			
		||||
                A[x][y] = B[x][y]^((~B[(x+1)%5][y]) & B[(x+2)%5][y])
 | 
			
		||||
 | 
			
		||||
        #Iota step
 | 
			
		||||
        A[0][0] = A[0][0]^RCfixed
 | 
			
		||||
 | 
			
		||||
        return A
 | 
			
		||||
 | 
			
		||||
    def KeccakF(self,A, verbose=False):
 | 
			
		||||
        """Perform Keccak-f function on the state A
 | 
			
		||||
 | 
			
		||||
        verbose: a boolean flag activating the printing of intermediate computations
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if verbose:
 | 
			
		||||
            self.printState(A,"Before first round")
 | 
			
		||||
 | 
			
		||||
        for i in range(self.nr):
 | 
			
		||||
            #NB: result is truncated to lane size
 | 
			
		||||
            A = self.Round(A,self.RC[i]%(1<<self.w))
 | 
			
		||||
 | 
			
		||||
            if verbose:
 | 
			
		||||
                  self.printState(A,"Satus end of round #%d/%d" % (i+1,self.nr))
 | 
			
		||||
 | 
			
		||||
        return A
 | 
			
		||||
 | 
			
		||||
    ### Padding rule
 | 
			
		||||
 | 
			
		||||
    def pad10star1(self, M, n):
 | 
			
		||||
        """Pad M with the pad10*1 padding rule to reach a length multiple of r bits
 | 
			
		||||
 | 
			
		||||
        M: message pair (length in bits, string of hex characters ('9AFC...')
 | 
			
		||||
        n: length in bits (must be a multiple of 8)
 | 
			
		||||
        Example: pad10star1([60, 'BA594E0FB9EBBD30'],8) returns 'BA594E0FB9EBBD93'
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        [my_string_length, my_string]=M
 | 
			
		||||
 | 
			
		||||
        # Check the parameter n
 | 
			
		||||
        if n%8!=0:
 | 
			
		||||
            raise KeccakError.KeccakError("n must be a multiple of 8")
 | 
			
		||||
 | 
			
		||||
        # Check the length of the provided string
 | 
			
		||||
        if len(my_string)%2!=0:
 | 
			
		||||
            #Pad with one '0' to reach correct length (don't know test
 | 
			
		||||
            #vectors coding)
 | 
			
		||||
            my_string=my_string+'0'
 | 
			
		||||
        if my_string_length>(len(my_string)//2*8):
 | 
			
		||||
            raise KeccakError.KeccakError("the string is too short to contain the number of bits announced")
 | 
			
		||||
 | 
			
		||||
        nr_bytes_filled=my_string_length//8
 | 
			
		||||
        nbr_bits_filled=my_string_length%8
 | 
			
		||||
        l = my_string_length % n
 | 
			
		||||
        if ((n-8) <= l <= (n-2)):
 | 
			
		||||
            if (nbr_bits_filled == 0):
 | 
			
		||||
                my_byte = 0
 | 
			
		||||
            else:
 | 
			
		||||
                my_byte=int(my_string[nr_bytes_filled*2:nr_bytes_filled*2+2],16)
 | 
			
		||||
            my_byte=(my_byte>>(8-nbr_bits_filled))
 | 
			
		||||
            my_byte=my_byte+2**(nbr_bits_filled)+2**7
 | 
			
		||||
            my_byte="%02X" % my_byte
 | 
			
		||||
            my_string=my_string[0:nr_bytes_filled*2]+my_byte
 | 
			
		||||
        else:
 | 
			
		||||
            if (nbr_bits_filled == 0):
 | 
			
		||||
                my_byte = 0
 | 
			
		||||
            else:
 | 
			
		||||
                my_byte=int(my_string[nr_bytes_filled*2:nr_bytes_filled*2+2],16)
 | 
			
		||||
            my_byte=(my_byte>>(8-nbr_bits_filled))
 | 
			
		||||
            my_byte=my_byte+2**(nbr_bits_filled)
 | 
			
		||||
            my_byte="%02X" % my_byte
 | 
			
		||||
            my_string=my_string[0:nr_bytes_filled*2]+my_byte
 | 
			
		||||
            while((8*len(my_string)//2)%n < (n-8)):
 | 
			
		||||
                my_string=my_string+'00'
 | 
			
		||||
            my_string = my_string+'80'
 | 
			
		||||
 | 
			
		||||
        return my_string
 | 
			
		||||
 | 
			
		||||
    def Keccak(self,M,r=1024,c=512,n=1024,verbose=False):
 | 
			
		||||
        """Compute the Keccak[r,c,d] sponge function on message M
 | 
			
		||||
 | 
			
		||||
        M: message pair (length in bits, string of hex characters ('9AFC...')
 | 
			
		||||
        r: bitrate in bits (defautl: 1024)
 | 
			
		||||
        c: capacity in bits (default: 576)
 | 
			
		||||
        n: length of output in bits (default: 1024),
 | 
			
		||||
        verbose: print the details of computations(default:False)
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        #Check the inputs
 | 
			
		||||
        if (r<0) or (r%8!=0):
 | 
			
		||||
            raise KeccakError.KeccakError('r must be a multiple of 8 in this implementation')
 | 
			
		||||
        if (n%8!=0):
 | 
			
		||||
            raise KeccakError.KeccakError('outputLength must be a multiple of 8')
 | 
			
		||||
        self.setB(r+c)
 | 
			
		||||
 | 
			
		||||
        if verbose:
 | 
			
		||||
            print("Create a Keccak function with (r=%d, c=%d (i.e. w=%d))" % (r,c,(r+c)//25))
 | 
			
		||||
 | 
			
		||||
        #Compute lane length (in bits)
 | 
			
		||||
        w=(r+c)//25
 | 
			
		||||
 | 
			
		||||
        # Initialisation of state
 | 
			
		||||
        S=[[0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0],
 | 
			
		||||
           [0,0,0,0,0]]
 | 
			
		||||
 | 
			
		||||
        #Padding of messages
 | 
			
		||||
        P = self.pad10star1(M, r)
 | 
			
		||||
 | 
			
		||||
        if verbose:
 | 
			
		||||
            print("String ready to be absorbed: %s (will be completed by %d x '00')" % (P, c//8))
 | 
			
		||||
 | 
			
		||||
        #Absorbing phase
 | 
			
		||||
        for i in range((len(P)*8//2)//r):
 | 
			
		||||
            Pi=self.convertStrToTable(P[i*(2*r//8):(i+1)*(2*r//8)]+'00'*(c//8))
 | 
			
		||||
 | 
			
		||||
            for y in range(5):
 | 
			
		||||
              for x in range(5):
 | 
			
		||||
                  S[x][y] = S[x][y]^Pi[x][y]
 | 
			
		||||
            S = self.KeccakF(S, verbose)
 | 
			
		||||
 | 
			
		||||
        if verbose:
 | 
			
		||||
            print("Value after absorption : %s" % (self.convertTableToStr(S)))
 | 
			
		||||
 | 
			
		||||
        #Squeezing phase
 | 
			
		||||
        Z = ''
 | 
			
		||||
        outputLength = n
 | 
			
		||||
        while outputLength>0:
 | 
			
		||||
            string=self.convertTableToStr(S)
 | 
			
		||||
            Z = Z + string[:r*2//8]
 | 
			
		||||
            outputLength -= r
 | 
			
		||||
            if outputLength>0:
 | 
			
		||||
                S = self.KeccakF(S, verbose)
 | 
			
		||||
 | 
			
		||||
            # NB: done by block of length r, could have to be cut if outputLength
 | 
			
		||||
            #     is not a multiple of r
 | 
			
		||||
 | 
			
		||||
        if verbose:
 | 
			
		||||
            print("Value after squeezing : %s" % (self.convertTableToStr(S)))
 | 
			
		||||
 | 
			
		||||
        return Z[:2*n//8]
 | 
			
		||||
@ -108,6 +108,8 @@ class Bid(Base):
 | 
			
		||||
    initiate_tx = None
 | 
			
		||||
    participate_tx = None
 | 
			
		||||
    xmr_a_lock_tx = None
 | 
			
		||||
    xmr_b_lock_tx = None
 | 
			
		||||
    xmr_a_lock_spend_tx = None
 | 
			
		||||
 | 
			
		||||
    def getITxState(self):
 | 
			
		||||
        if self.initiate_tx is None:
 | 
			
		||||
@ -236,6 +238,7 @@ class XmrSwap(Base):
 | 
			
		||||
 | 
			
		||||
    contract_count = sa.Column(sa.Integer)
 | 
			
		||||
 | 
			
		||||
    sv = sa.Column(sa.LargeBinary)  # Secret value
 | 
			
		||||
    sh = sa.Column(sa.LargeBinary)  # Secret hash
 | 
			
		||||
 | 
			
		||||
    dest_af = sa.Column(sa.LargeBinary)  # Destination for coin A amount to follower when swap completes successfully
 | 
			
		||||
@ -261,6 +264,10 @@ class XmrSwap(Base):
 | 
			
		||||
    kbsl_dleag = sa.Column(sa.LargeBinary)
 | 
			
		||||
    kbsf_dleag = sa.Column(sa.LargeBinary)
 | 
			
		||||
 | 
			
		||||
    vkbv = sa.Column(sa.LargeBinary)
 | 
			
		||||
    pkbv = sa.Column(sa.LargeBinary)
 | 
			
		||||
    pkbs = sa.Column(sa.LargeBinary)
 | 
			
		||||
 | 
			
		||||
    a_lock_tx = sa.Column(sa.LargeBinary)
 | 
			
		||||
    a_lock_tx_script = sa.Column(sa.LargeBinary)
 | 
			
		||||
 | 
			
		||||
@ -275,8 +282,11 @@ class XmrSwap(Base):
 | 
			
		||||
    af_lock_refund_tx_sig = sa.Column(sa.LargeBinary)
 | 
			
		||||
 | 
			
		||||
    a_lock_spend_tx = sa.Column(sa.LargeBinary)
 | 
			
		||||
    a_lock_spend_tx_id = sa.Column(sa.LargeBinary)
 | 
			
		||||
    al_lock_spend_tx_esig = sa.Column(sa.LargeBinary)
 | 
			
		||||
 | 
			
		||||
    b_lock_tx_id = sa.Column(sa.LargeBinary)
 | 
			
		||||
 | 
			
		||||
    b_restore_height = sa.Column(sa.Integer)  # Height of xmr chain before the swap
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -117,6 +117,7 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
        self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth'])
 | 
			
		||||
        self.txoType = CTxOut
 | 
			
		||||
        self._network = network
 | 
			
		||||
        self.blocks_confirmed = coin_settings['blocks_confirmed']
 | 
			
		||||
 | 
			
		||||
    def testDaemonRPC(self):
 | 
			
		||||
        self.rpc_callback('getwalletinfo', [])
 | 
			
		||||
@ -596,13 +597,14 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def verifyLockSpendTx(self, tx,
 | 
			
		||||
                          lock_tx, lock_tx_script,
 | 
			
		||||
    def verifyLockSpendTx(self, tx_bytes,
 | 
			
		||||
                          lock_tx_bytes, lock_tx_script,
 | 
			
		||||
                          a_pkhash_f, feerate):
 | 
			
		||||
        # Verify:
 | 
			
		||||
        #   Must have only one input with correct prevout (n is always 0) and sequence
 | 
			
		||||
        #   Must have only one output with destination and amount
 | 
			
		||||
 | 
			
		||||
        tx = self.loadTx(tx_bytes)
 | 
			
		||||
        tx_hash = self.getTxHash(tx)
 | 
			
		||||
        logging.info('Verifying lock spend tx: {}.'.format(b2h(tx_hash)))
 | 
			
		||||
 | 
			
		||||
@ -610,6 +612,7 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
        assert_cond(tx.nLockTime == 0, 'nLockTime not 0')
 | 
			
		||||
        assert_cond(len(tx.vin) == 1, 'tx doesn\'t have one input')
 | 
			
		||||
 | 
			
		||||
        lock_tx = self.loadTx(lock_tx_bytes)
 | 
			
		||||
        lock_tx_id = self.getTxHash(lock_tx)
 | 
			
		||||
 | 
			
		||||
        output_script = CScript([OP_0, hashlib.sha256(lock_tx_script).digest()])
 | 
			
		||||
@ -727,21 +730,24 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
 | 
			
		||||
    def getTransaction(self, txid):
 | 
			
		||||
        try:
 | 
			
		||||
            return self.rpc_callback('getrawtransaction', [txid.hex()])
 | 
			
		||||
            return bytes.fromhex(self.rpc_callback('getrawtransaction', [txid.hex()]))
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            # TODO: filter errors
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def setTxSignature(self, tx, stack):
 | 
			
		||||
    def setTxSignature(self, tx_bytes, stack):
 | 
			
		||||
        tx = self.loadTx(tx_bytes)
 | 
			
		||||
        tx.wit.vtxinwit.clear()
 | 
			
		||||
        tx.wit.vtxinwit.append(CTxInWitness())
 | 
			
		||||
        tx.wit.vtxinwit[0].scriptWitness.stack = stack
 | 
			
		||||
        return True
 | 
			
		||||
        return tx.serialize()
 | 
			
		||||
 | 
			
		||||
    def extractLeaderSig(self, tx):
 | 
			
		||||
    def extractLeaderSig(self, tx_bytes):
 | 
			
		||||
        tx = self.loadTx(tx_bytes)
 | 
			
		||||
        return tx.wit.vtxinwit[0].scriptWitness.stack[1]
 | 
			
		||||
 | 
			
		||||
    def extractFollowerSig(self, tx):
 | 
			
		||||
    def extractFollowerSig(self, tx_bytes):
 | 
			
		||||
        tx = self.loadTx(tx_bytes)
 | 
			
		||||
        return tx.wit.vtxinwit[0].scriptWitness.stack[2]
 | 
			
		||||
 | 
			
		||||
    def createBLockTx(self, Kbs, output_amount):
 | 
			
		||||
@ -761,7 +767,8 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
        return self.publishTx(b_lock_tx)
 | 
			
		||||
 | 
			
		||||
    def recoverEncKey(self, esig, sig, K):
 | 
			
		||||
        return otves.RecoverEncKey(esig, sig[:-1], K)  # Strip sighash type
 | 
			
		||||
        return ecdsaotves_rec_enc_key(K, esig, sig[:-1])  # Strip sighash type
 | 
			
		||||
        #return otves.RecoverEncKey(esig, sig[:-1], K)  # Strip sighash type
 | 
			
		||||
 | 
			
		||||
    def getTxVSize(self, tx, add_bytes=0, add_witness_bytes=0):
 | 
			
		||||
        wsf = self.witnessScaleFactor()
 | 
			
		||||
@ -781,8 +788,8 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
                if utxo['amount'] * COIN != cb_swap_value:
 | 
			
		||||
                    logging.warning('Found output to lock tx pubkey of incorrect value: %s', str(utxo['amount']))
 | 
			
		||||
                else:
 | 
			
		||||
                    return True
 | 
			
		||||
        return False
 | 
			
		||||
                    return {'txid': utxo['txid'], 'vout': utxo['vout'], 'amount': utxo['amount'], 'height': utxo['height']}
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed):
 | 
			
		||||
 | 
			
		||||
@ -805,6 +812,31 @@ class BTCInterface(CoinInterface):
 | 
			
		||||
    def spendBLockTx(self, address_to, kbv, kbs, cb_swap_value, b_fee, restore_height):
 | 
			
		||||
        print('TODO: spendBLockTx')
 | 
			
		||||
 | 
			
		||||
    def getOutput(self, txid, dest_script, expect_value):
 | 
			
		||||
        # TODO: Use getrawtransaction if txindex is active
 | 
			
		||||
        utxos = self.rpc_callback('scantxoutset', ['start', ['raw({})'.format(dest_script.hex())]])
 | 
			
		||||
        print('utxos', utxos)
 | 
			
		||||
 | 
			
		||||
        chain_height = utxos['height']
 | 
			
		||||
        rv = []
 | 
			
		||||
        for utxo in utxos['unspents']:
 | 
			
		||||
            print('utxo', utxo)
 | 
			
		||||
            depth = 0 if 'height' not in utxo else utxos['height'] - utxo['height']
 | 
			
		||||
 | 
			
		||||
            if txid and txid.hex() != utxo['txid']:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if expect_value != utxo['amount'] * COIN:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            rv.append({
 | 
			
		||||
                'depth': depth,
 | 
			
		||||
                'amount': utxo['amount'] * COIN,
 | 
			
		||||
                'txid': utxo['txid'],
 | 
			
		||||
                'vout': utxo['vout']})
 | 
			
		||||
        return rv
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def testBTCInterface():
 | 
			
		||||
    print('testBTCInterface')
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,7 @@ class PARTInterface(BTCInterface):
 | 
			
		||||
        self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth'])
 | 
			
		||||
        self.txoType = CTxOutPart
 | 
			
		||||
        self._network = network
 | 
			
		||||
        self.blocks_confirmed = coin_settings['blocks_confirmed']
 | 
			
		||||
 | 
			
		||||
    def getNewAddress(self, use_segwit):
 | 
			
		||||
        return self.rpc_callback('getnewaddress', ['swap_receive'])
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ import logging
 | 
			
		||||
 | 
			
		||||
import basicswap.contrib.ed25519_fast as edf
 | 
			
		||||
import basicswap.ed25519_fast_util as edu
 | 
			
		||||
import basicswap.util_xmr as xmr_util
 | 
			
		||||
from coincurve.ed25519 import ed25519_get_pubkey
 | 
			
		||||
from coincurve.keys import PrivateKey
 | 
			
		||||
from coincurve.dleag import (
 | 
			
		||||
@ -19,12 +20,13 @@ from coincurve.dleag import (
 | 
			
		||||
    dleag_prove)
 | 
			
		||||
 | 
			
		||||
from .util import (
 | 
			
		||||
    dumpj,
 | 
			
		||||
    format_amount)
 | 
			
		||||
from .rpc_xmr import (
 | 
			
		||||
    make_xmr_rpc_func,
 | 
			
		||||
    make_xmr_wallet_rpc_func)
 | 
			
		||||
from .ecc_util import (
 | 
			
		||||
    b2i)
 | 
			
		||||
    b2i, i2b, b2h)
 | 
			
		||||
from .chainparams import CoinInterface, Coins
 | 
			
		||||
 | 
			
		||||
XMR_COIN = 10 ** 12
 | 
			
		||||
@ -54,6 +56,10 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
        self.rpc_cb = rpc_cb
 | 
			
		||||
        self.rpc_wallet_cb = rpc_wallet_cb
 | 
			
		||||
        self._network = network
 | 
			
		||||
        self.blocks_confirmed = coin_settings['blocks_confirmed']
 | 
			
		||||
 | 
			
		||||
    def setWalletFilename(self, wallet_filename):
 | 
			
		||||
        self._wallet_filename = wallet_filename
 | 
			
		||||
 | 
			
		||||
    def testDaemonRPC(self):
 | 
			
		||||
        self.rpc_wallet_cb('get_languages')
 | 
			
		||||
@ -77,8 +83,13 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
        rv['unconfirmed_balance'] = format_amount(balance_info['balance'] - balance_info['unlocked_balance'], XMRInterface.exp())
 | 
			
		||||
        return rv
 | 
			
		||||
 | 
			
		||||
    def getMainWalletAddress(self):
 | 
			
		||||
        self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
 | 
			
		||||
        return self.rpc_wallet_cb('get_address')['address']
 | 
			
		||||
 | 
			
		||||
    def getNewAddress(self, placeholder):
 | 
			
		||||
        logging.debug('TODO - subaddress?')
 | 
			
		||||
        self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
 | 
			
		||||
        return self.rpc_wallet_cb('get_address')['address']
 | 
			
		||||
 | 
			
		||||
    def isValidKey(self, key_bytes):
 | 
			
		||||
@ -123,36 +134,41 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
        return i
 | 
			
		||||
 | 
			
		||||
    def sumKeys(self, ka, kb):
 | 
			
		||||
        return (ka + kb) % edf.l
 | 
			
		||||
        return i2b((b2i(ka) + b2i(kb)) % edf.l)
 | 
			
		||||
 | 
			
		||||
    def sumPubkeys(self, Ka, Kb):
 | 
			
		||||
        return edf.edwards_add(Ka, Kb)
 | 
			
		||||
        Ka_d = edf.decodepoint(Ka)
 | 
			
		||||
        Kb_d = edf.decodepoint(Kb)
 | 
			
		||||
        return self.encodePubkey(edf.edwards_add(Ka_d, Kb_d))
 | 
			
		||||
 | 
			
		||||
    def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
 | 
			
		||||
 | 
			
		||||
        shared_addr = xmr_util.encode_address(self.encodePubkey(Kbv), self.encodePubkey(Kbs))
 | 
			
		||||
        shared_addr = xmr_util.encode_address(Kbv, Kbs)
 | 
			
		||||
 | 
			
		||||
        # TODO: How to set feerate?
 | 
			
		||||
        params = {'destinations': [{'amount': output_amount, 'address': shared_addr}]}
 | 
			
		||||
        rv = self.rpc_wallet_cb('transfer', params)
 | 
			
		||||
        logging.info('publishBLockTx %s to address_b58 %s', rv['tx_hash'], shared_addr)
 | 
			
		||||
 | 
			
		||||
        return rv['tx_hash']
 | 
			
		||||
        return bytes.fromhex(rv['tx_hash'])
 | 
			
		||||
 | 
			
		||||
    def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
 | 
			
		||||
        Kbv_enc = self.encodePubkey(self.pubkey(kbv))
 | 
			
		||||
        address_b58 = xmr_util.encode_address(Kbv_enc, self.encodePubkey(Kbs))
 | 
			
		||||
        #Kbv_enc = self.encodePubkey(self.pubkey(kbv))
 | 
			
		||||
        Kbv = self.getPubkey(kbv)
 | 
			
		||||
        address_b58 = xmr_util.encode_address(Kbv, Kbs)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.rpc_wallet_cb('close_wallet')
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logging.warning('close_wallet failed %s', str(e))
 | 
			
		||||
 | 
			
		||||
        kbv_le = kbv[::-1]
 | 
			
		||||
        params = {
 | 
			
		||||
            'restore_height': restore_height,
 | 
			
		||||
            'filename': address_b58,
 | 
			
		||||
            'address': address_b58,
 | 
			
		||||
            'viewkey': b2h(intToBytes32_le(kbv)),
 | 
			
		||||
            #'viewkey': b2h(intToBytes32_le(kbv)),
 | 
			
		||||
            'viewkey': b2h(kbv_le),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
@ -162,6 +178,8 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
            logging.info('generate_from_keys %s', dumpj(rv))
 | 
			
		||||
            rv = self.rpc_wallet_cb('open_wallet', {'filename': address_b58})
 | 
			
		||||
 | 
			
		||||
        rv = self.rpc_wallet_cb('refresh')
 | 
			
		||||
 | 
			
		||||
        # Debug
 | 
			
		||||
        try:
 | 
			
		||||
            current_height = self.rpc_cb('get_block_count')['count']
 | 
			
		||||
@ -170,18 +188,15 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
            logging.info('rpc_cb failed %s', str(e))
 | 
			
		||||
            current_height = None  # If the transfer is available it will be deep enough
 | 
			
		||||
 | 
			
		||||
        # For a while after opening the wallet rpc cmds return empty data
 | 
			
		||||
        for i in range(5):
 | 
			
		||||
            params = {'transfer_type': 'available'}
 | 
			
		||||
            rv = self.rpc_wallet_cb('incoming_transfers', params)
 | 
			
		||||
            if 'transfers' in rv:
 | 
			
		||||
                for transfer in rv['transfers']:
 | 
			
		||||
                    if transfer['amount'] == cb_swap_value \
 | 
			
		||||
                       and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
 | 
			
		||||
                        return True
 | 
			
		||||
            time.sleep(1 + i)
 | 
			
		||||
        params = {'transfer_type': 'available'}
 | 
			
		||||
        rv = self.rpc_wallet_cb('incoming_transfers', params)
 | 
			
		||||
        if 'transfers' in rv:
 | 
			
		||||
            for transfer in rv['transfers']:
 | 
			
		||||
                if transfer['amount'] == cb_swap_value \
 | 
			
		||||
                   and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
 | 
			
		||||
                    return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': transfer['block_height']}
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
 | 
			
		||||
 | 
			
		||||
@ -244,11 +259,33 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def findTxnByHash(self, txid):
 | 
			
		||||
        self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
 | 
			
		||||
        self.rpc_wallet_cb('refresh')
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            current_height = self.rpc_cb('get_block_count')['count']
 | 
			
		||||
            logging.info('findTxnByHash XMR current_height %d\nhash: %s', current_height, txid)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logging.info('rpc_cb failed %s', str(e))
 | 
			
		||||
            current_height = None  # If the transfer is available it will be deep enough
 | 
			
		||||
 | 
			
		||||
        params = {'transfer_type': 'available'}
 | 
			
		||||
        rv = self.rpc_wallet_cb('incoming_transfers', params)
 | 
			
		||||
        if 'transfers' in rv:
 | 
			
		||||
            for transfer in rv['transfers']:
 | 
			
		||||
                if transfer['tx_hash'] == txid \
 | 
			
		||||
                   and (current_height is None or current_height - transfer['block_height'] > self.blocks_confirmed):
 | 
			
		||||
                    return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': transfer['block_height']}
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def spendBLockTx(self, address_to, kbv, kbs, cb_swap_value, b_fee_rate, restore_height):
 | 
			
		||||
 | 
			
		||||
        Kbv_enc = self.encodePubkey(self.pubkey(kbv))
 | 
			
		||||
        Kbs_enc = self.encodePubkey(self.pubkey(kbs))
 | 
			
		||||
        address_b58 = xmr_util.encode_address(Kbv_enc, Kbs_enc)
 | 
			
		||||
        Kbv = self.getPubkey(kbv)
 | 
			
		||||
        Kbs = self.getPubkey(kbs)
 | 
			
		||||
        address_b58 = xmr_util.encode_address(Kbv, Kbs)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.rpc_wallet_cb('close_wallet')
 | 
			
		||||
@ -260,8 +297,8 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
        params = {
 | 
			
		||||
            'filename': wallet_filename,
 | 
			
		||||
            'address': address_b58,
 | 
			
		||||
            'viewkey': b2h(intToBytes32_le(kbv)),
 | 
			
		||||
            'spendkey': b2h(intToBytes32_le(kbs)),
 | 
			
		||||
            'viewkey': b2h(kbv[::-1]),
 | 
			
		||||
            'spendkey': b2h(kbs[::-1]),
 | 
			
		||||
            'restore_height': restore_height,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -298,4 +335,4 @@ class XMRInterface(CoinInterface):
 | 
			
		||||
            b_fee += b_fee_rate
 | 
			
		||||
            logging.info('Raising fee to %d', b_fee)
 | 
			
		||||
 | 
			
		||||
        return rv['tx_hash']
 | 
			
		||||
        return bytes.fromhex(rv['tx_hash'])
 | 
			
		||||
 | 
			
		||||
@ -108,4 +108,9 @@ message XmrBidLockSpendTxMessage {
 | 
			
		||||
    bytes al_lock_spend_tx_esig = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message XmrBidSecretMessage {
 | 
			
		||||
    /* MSG5F */
 | 
			
		||||
    bytes bid_msg_id = 1;
 | 
			
		||||
 | 
			
		||||
    bytes secret_value = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
 | 
			
		||||
  syntax='proto3',
 | 
			
		||||
  serialized_options=None,
 | 
			
		||||
  create_key=_descriptor._internal_create_key,
 | 
			
		||||
  serialized_pb=b'\n\x0emessages.proto\x12\tbasicswap\"\xd8\x03\n\x0cOfferMessage\x12\x11\n\tcoin_from\x18\x01 \x01(\r\x12\x0f\n\x07\x63oin_to\x18\x02 \x01(\r\x12\x13\n\x0b\x61mount_from\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x16\n\x0emin_bid_amount\x18\x05 \x01(\x04\x12\x12\n\ntime_valid\x18\x06 \x01(\x04\x12\x33\n\tlock_type\x18\x07 \x01(\x0e\x32 .basicswap.OfferMessage.LockType\x12\x12\n\nlock_value\x18\x08 \x01(\r\x12\x11\n\tswap_type\x18\t \x01(\r\x12\x15\n\rproof_address\x18\n \x01(\t\x12\x17\n\x0fproof_signature\x18\x0b \x01(\t\x12\x15\n\rpkhash_seller\x18\x0c \x01(\x0c\x12\x13\n\x0bsecret_hash\x18\r \x01(\x0c\x12\x15\n\rfee_rate_from\x18\x0e \x01(\x04\x12\x13\n\x0b\x66\x65\x65_rate_to\x18\x0f \x01(\x04\"q\n\x08LockType\x12\x0b\n\x07NOT_SET\x10\x00\x12\x18\n\x14SEQUENCE_LOCK_BLOCKS\x10\x01\x12\x16\n\x12SEQUENCE_LOCK_TIME\x10\x02\x12\x13\n\x0f\x41\x42S_LOCK_BLOCKS\x10\x03\x12\x11\n\rABS_LOCK_TIME\x10\x04\"\x8c\x01\n\nBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x04 \x01(\x0c\x12\x15\n\rproof_address\x18\x05 \x01(\t\x12\x17\n\x0fproof_signature\x18\x06 \x01(\t\"V\n\x10\x42idAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x15\n\rinitiate_txid\x18\x02 \x01(\x0c\x12\x17\n\x0f\x63ontract_script\x18\x03 \x01(\x0c\"\x99\x01\n\rXmrBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x0c\n\x04pkaf\x18\x04 \x01(\x0c\x12\r\n\x05pkarf\x18\x05 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x06 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x07 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x08 \x01(\x0c\"T\n\x0fXmrSplitMessage\x12\x0e\n\x06msg_id\x18\x01 \x01(\x0c\x12\x10\n\x08msg_type\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\r\n\x05\x64leag\x18\x04 \x01(\x0c\"\x9b\x02\n\x13XmrBidAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\n\n\x02sh\x18\x02 \x01(\x0c\x12\x0c\n\x04pkal\x18\x03 \x01(\x0c\x12\r\n\x05pkarl\x18\x04 \x01(\x0c\x12\x0c\n\x04kbvl\x18\x05 \x01(\x0c\x12\x12\n\nkbsl_dleag\x18\x06 \x01(\x0c\x12\x11\n\ta_lock_tx\x18\x07 \x01(\x0c\x12\x18\n\x10\x61_lock_tx_script\x18\x08 \x01(\x0c\x12\x18\n\x10\x61_lock_refund_tx\x18\t \x01(\x0c\x12\x1f\n\x17\x61_lock_refund_tx_script\x18\n \x01(\x0c\x12\x1e\n\x16\x61_lock_refund_spend_tx\x18\x0b \x01(\x0c\x12\x1d\n\x15\x61l_lock_refund_tx_sig\x18\x0c \x01(\x0c\"r\n\x17XmrBidLockTxSigsMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12$\n\x1c\x61\x66_lock_refund_spend_tx_esig\x18\x02 \x01(\x0c\x12\x1d\n\x15\x61\x66_lock_refund_tx_sig\x18\x03 \x01(\x0c\"f\n\x18XmrBidLockSpendTxMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x17\n\x0f\x61_lock_spend_tx\x18\x02 \x01(\x0c\x12\x1d\n\x15\x61l_lock_spend_tx_esig\x18\x03 \x01(\x0c\x62\x06proto3'
 | 
			
		||||
  serialized_pb=b'\n\x0emessages.proto\x12\tbasicswap\"\xd8\x03\n\x0cOfferMessage\x12\x11\n\tcoin_from\x18\x01 \x01(\r\x12\x0f\n\x07\x63oin_to\x18\x02 \x01(\r\x12\x13\n\x0b\x61mount_from\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x16\n\x0emin_bid_amount\x18\x05 \x01(\x04\x12\x12\n\ntime_valid\x18\x06 \x01(\x04\x12\x33\n\tlock_type\x18\x07 \x01(\x0e\x32 .basicswap.OfferMessage.LockType\x12\x12\n\nlock_value\x18\x08 \x01(\r\x12\x11\n\tswap_type\x18\t \x01(\r\x12\x15\n\rproof_address\x18\n \x01(\t\x12\x17\n\x0fproof_signature\x18\x0b \x01(\t\x12\x15\n\rpkhash_seller\x18\x0c \x01(\x0c\x12\x13\n\x0bsecret_hash\x18\r \x01(\x0c\x12\x15\n\rfee_rate_from\x18\x0e \x01(\x04\x12\x13\n\x0b\x66\x65\x65_rate_to\x18\x0f \x01(\x04\"q\n\x08LockType\x12\x0b\n\x07NOT_SET\x10\x00\x12\x18\n\x14SEQUENCE_LOCK_BLOCKS\x10\x01\x12\x16\n\x12SEQUENCE_LOCK_TIME\x10\x02\x12\x13\n\x0f\x41\x42S_LOCK_BLOCKS\x10\x03\x12\x11\n\rABS_LOCK_TIME\x10\x04\"\x8c\x01\n\nBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x04 \x01(\x0c\x12\x15\n\rproof_address\x18\x05 \x01(\t\x12\x17\n\x0fproof_signature\x18\x06 \x01(\t\"V\n\x10\x42idAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x15\n\rinitiate_txid\x18\x02 \x01(\x0c\x12\x17\n\x0f\x63ontract_script\x18\x03 \x01(\x0c\"\x99\x01\n\rXmrBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x0c\n\x04pkaf\x18\x04 \x01(\x0c\x12\r\n\x05pkarf\x18\x05 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x06 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x07 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x08 \x01(\x0c\"T\n\x0fXmrSplitMessage\x12\x0e\n\x06msg_id\x18\x01 \x01(\x0c\x12\x10\n\x08msg_type\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\r\n\x05\x64leag\x18\x04 \x01(\x0c\"\x9b\x02\n\x13XmrBidAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\n\n\x02sh\x18\x02 \x01(\x0c\x12\x0c\n\x04pkal\x18\x03 \x01(\x0c\x12\r\n\x05pkarl\x18\x04 \x01(\x0c\x12\x0c\n\x04kbvl\x18\x05 \x01(\x0c\x12\x12\n\nkbsl_dleag\x18\x06 \x01(\x0c\x12\x11\n\ta_lock_tx\x18\x07 \x01(\x0c\x12\x18\n\x10\x61_lock_tx_script\x18\x08 \x01(\x0c\x12\x18\n\x10\x61_lock_refund_tx\x18\t \x01(\x0c\x12\x1f\n\x17\x61_lock_refund_tx_script\x18\n \x01(\x0c\x12\x1e\n\x16\x61_lock_refund_spend_tx\x18\x0b \x01(\x0c\x12\x1d\n\x15\x61l_lock_refund_tx_sig\x18\x0c \x01(\x0c\"r\n\x17XmrBidLockTxSigsMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12$\n\x1c\x61\x66_lock_refund_spend_tx_esig\x18\x02 \x01(\x0c\x12\x1d\n\x15\x61\x66_lock_refund_tx_sig\x18\x03 \x01(\x0c\"f\n\x18XmrBidLockSpendTxMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x17\n\x0f\x61_lock_spend_tx\x18\x02 \x01(\x0c\x12\x1d\n\x15\x61l_lock_spend_tx_esig\x18\x03 \x01(\x0c\"?\n\x13XmrBidSecretMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x14\n\x0csecret_value\x18\x02 \x01(\x0c\x62\x06proto3'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -643,6 +643,45 @@ _XMRBIDLOCKSPENDTXMESSAGE = _descriptor.Descriptor(
 | 
			
		||||
  serialized_end=1481,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_XMRBIDSECRETMESSAGE = _descriptor.Descriptor(
 | 
			
		||||
  name='XmrBidSecretMessage',
 | 
			
		||||
  full_name='basicswap.XmrBidSecretMessage',
 | 
			
		||||
  filename=None,
 | 
			
		||||
  file=DESCRIPTOR,
 | 
			
		||||
  containing_type=None,
 | 
			
		||||
  create_key=_descriptor._internal_create_key,
 | 
			
		||||
  fields=[
 | 
			
		||||
    _descriptor.FieldDescriptor(
 | 
			
		||||
      name='bid_msg_id', full_name='basicswap.XmrBidSecretMessage.bid_msg_id', index=0,
 | 
			
		||||
      number=1, type=12, cpp_type=9, label=1,
 | 
			
		||||
      has_default_value=False, default_value=b"",
 | 
			
		||||
      message_type=None, enum_type=None, containing_type=None,
 | 
			
		||||
      is_extension=False, extension_scope=None,
 | 
			
		||||
      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
 | 
			
		||||
    _descriptor.FieldDescriptor(
 | 
			
		||||
      name='secret_value', full_name='basicswap.XmrBidSecretMessage.secret_value', index=1,
 | 
			
		||||
      number=2, type=12, cpp_type=9, label=1,
 | 
			
		||||
      has_default_value=False, default_value=b"",
 | 
			
		||||
      message_type=None, enum_type=None, containing_type=None,
 | 
			
		||||
      is_extension=False, extension_scope=None,
 | 
			
		||||
      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
 | 
			
		||||
  ],
 | 
			
		||||
  extensions=[
 | 
			
		||||
  ],
 | 
			
		||||
  nested_types=[],
 | 
			
		||||
  enum_types=[
 | 
			
		||||
  ],
 | 
			
		||||
  serialized_options=None,
 | 
			
		||||
  is_extendable=False,
 | 
			
		||||
  syntax='proto3',
 | 
			
		||||
  extension_ranges=[],
 | 
			
		||||
  oneofs=[
 | 
			
		||||
  ],
 | 
			
		||||
  serialized_start=1483,
 | 
			
		||||
  serialized_end=1546,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_OFFERMESSAGE.fields_by_name['lock_type'].enum_type = _OFFERMESSAGE_LOCKTYPE
 | 
			
		||||
_OFFERMESSAGE_LOCKTYPE.containing_type = _OFFERMESSAGE
 | 
			
		||||
DESCRIPTOR.message_types_by_name['OfferMessage'] = _OFFERMESSAGE
 | 
			
		||||
@ -653,6 +692,7 @@ DESCRIPTOR.message_types_by_name['XmrSplitMessage'] = _XMRSPLITMESSAGE
 | 
			
		||||
DESCRIPTOR.message_types_by_name['XmrBidAcceptMessage'] = _XMRBIDACCEPTMESSAGE
 | 
			
		||||
DESCRIPTOR.message_types_by_name['XmrBidLockTxSigsMessage'] = _XMRBIDLOCKTXSIGSMESSAGE
 | 
			
		||||
DESCRIPTOR.message_types_by_name['XmrBidLockSpendTxMessage'] = _XMRBIDLOCKSPENDTXMESSAGE
 | 
			
		||||
DESCRIPTOR.message_types_by_name['XmrBidSecretMessage'] = _XMRBIDSECRETMESSAGE
 | 
			
		||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
 | 
			
		||||
 | 
			
		||||
OfferMessage = _reflection.GeneratedProtocolMessageType('OfferMessage', (_message.Message,), {
 | 
			
		||||
@ -711,5 +751,12 @@ XmrBidLockSpendTxMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockS
 | 
			
		||||
  })
 | 
			
		||||
_sym_db.RegisterMessage(XmrBidLockSpendTxMessage)
 | 
			
		||||
 | 
			
		||||
XmrBidSecretMessage = _reflection.GeneratedProtocolMessageType('XmrBidSecretMessage', (_message.Message,), {
 | 
			
		||||
  'DESCRIPTOR' : _XMRBIDSECRETMESSAGE,
 | 
			
		||||
  '__module__' : 'messages_pb2'
 | 
			
		||||
  # @@protoc_insertion_point(class_scope:basicswap.XmrBidSecretMessage)
 | 
			
		||||
  })
 | 
			
		||||
_sym_db.RegisterMessage(XmrBidSecretMessage)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# @@protoc_insertion_point(module_scope)
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import xmrswap.contrib.Keccak as Keccak
 | 
			
		||||
import basicswap.contrib.Keccak as Keccak
 | 
			
		||||
from .contrib.MoneroPy.base58 import encode as xmr_b58encode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -271,7 +271,7 @@ class Test(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        waitForRPC(btcRpc)
 | 
			
		||||
        cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
 | 
			
		||||
        logging.info('Mining %d bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
			
		||||
        logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
			
		||||
        btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
 | 
			
		||||
 | 
			
		||||
        ro = btcRpc('getblockchaininfo')
 | 
			
		||||
 | 
			
		||||
@ -175,7 +175,7 @@ class Test(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            num_blocks = 500
 | 
			
		||||
            btc_addr = btcRpc(1, 'getnewaddress mining_addr bech32')
 | 
			
		||||
            logging.info('Mining %d bitcoin blocks to %s', num_blocks, btc_addr)
 | 
			
		||||
            logging.info('Mining %d Bitcoin blocks to %s', num_blocks, btc_addr)
 | 
			
		||||
            btcRpc(1, 'generatetoaddress {} {}'.format(num_blocks, btc_addr))
 | 
			
		||||
 | 
			
		||||
            for i in range(20):
 | 
			
		||||
 | 
			
		||||
@ -271,7 +271,7 @@ class Test(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        waitForRPC(btcRpc)
 | 
			
		||||
        cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
 | 
			
		||||
        logging.info('Mining %d bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
			
		||||
        logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
			
		||||
        btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
 | 
			
		||||
 | 
			
		||||
        ro = btcRpc('getblockchaininfo')
 | 
			
		||||
 | 
			
		||||
@ -67,7 +67,6 @@ from tests.basicswap.common import (
 | 
			
		||||
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
 | 
			
		||||
from bin.basicswap_run import startDaemon
 | 
			
		||||
 | 
			
		||||
from pprint import pprint
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger()
 | 
			
		||||
 | 
			
		||||
@ -223,6 +222,7 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey):
 | 
			
		||||
                'walletrpcport': XMR_BASE_WALLET_RPC_PORT + node_id,
 | 
			
		||||
                'walletrpcuser': 'test' + str(node_id),
 | 
			
		||||
                'walletrpcpassword': 'test_pass' + str(node_id),
 | 
			
		||||
                'walletfile': 'testwallet',
 | 
			
		||||
                'datadir': os.path.join(datadir, 'xmr_' + str(node_id)),
 | 
			
		||||
                'bindir': cfg.XMR_BINDIR,
 | 
			
		||||
            },
 | 
			
		||||
@ -303,18 +303,21 @@ def callnoderpc(node_id, method, params=[], wallet=None, base_rpc_port=BASE_RPC_
 | 
			
		||||
    auth = 'test{0}:test_pass{0}'.format(node_id)
 | 
			
		||||
    return callrpc(base_rpc_port + node_id, auth, method, params, wallet)
 | 
			
		||||
 | 
			
		||||
def run_coins_loop(cls):
 | 
			
		||||
    global stop_test
 | 
			
		||||
    while not stop_test:
 | 
			
		||||
        if cls.btc_addr is not None:
 | 
			
		||||
            btcRpc('generatetoaddress 1 {}'.format(cls.btc_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})
 | 
			
		||||
        time.sleep(1.0)
 | 
			
		||||
 | 
			
		||||
def run_loop(cls):
 | 
			
		||||
    global stop_test
 | 
			
		||||
    while not stop_test:
 | 
			
		||||
        for c in cls.swap_clients:
 | 
			
		||||
            c.update()
 | 
			
		||||
 | 
			
		||||
        if cls.btc_addr is not None:
 | 
			
		||||
            btcRpc('generatetoaddress 1 {}'.format(cls.btc_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})
 | 
			
		||||
        time.sleep(1.0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -325,6 +328,7 @@ class Test(unittest.TestCase):
 | 
			
		||||
        super(Test, cls).setUpClass()
 | 
			
		||||
 | 
			
		||||
        cls.update_thread = None
 | 
			
		||||
        cls.coins_update_thread = None
 | 
			
		||||
        cls.http_threads = []
 | 
			
		||||
        cls.swap_clients = []
 | 
			
		||||
        cls.part_daemons = []
 | 
			
		||||
@ -425,7 +429,7 @@ class Test(unittest.TestCase):
 | 
			
		||||
            cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
 | 
			
		||||
 | 
			
		||||
            num_blocks = 500
 | 
			
		||||
            logging.info('Mining %d bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
			
		||||
            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))
 | 
			
		||||
@ -440,6 +444,9 @@ class Test(unittest.TestCase):
 | 
			
		||||
            signal.signal(signal.SIGINT, signal_handler)
 | 
			
		||||
            cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
 | 
			
		||||
            cls.update_thread.start()
 | 
			
		||||
 | 
			
		||||
            cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,))
 | 
			
		||||
            cls.coins_update_thread.start()
 | 
			
		||||
        except Exception:
 | 
			
		||||
            traceback.print_exc()
 | 
			
		||||
            Test.tearDownClass()
 | 
			
		||||
@ -455,7 +462,11 @@ class Test(unittest.TestCase):
 | 
			
		||||
                cls.update_thread.join()
 | 
			
		||||
            except Exception:
 | 
			
		||||
                logging.info('Failed to join update_thread')
 | 
			
		||||
        cls.update_thread = None
 | 
			
		||||
        if cls.coins_update_thread is not None:
 | 
			
		||||
            try:
 | 
			
		||||
                cls.coins_update_thread.join()
 | 
			
		||||
            except Exception:
 | 
			
		||||
                logging.info('Failed to join coins_update_thread')
 | 
			
		||||
 | 
			
		||||
        for t in cls.http_threads:
 | 
			
		||||
            t.stop()
 | 
			
		||||
@ -541,7 +552,6 @@ class Test(unittest.TestCase):
 | 
			
		||||
        offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
 | 
			
		||||
        assert(len(offers) == 1)
 | 
			
		||||
        offer = offers[0]
 | 
			
		||||
        pprint(vars(offer))
 | 
			
		||||
 | 
			
		||||
        bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
 | 
			
		||||
 | 
			
		||||
@ -552,13 +562,15 @@ class Test(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        swap_clients[0].acceptXmrBid(bid_id)
 | 
			
		||||
 | 
			
		||||
        self.wait_for_bid(swap_clients[1], bid_id, BidStates.BID_ACCEPTED, sent=True, wait_for=40)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, sent=True)
 | 
			
		||||
 | 
			
		||||
        #self.wait_for_bid(swap_clients[1], bid_id, BidStates.BID_ACCEPTED, sent=True, wait_for=40)
 | 
			
		||||
        #self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED)
 | 
			
		||||
        #self.wait_for_bid(swap_clients[1], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, wait_for=40, sent=True)
 | 
			
		||||
 | 
			
		||||
        #self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED, wait_for=80)
 | 
			
		||||
        #self.wait_for_bid(swap_clients[1], bid_id, BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED, sent=True)
 | 
			
		||||
 | 
			
		||||
        self.wait_for_bid(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
 | 
			
		||||
        self.wait_for_bid(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user