diff --git a/basicswap/base.py b/basicswap/base.py index 48d40d2..6052164 100644 --- a/basicswap/base.py +++ b/basicswap/base.py @@ -36,7 +36,7 @@ class BaseApp: self.settings = settings self.coin_clients = {} self.mxDB = threading.RLock() - self.debug = self.settings.get('debug', cfg.DEBUG) + self.debug = self.settings.get('debug', False) self.prepareLogging() self.log.info('Network: {}'.format(self.chain)) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index d5ab3b4..26d89c9 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2019 tecnovert +# Copyright (c) 2019-2020 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. @@ -57,6 +57,8 @@ from .messages_pb2 import ( XmrBidMessage, XmrBidAcceptMessage, XmrSplitMessage, + XmrBidLockTxSigsMessage, + XmrBidLockSpendTxMessage, ) from .db import ( CURRENT_DB_VERSION, @@ -95,6 +97,7 @@ class MessageTypes(IntEnum): XMR_BID = auto() XMR_BID_SPLIT = auto() XMR_BID_ACCEPT = auto() + XMR_BID_TXN_SIGS_FL = auto() class SwapTypes(IntEnum): @@ -120,6 +123,8 @@ class BidStates(IntEnum): SWAP_INITIATED = auto() # Initiate txn validated SWAP_PARTICIPATING = auto() # Participate txn validated SWAP_COMPLETED = auto() # All swap txns spent + XMR_SWAP_SCRIPT_COIN_LOCKED = auto() + SWAP_DELAYING = auto() SWAP_TIMEDOUT = auto() BID_ABANDONED = auto() # Bid will no longer be processed BID_ERROR = auto() # An error occurred @@ -141,9 +146,15 @@ class TxTypes(IntEnum): PTX_REDEEM = auto() PTX_REFUND = auto() + XMR_SWAP_A_LOCK = auto() + XMR_SWAP_A_LOCK_REFUND = auto() + XMR_SWAP_A_LOCK_REFUND_SPEND = auto() + class EventTypes(IntEnum): ACCEPT_BID = auto() + SIGN_XMR_SWAP_LOCK_TX_A = auto() + SEND_XMR_SWAP_LOCK_TX_A = auto() class XmrSplitMsgTypes(IntEnum): @@ -467,15 +478,15 @@ class BasicSwap(BaseApp): def createInterface(self, coin): if coin == Coins.PART: - return PARTInterface(self.coin_clients[coin]) + return PARTInterface(self.coin_clients[coin], self.chain) elif coin == Coins.BTC: - return BTCInterface(self.coin_clients[coin]) + return BTCInterface(self.coin_clients[coin], self.chain) elif coin == Coins.LTC: - return LTCInterface(self.coin_clients[coin]) + return LTCInterface(self.coin_clients[coin], self.chain) elif coin == Coins.NMC: - return NMCInterface(self.coin_clients[coin]) + return NMCInterface(self.coin_clients[coin], self.chain) elif coin == Coins.XMR: - return XMRInterface(self.coin_clients[coin]) + return XMRInterface(self.coin_clients[coin], self.chain) else: raise ValueError('Unknown coin type') @@ -1087,18 +1098,22 @@ class BasicSwap(BaseApp): finally: self.mxDB.release() - def createEvent(self, delay, event_type, linked_id): + def createEventInSession(self, delay, event_type, linked_id, session): self.log.debug('createEvent %d %s', event_type, linked_id.hex()) + event = EventQueue() + event.active_ind = 1 + event.created_at = int(time.time()) + event.trigger_at = event.created_at + delay + event.event_type = event_type + event.linked_id = linked_id + session.add(event) + + def createEvent(self, delay, event_type, linked_id): + #self.log.debug('createEvent %d %s', event_type, linked_id.hex()) self.mxDB.acquire() try: session = scoped_session(self.session_factory) - event = EventQueue() - event.active_ind = 1 - event.created_at = int(time.time()) - event.trigger_at = event.created_at + delay - event.event_type = event_type - event.linked_id = linked_id - session.add(event) + self.createEventInSession(delay, event_type, linked_id, session) session.commit() session.close() session.remove() @@ -1351,11 +1366,6 @@ class BasicSwap(BaseApp): assert(xmr_offer), 'XMR offer not found: {}.'.format(offer_id.hex()) assert(offer.expire_at > int(time.time())), 'Offer has expired' - msg_buf = XmrBidMessage() - msg_buf.offer_msg_id = offer_id - msg_buf.time_valid = 60 * 10 - msg_buf.amount = int(amount) # amount of coin_from - coin_from = Coins(offer.coin_from) coin_to = Coins(offer.coin_to) ci_from = self.ci(coin_from) @@ -1363,6 +1373,14 @@ class BasicSwap(BaseApp): self.checkSynced(coin_from, coin_to) + msg_buf = XmrBidMessage() + msg_buf.offer_msg_id = offer_id + msg_buf.time_valid = 60 * 10 + msg_buf.amount = int(amount) # amount of coin_from + + address_out = self.getReceiveAddressFromPool(coin_from, offer_id, TxTypes.XMR_SWAP_A_LOCK) + msg_buf.dest_af = ci_from.decodeAddress(address_out) + bid_created_at = int(time.time()) if offer.swap_type != SwapTypes.XMR_SWAP: raise ValueError('TODO') @@ -1370,6 +1388,7 @@ class BasicSwap(BaseApp): # Follower to leader xmr_swap = XmrSwap() xmr_swap.contract_count = self.getNewContractId() + xmr_swap.dest_af = msg_buf.dest_af xmr_swap.b_restore_height = self.ci(coin_to).getChainHeight() kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_xmr=True) kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_xmr=True) @@ -1580,7 +1599,8 @@ class BasicSwap(BaseApp): session.close() session.remove() - self.swaps_in_progress[bid_id] = (bid, offer) + #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()) return bid_id finally: @@ -1625,6 +1645,7 @@ class BasicSwap(BaseApp): self.mxDB.release() def setBidError(self, bid_id, bid, error_str): + self.log.error('Bid %s - Error: %s', bid_id.hex(), error_str) bid.setState(BidStates.BID_ERROR) bid.state_note = 'error msg: ' + error_str self.saveBid(bid_id, bid) @@ -2435,6 +2456,10 @@ class BasicSwap(BaseApp): for row in q: if row.event_type == EventTypes.ACCEPT_BID: self.acceptBid(row.linked_id) + elif row.event_type == EventTypes.SIGN_XMR_SWAP_LOCK_TX_A: + self.sendXmrBidTxnSigsFtoL(row.linked_id, session) + elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_A: + self.sendXmrBidCoinALockTx(row.linked_id, session) else: self.log.warning('Unknown event type: %d', row.event_type) @@ -2459,9 +2484,9 @@ class BasicSwap(BaseApp): if num_segments > 1: try: self.receiveXmrBid(bid, session) - except Exception as e: - self.log.info('Verify xmr bid {} failed: {}'.format(bid.bid_id.hex(), str(e))) - bid.setState(BidStates.BID_ERROR, 'Failed validation: ' + str(e)) + except Exception as ex: + self.log.info('Verify xmr bid {} failed: {}'.format(bid.bid_id.hex(), str(ex))) + bid.setState(BidStates.BID_ERROR, 'Failed validation: ' + str(ex)) session.add(bid) continue if bid.created_at + ttl_xmr_split_messages < now: @@ -2476,9 +2501,9 @@ class BasicSwap(BaseApp): if num_segments > 1: try: self.receiveXmrBidAccept(bid, session) - except Exception as e: - self.log.info('Verify xmr bid accept {} failed: {}'.format(bid.bid_id.hex(), str(e))) - bid.setState(BidStates.BID_ERROR, 'Failed accept validation: ' + str(e)) + except Exception as ex: + self.log.info('Verify xmr bid accept {} failed: {}'.format(bid.bid_id.hex(), str(ex))) + bid.setState(BidStates.BID_ERROR, 'Failed accept validation: ' + str(ex)) session.add(bid) continue if bid.created_at + ttl_xmr_split_messages < now: @@ -2489,6 +2514,7 @@ class BasicSwap(BaseApp): # Expire old records q = session.query(XmrSplitData).filter(XmrSplitData.created_at + ttl_xmr_split_messages < now) q.delete(synchronize_session=False) + session.commit() session.close() session.remove() @@ -2659,7 +2685,6 @@ class BasicSwap(BaseApp): else: delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept) self.log.info('Auto accepting bid %s in %d seconds', bid_id.hex(), delay) - self.createEvent(delay, EventTypes.ACCEPT_BID, bid_id) def processBidAccept(self, msg): @@ -2760,6 +2785,14 @@ class BasicSwap(BaseApp): if not ci_to.verifyDLEAG(xmr_swap.kbsf_dleag): raise ValueError('Invalid DLEAG proof.') + # Extract pubkeys from MSG1L DLEAG + xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33] + if not ci_from.verifyPubkey(xmr_swap.pkasf): + raise ValueError('Invalid coin a pubkey.') + xmr_swap.pkbsf = xmr_swap.kbsf_dleag[33: 33 + 32] + if not ci_to.verifyPubkey(xmr_swap.pkbsf): + raise ValueError('Invalid coin b pubkey.') + if not ci_from.verifyPubkey(xmr_swap.pkaf): raise ValueError('Invalid pubkey.') if not ci_from.verifyPubkey(xmr_swap.pkarf): @@ -2794,14 +2827,27 @@ class BasicSwap(BaseApp): if not ci_to.verifyDLEAG(xmr_swap.kbsl_dleag): raise ValueError('Invalid DLEAG proof.') + # Extract pubkeys from MSG1F DLEAG + xmr_swap.pkasl = xmr_swap.kbsl_dleag[0: 33] + if not ci_from.verifyPubkey(xmr_swap.pkasl): + raise ValueError('Invalid coin a pubkey.') + xmr_swap.pkbsl = xmr_swap.kbsl_dleag[33: 33 + 32] + if not ci_to.verifyPubkey(xmr_swap.pkbsl): + raise ValueError('Invalid coin b pubkey.') + if not ci_from.verifyPubkey(xmr_swap.pkal): raise ValueError('Invalid pubkey.') if not ci_from.verifyPubkey(xmr_swap.pkarl): raise ValueError('Invalid pubkey.') - bid.setState(BidStates.BID_ACCEPTED) + #bid.setState(BidStates.BID_ACCEPTED) + bid.setState(BidStates.SWAP_DELAYING) self.saveBidInSession(bid.bid_id, bid, session, xmr_swap) + delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept) + self.log.info('Responding to xmr bid accept %s in %d seconds', bid.bid_id.hex(), delay) + self.createEventInSession(delay, EventTypes.SIGN_XMR_SWAP_LOCK_TX_A, bid.bid_id, session) + def processXmrBid(self, msg): self.log.debug('Processing xmr bid msg %s', msg['msgid']) now = int(time.time()) @@ -2841,6 +2887,7 @@ class BasicSwap(BaseApp): xmr_swap = XmrSwap( bid_id=bid_id, + dest_af=bid_data.dest_af, pkaf=bid_data.pkaf, pkarf=bid_data.pkarf, vkbvf=bid_data.kbvf, @@ -2858,7 +2905,7 @@ class BasicSwap(BaseApp): self.saveBid(bid_id, bid, xmr_swap=xmr_swap) def processXmrBidAccept(self, msg): - # F receiving MSG1F + # F receiving MSG1F and MSG2F self.log.debug('Processing xmr bid accept msg %s', msg['msgid']) now = int(time.time()) msg_bytes = bytes.fromhex(msg['hex'][2:-2]) @@ -2869,23 +2916,192 @@ class BasicSwap(BaseApp): self.log.debug('for bid %s', msg_data.bid_msg_id.hex()) bid, xmr_swap = self.getXmrBid(msg_data.bid_msg_id) + assert(bid), 'Bid not found: {}.'.format(msg_data.bid_id.hex()) + assert(xmr_swap), 'XMR swap not found: {}.'.format(msg_data.bid_id.hex()) offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=True) 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) - assert(len(msg_data.sh) == 32), 'Bad secret hash length' + try: + assert(len(msg_data.sh) == 32), 'Bad secret hash length' - xmr_swap.sh = msg_data.sh - xmr_swap.pkal = msg_data.pkal - xmr_swap.pkarl = msg_data.pkarl - xmr_swap.vkbvl = msg_data.kbvl - xmr_swap.kbsl_dleag = msg_data.kbsl_dleag + xmr_swap.sh = msg_data.sh + xmr_swap.pkal = msg_data.pkal + xmr_swap.pkarl = msg_data.pkarl + xmr_swap.vkbvl = msg_data.kbvl + xmr_swap.kbsl_dleag = msg_data.kbsl_dleag - bid.setState(BidStates.BID_RECEIVING_ACC) - self.saveBid(msg_data.bid_msg_id, bid, xmr_swap=xmr_swap) + xmr_swap.a_lock_tx = msg_data.a_lock_tx + xmr_swap.a_lock_tx_script = msg_data.a_lock_tx_script + xmr_swap.a_lock_refund_tx = msg_data.a_lock_refund_tx + xmr_swap.a_lock_refund_tx_script = msg_data.a_lock_refund_tx_script + xmr_swap.a_lock_refund_spend_tx = msg_data.a_lock_refund_spend_tx + xmr_swap.al_lock_refund_tx_sig = msg_data.al_lock_refund_tx_sig + + check_a_lock_tx_inputs = True + xmr_swap.a_lock_tx_id, lock_tx_vout = ci_from.verifyLockTx( + xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script, + bid.amount, + xmr_swap.sh, + xmr_swap.pkal, xmr_swap.pkaf, + xmr_offer.lock_time_1, xmr_offer.a_fee_rate, + 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) + + 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, + xmr_swap.a_lock_tx_id, lock_tx_vout, xmr_offer.lock_time_1, xmr_swap.a_lock_tx_script, + xmr_swap.pkarl, xmr_swap.pkarf, + xmr_offer.lock_time_2, + xmr_swap.pkaf, + bid.amount, xmr_offer.a_fee_rate + ) + + ci_from.verifyLockRefundSpendTx( + xmr_swap.a_lock_refund_spend_tx, + lock_refund_tx_id, xmr_swap.a_lock_refund_tx_script, + xmr_swap.pkal, + xmr_swap.a_swap_refund_value, xmr_offer.a_fee_rate + ) + + logging.info('Checking leader\'s lock refund tx signature') + v = ci_from.verifyTxSig(xmr_swap.a_lock_refund_tx, xmr_swap.al_lock_refund_tx_sig, xmr_swap.pkarl, 0, xmr_swap.a_lock_tx_script, bid.amount) + + bid.setState(BidStates.BID_RECEIVING_ACC) + self.saveBid(bid.bid_id, bid, xmr_swap=xmr_swap) + except Exception as ex: + if self.debug: + traceback.print_exc() + self.setBidError(bid.bid_id, bid, str(ex)) + + def sendXmrBidTxnSigsFtoL(self, bid_id, session): + # F -> L: Sending MSG3L + self.log.debug('Signing xmr bid lock txns %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) + + 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) + + msg_buf = XmrBidLockTxSigsMessage( + bid_msg_id=bid_id, + af_lock_refund_spend_tx_esig=xmr_swap.af_lock_refund_spend_tx_esig, + 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() + + 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', [bid.bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options]) + xmr_swap.coin_a_lock_tx_sigs_l_id = bytes.fromhex(ro['msgid']) + + self.log.info('Sent XMR_BID_TXN_SIGS_FL %s', xmr_swap.coin_a_lock_tx_sigs_l_id.hex()) + + bid.setState(BidStates.BID_ACCEPTED) + session.add(bid) + session.add(xmr_swap) + + self.swaps_in_progress[bid_id] = (bid, offer) + except Exception as ex: + if self.debug: + traceback.print_exc() + + def sendXmrBidCoinALockTx(self, bid_id, session): + # Send coin A lock tx and MSG4F L -> F + self.log.debug('Sending 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) + + kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4) + + xmr_swap.a_lock_spend_tx = ci_from.createScriptLockSpendTx( + xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script, + xmr_swap.dest_af, + xmr_offer.a_fee_rate) + + 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 + + # TODO: Separate MSG4F and txn sending + + + def processXmrBidCoinALockSigs(self, msg): + # Follower processing MSG3L + self.log.debug('Processing xmr coin a follower lock sigs msg %s', msg['msgid']) + now = int(time.time()) + msg_bytes = bytes.fromhex(msg['hex'][2:-2]) + msg_data = XmrBidLockTxSigsMessage() + 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.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig + xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig + + 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' + + delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept) + self.log.info('Sending coin A lock tx for xmr bid %s in %d seconds', bid_id.hex(), delay) + self.createEvent(delay, EventTypes.SEND_XMR_SWAP_LOCK_TX_A, bid_id) + + bid.setState(BidStates.SWAP_DELAYING) + 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)) def processXmrSplitMessage(self, msg): self.log.debug('Processing xmr split msg %s', msg['msgid']) @@ -2895,7 +3111,6 @@ class BasicSwap(BaseApp): msg_data.ParseFromString(msg_bytes) # Validate data - print('[rm] msg_data.msg_id', msg_data.msg_id.hex()) assert(len(msg_data.msg_id) == 28), 'Bad msg_id length' if msg_data.msg_type == XmrSplitMsgTypes.BID or msg_data.msg_type == XmrSplitMsgTypes.BID_ACCEPT: @@ -2923,12 +3138,15 @@ class BasicSwap(BaseApp): self.processXmrBid(msg) elif msg_type == MessageTypes.XMR_BID_ACCEPT: self.processXmrBidAccept(msg) + elif msg_type == MessageTypes.XMR_BID_TXN_SIGS_FL: + self.processXmrBidCoinALockSigs(msg) elif msg_type == MessageTypes.XMR_BID_SPLIT: self.processXmrSplitMessage(msg) except Exception as ex: self.log.error('processMsg %s', str(ex)) - traceback.print_exc() + if self.debug: + traceback.print_exc() finally: self.mxDB.release() @@ -2954,7 +3172,8 @@ class BasicSwap(BaseApp): pass except Exception as ex: self.log.error('smsg zmq %s', str(ex)) - traceback.print_exc() + if self.debug: + traceback.print_exc() self.mxDB.acquire() try: @@ -2968,7 +3187,8 @@ class BasicSwap(BaseApp): to_remove.append(bid_id) except Exception as ex: self.log.error('checkBidState %s %s', bid_id.hex(), str(ex)) - traceback.print_exc() + if self.debug: + traceback.print_exc() self.setBidError(bid_id, v[0], str(ex)) for bid_id in to_remove: @@ -2996,7 +3216,8 @@ class BasicSwap(BaseApp): except Exception as ex: self.log.error('update %s', str(ex)) - traceback.print_exc() + if self.debug: + traceback.print_exc() finally: self.mxDB.release() diff --git a/basicswap/config.py b/basicswap/config.py index 0838f05..dc93626 100644 --- a/basicswap/config.py +++ b/basicswap/config.py @@ -6,8 +6,6 @@ import os -DEBUG = True - CONFIG_FILENAME = 'basicswap.json' DEFAULT_DATADIR = '~/.basicswap' TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap')) diff --git a/basicswap/db.py b/basicswap/db.py index a0a4cb5..b34eaa0 100644 --- a/basicswap/db.py +++ b/basicswap/db.py @@ -228,15 +228,21 @@ class XmrSwap(Base): bid_accept_msg_id2 = sa.Column(sa.LargeBinary) bid_accept_msg_id3 = sa.Column(sa.LargeBinary) + coin_a_lock_tx_sigs_l_id = sa.Column(sa.LargeBinary) # MSG3L F -> L + contract_count = sa.Column(sa.Integer) sh = sa.Column(sa.LargeBinary) # Secret hash + dest_af = sa.Column(sa.LargeBinary) # Destination for coin A amount to follower when swap completes successfully + pkal = sa.Column(sa.LargeBinary) pkarl = sa.Column(sa.LargeBinary) + pkasl = sa.Column(sa.LargeBinary) pkaf = sa.Column(sa.LargeBinary) pkarf = sa.Column(sa.LargeBinary) + pkasf = sa.Column(sa.LargeBinary) vkbvl = sa.Column(sa.LargeBinary) vkbsl = sa.Column(sa.LargeBinary) @@ -260,6 +266,13 @@ class XmrSwap(Base): a_lock_refund_spend_tx = sa.Column(sa.LargeBinary) + af_lock_refund_spend_tx_esig = sa.Column(sa.LargeBinary) + af_lock_refund_spend_tx_sig = sa.Column(sa.LargeBinary) + af_lock_refund_tx_sig = sa.Column(sa.LargeBinary) + + a_lock_spend_tx = sa.Column(sa.LargeBinary) + al_lock_spend_tx_esig = sa.Column(sa.LargeBinary) + b_restore_height = sa.Column(sa.Integer) # Height of xmr chain before the swap diff --git a/basicswap/interface_btc.py b/basicswap/interface_btc.py index 845e040..d2d46e6 100644 --- a/basicswap/interface_btc.py +++ b/basicswap/interface_btc.py @@ -9,18 +9,25 @@ import time import hashlib import logging from io import BytesIO +from basicswap.contrib.test_framework import segwit_addr + from .util import ( decodeScriptNum, getCompactSizeLen, dumpj, format_amount, - make_int -) + make_int, + decodeAddress) from coincurve.keys import ( PublicKey) from coincurve.dleag import ( verify_secp256k1_point) +from coincurve.ecdsaotves import ( + ecdsaotves_enc_sign, + ecdsaotves_enc_verify, + ecdsaotves_dec_sig, + ecdsaotves_rec_enc_key) from .ecc_util import ( G, ep, @@ -59,7 +66,7 @@ from .contrib.test_framework.script import ( from .contrib.test_framework.key import ECKey, ECPubKey -from .chainparams import CoinInterface +from .chainparams import CoinInterface, Coins, chainparams from .rpc import make_rpc_func from .util import assert_cond @@ -72,6 +79,10 @@ def findOutput(tx, script_pk): class BTCInterface(CoinInterface): + @staticmethod + def coin_type(): + return Coins.BTC + @staticmethod def exp(): return 8 @@ -102,9 +113,10 @@ class BTCInterface(CoinInterface): def compareFeeRates(self, a, b): return abs(a - b) < 20 - def __init__(self, coin_settings): + def __init__(self, coin_settings, network): self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth']) self.txoType = CTxOut + self._network = network def testDaemonRPC(self): self.rpc_callback('getwalletinfo', []) @@ -124,6 +136,13 @@ class BTCInterface(CoinInterface): args.append('bech32') return self.rpc_callback('getnewaddress', args) + def decodeAddress(self, address): + bech32_prefix = chainparams[self.coin_type()][self._network]['hrp'] + if address.startswith(bech32_prefix): + ignr, pkhash = segwit_addr.decode(bech32_prefix, address) + return pkhash + return decodeAddress(address)[1:] + def getNewSecretKey(self): return getSecretInt() @@ -379,8 +398,8 @@ class BTCInterface(CoinInterface): return tx.serialize() - def createScriptLockSpendTx(self, tx_lock, script_lock, pkh_dest, tx_fee_rate): - + def createScriptLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate): + tx_lock = self.loadTx(tx_lock_bytes) output_script = CScript([OP_0, hashlib.sha256(script_lock).digest()]) locked_n = findOutput(tx_lock, output_script) assert_cond(locked_n is not None, 'Output not found in tx') @@ -411,7 +430,7 @@ class BTCInterface(CoinInterface): return tx.serialize() - def verifyLockTx(self, tx, script_out, + def verifyLockTx(self, tx_bytes, script_out, swap_value, sh, Kal, Kaf, @@ -425,6 +444,7 @@ class BTCInterface(CoinInterface): # However by checking early we can avoid wasting time processing unmineable txns # Check fee is reasonable + tx = self.loadTx(tx_bytes) tx_hash = self.getTxHash(tx) logging.info('Verifying lock tx: {}.'.format(b2h(tx_hash))) @@ -441,11 +461,11 @@ class BTCInterface(CoinInterface): # Check script and values shv, A, B, csv_val, C, D = self.extractScriptLockScriptValues(script_out) assert_cond(shv == sh, 'Bad hash lock') - assert_cond(A == self.encodePubkey(Kal), 'Bad script pubkey') - assert_cond(B == self.encodePubkey(Kaf), 'Bad script pubkey') + assert_cond(A == Kal, 'Bad script pubkey') + assert_cond(B == Kaf, 'Bad script pubkey') assert_cond(csv_val == lock_value, 'Bad script csv value') - assert_cond(C == self.encodePubkey(Karl), 'Bad script pubkey') - assert_cond(D == self.encodePubkey(Karf), 'Bad script pubkey') + assert_cond(C == Karl, 'Bad script pubkey') + assert_cond(D == Karf, 'Bad script pubkey') if check_lock_tx_inputs: # Check that inputs are unspent and verify fee rate @@ -454,7 +474,6 @@ class BTCInterface(CoinInterface): add_witness_bytes = getCompactSizeLen(len(tx.vin)) for pi in tx.vin: ptx = self.rpc_callback('getrawtransaction', [i2h(pi.prevout.hash), True]) - print('ptx', dumpj(ptx)) prevout = ptx['vout'][pi.prevout.n] inputs_value += make_int(prevout['value']) @@ -483,7 +502,7 @@ class BTCInterface(CoinInterface): return tx_hash, locked_n - def verifyLockRefundTx(self, tx, script_out, + def verifyLockRefundTx(self, tx_bytes, script_out, prevout_id, prevout_n, prevout_seq, prevout_script, Karl, Karf, csv_val_expect, Kaf, swap_value, feerate): # Verify: @@ -491,6 +510,7 @@ class BTCInterface(CoinInterface): # Must have only one output to the p2wsh of the lock refund script # Output value must be locked_coin - lock tx fee + tx = self.loadTx(tx_bytes) tx_hash = self.getTxHash(tx) logging.info('Verifying lock refund tx: {}.'.format(b2h(tx_hash))) @@ -511,10 +531,10 @@ class BTCInterface(CoinInterface): # Check script and values A, B, csv_val, C = self.extractScriptLockRefundScriptValues(script_out) - assert_cond(A == self.encodePubkey(Karl), 'Bad script pubkey') - assert_cond(B == self.encodePubkey(Karf), 'Bad script pubkey') + assert_cond(A == Karl, 'Bad script pubkey') + assert_cond(B == Karf, 'Bad script pubkey') assert_cond(csv_val == csv_val_expect, 'Bad script csv value') - assert_cond(C == self.encodePubkey(Kaf), 'Bad script pubkey') + assert_cond(C == Kaf, 'Bad script pubkey') fee_paid = swap_value - locked_coin assert(fee_paid > 0) @@ -533,13 +553,14 @@ class BTCInterface(CoinInterface): return tx_hash, locked_coin - def verifyLockRefundSpendTx(self, tx, + def verifyLockRefundSpendTx(self, tx_bytes, lock_refund_tx_id, prevout_script, Kal, prevout_value, feerate): # Verify: # Must have only one input with correct prevout (n is always 0) and sequence # Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr + tx = self.loadTx(tx_bytes) tx_hash = self.getTxHash(tx) logging.info('Verifying lock refund spend tx: {}.'.format(b2h(tx_hash))) @@ -553,7 +574,7 @@ class BTCInterface(CoinInterface): assert_cond(len(tx.vout) == 1, 'tx doesn\'t have one output') - p2wpkh = CScript([OP_0, hash160(self.encodePubkey(Kal))]) + p2wpkh = CScript([OP_0, hash160(Kal)]) locked_n = findOutput(tx, p2wpkh) assert_cond(locked_n is not None, 'Output not found in lock refund spend tx') tx_value = tx.vout[locked_n].nValue @@ -632,16 +653,22 @@ class BTCInterface(CoinInterface): return eck.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL - def signTxOtVES(self, key_sign, key_encrypt, tx, prevout_n, prevout_script, prevout_value): + def signTxOtVES(self, key_sign, pubkey_encrypt, tx_bytes, prevout_n, prevout_script, prevout_value): + tx = self.loadTx(tx_bytes) sig_hash = SegwitV0SignatureHash(prevout_script, tx, prevout_n, SIGHASH_ALL, prevout_value) - return otves.EncSign(key_sign, key_encrypt, sig_hash) - def verifyTxOtVES(self, tx, sig, Ks, Ke, prevout_n, prevout_script, prevout_value): + return ecdsaotves_enc_sign(key_sign, pubkey_encrypt, sig_hash) + #return otves.EncSign(key_sign, key_encrypt, sig_hash) + + def verifyTxOtVES(self, tx_bytes, sig, Ks, Ke, prevout_n, prevout_script, prevout_value): + tx = self.loadTx(tx_bytes) sig_hash = SegwitV0SignatureHash(prevout_script, tx, prevout_n, SIGHASH_ALL, prevout_value) - return otves.EncVrfy(Ks, Ke, sig_hash, sig) + return ecdsaotves_enc_verify(Ks, Ke, sig_hash, sig) + #return otves.EncVrfy(Ks, Ke, sig_hash, sig) def decryptOtVES(self, k, esig): - return otves.DecSig(k, esig) + b'\x01' # 0x1 is SIGHASH_ALL + return ecdsaotves_dec_sig(k, esig) + b'\x01' # 0x1 is SIGHASH_ALL + #return otves.DecSig(k, esig) + b'\x01' # 0x1 is SIGHASH_ALL def verifyTxSig(self, tx_bytes, sig, K, prevout_n, prevout_script, prevout_value): tx = self.loadTx(tx_bytes) @@ -675,9 +702,9 @@ class BTCInterface(CoinInterface): tx.deserialize(BytesIO(tx_bytes)) return tx - def getTxHash(self, tx_bytes): - tx = CTransaction() - tx = FromHex(tx, tx_bytes.hex()) + def getTxHash(self, tx): + if isinstance(tx, bytes): + tx = self.loadTx(tx) tx.rehash() return i2b(tx.sha256) diff --git a/basicswap/interface_ltc.py b/basicswap/interface_ltc.py index c052766..31a1e49 100644 --- a/basicswap/interface_ltc.py +++ b/basicswap/interface_ltc.py @@ -6,7 +6,9 @@ # file LICENSE or http://www.opensource.org/licenses/mit-license.php. from .interface_btc import BTCInterface - +from .chainparams import Coins class LTCInterface(BTCInterface): - pass + @staticmethod + def coin_type(): + return Coins.LTC diff --git a/basicswap/interface_nmc.py b/basicswap/interface_nmc.py index c88a6d3..34a5190 100644 --- a/basicswap/interface_nmc.py +++ b/basicswap/interface_nmc.py @@ -6,7 +6,9 @@ # file LICENSE or http://www.opensource.org/licenses/mit-license.php. from .interface_btc import BTCInterface - +from .chainparams import Coins class NMCInterface(BTCInterface): - pass + @staticmethod + def coin_type(): + return Coins.NMC diff --git a/basicswap/interface_part.py b/basicswap/interface_part.py index cef941c..6663070 100644 --- a/basicswap/interface_part.py +++ b/basicswap/interface_part.py @@ -10,11 +10,15 @@ from .contrib.test_framework.messages import ( ) from .interface_btc import BTCInterface -from .chainparams import CoinInterface +from .chainparams import CoinInterface, Coins from .rpc import make_rpc_func class PARTInterface(BTCInterface): + @staticmethod + def coin_type(): + return Coins.PART + @staticmethod def witnessScaleFactor(): return 2 @@ -23,9 +27,10 @@ class PARTInterface(BTCInterface): def txVersion(): return 0xa0 - def __init__(self, coin_settings): + def __init__(self, coin_settings, network): self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth']) self.txoType = CTxOutPart + self._network = network def getNewAddress(self, use_segwit): return self.rpc_callback('getnewaddress', ['swap_receive']) diff --git a/basicswap/interface_xmr.py b/basicswap/interface_xmr.py index f94f33f..9631fa1 100644 --- a/basicswap/interface_xmr.py +++ b/basicswap/interface_xmr.py @@ -25,12 +25,16 @@ from .rpc_xmr import ( make_xmr_wallet_rpc_func) from .ecc_util import ( b2i) -from .chainparams import CoinInterface +from .chainparams import CoinInterface, Coins XMR_COIN = 10 ** 12 class XMRInterface(CoinInterface): + @staticmethod + def coin_type(): + return Coins.XMR + @staticmethod def exp(): return 12 @@ -43,12 +47,13 @@ class XMRInterface(CoinInterface): def nbK(): # No. of bytes requires to encode a public key return 32 - def __init__(self, coin_settings): + def __init__(self, coin_settings, network): rpc_cb = make_xmr_rpc_func(coin_settings['rpcport']) rpc_wallet_cb = make_xmr_wallet_rpc_func(coin_settings['walletrpcport'], coin_settings['walletrpcauth']) self.rpc_cb = rpc_cb self.rpc_wallet_cb = rpc_wallet_cb + self._network = network def testDaemonRPC(self): self.rpc_wallet_cb('get_languages') diff --git a/basicswap/messages.proto b/basicswap/messages.proto index 135436f..f720847 100644 --- a/basicswap/messages.proto +++ b/basicswap/messages.proto @@ -51,9 +51,8 @@ message BidAcceptMessage { } - -/* Step 2, buyer -> seller */ message XmrBidMessage { + /* MSG1L, F -> L */ bytes offer_msg_id = 1; uint64 time_valid = 2; /* seconds bid is valid for */ uint64 amount = 3; /* amount of amount_from bid is for */ @@ -63,6 +62,8 @@ message XmrBidMessage { bytes kbvf = 6; bytes kbsf_dleag = 7; + + bytes dest_af = 8; } message XmrSplitMessage { @@ -72,7 +73,6 @@ message XmrSplitMessage { bytes dleag = 4; } - message XmrBidAcceptMessage { bytes bid_msg_id = 1; @@ -92,3 +92,20 @@ message XmrBidAcceptMessage { bytes al_lock_refund_tx_sig = 12; } +message XmrBidLockTxSigsMessage { + /* MSG3L */ + bytes bid_msg_id = 1; + + bytes af_lock_refund_spend_tx_esig = 2; + bytes af_lock_refund_tx_sig = 3; +} + +message XmrBidLockSpendTxMessage { + /* MSG4F */ + bytes bid_msg_id = 1; + + bytes a_lock_spend_tx = 2; + bytes al_lock_spend_tx_esig = 3; +} + + diff --git a/basicswap/messages_pb2.py b/basicswap/messages_pb2.py index b6b5029..6d81e84 100644 --- a/basicswap/messages_pb2.py +++ b/basicswap/messages_pb2.py @@ -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\"\x88\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\"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\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\x62\x06proto3' ) @@ -366,6 +366,13 @@ _XMRBIDMESSAGE = _descriptor.Descriptor( 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='dest_af', full_name='basicswap.XmrBidMessage.dest_af', index=7, + number=8, 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=[ ], @@ -379,7 +386,7 @@ _XMRBIDMESSAGE = _descriptor.Descriptor( oneofs=[ ], serialized_start=736, - serialized_end=872, + serialized_end=889, ) @@ -431,8 +438,8 @@ _XMRSPLITMESSAGE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=874, - serialized_end=958, + serialized_start=891, + serialized_end=975, ) @@ -540,8 +547,100 @@ _XMRBIDACCEPTMESSAGE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=961, - serialized_end=1244, + serialized_start=978, + serialized_end=1261, +) + + +_XMRBIDLOCKTXSIGSMESSAGE = _descriptor.Descriptor( + name='XmrBidLockTxSigsMessage', + full_name='basicswap.XmrBidLockTxSigsMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='bid_msg_id', full_name='basicswap.XmrBidLockTxSigsMessage.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='af_lock_refund_spend_tx_esig', full_name='basicswap.XmrBidLockTxSigsMessage.af_lock_refund_spend_tx_esig', 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), + _descriptor.FieldDescriptor( + name='af_lock_refund_tx_sig', full_name='basicswap.XmrBidLockTxSigsMessage.af_lock_refund_tx_sig', index=2, + number=3, 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=1263, + serialized_end=1377, +) + + +_XMRBIDLOCKSPENDTXMESSAGE = _descriptor.Descriptor( + name='XmrBidLockSpendTxMessage', + full_name='basicswap.XmrBidLockSpendTxMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='bid_msg_id', full_name='basicswap.XmrBidLockSpendTxMessage.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='a_lock_spend_tx', full_name='basicswap.XmrBidLockSpendTxMessage.a_lock_spend_tx', 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), + _descriptor.FieldDescriptor( + name='al_lock_spend_tx_esig', full_name='basicswap.XmrBidLockSpendTxMessage.al_lock_spend_tx_esig', index=2, + number=3, 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=1379, + serialized_end=1481, ) _OFFERMESSAGE.fields_by_name['lock_type'].enum_type = _OFFERMESSAGE_LOCKTYPE @@ -552,6 +651,8 @@ DESCRIPTOR.message_types_by_name['BidAcceptMessage'] = _BIDACCEPTMESSAGE DESCRIPTOR.message_types_by_name['XmrBidMessage'] = _XMRBIDMESSAGE 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 _sym_db.RegisterFileDescriptor(DESCRIPTOR) OfferMessage = _reflection.GeneratedProtocolMessageType('OfferMessage', (_message.Message,), { @@ -596,5 +697,19 @@ XmrBidAcceptMessage = _reflection.GeneratedProtocolMessageType('XmrBidAcceptMess }) _sym_db.RegisterMessage(XmrBidAcceptMessage) +XmrBidLockTxSigsMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockTxSigsMessage', (_message.Message,), { + 'DESCRIPTOR' : _XMRBIDLOCKTXSIGSMESSAGE, + '__module__' : 'messages_pb2' + # @@protoc_insertion_point(class_scope:basicswap.XmrBidLockTxSigsMessage) + }) +_sym_db.RegisterMessage(XmrBidLockTxSigsMessage) + +XmrBidLockSpendTxMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockSpendTxMessage', (_message.Message,), { + 'DESCRIPTOR' : _XMRBIDLOCKSPENDTXMESSAGE, + '__module__' : 'messages_pb2' + # @@protoc_insertion_point(class_scope:basicswap.XmrBidLockSpendTxMessage) + }) +_sym_db.RegisterMessage(XmrBidLockSpendTxMessage) + # @@protoc_insertion_point(module_scope) diff --git a/tests/basicswap/test_nmc.py b/tests/basicswap/test_nmc.py index 0a11d3d..bfde986 100644 --- a/tests/basicswap/test_nmc.py +++ b/tests/basicswap/test_nmc.py @@ -135,6 +135,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey): btcdatadir = os.path.join(datadir, str(BTC_NODE)) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings = { + 'debug': True, 'zmqhost': 'tcp://127.0.0.1', 'zmqport': BASE_ZMQ_PORT + nodeId, 'htmlhost': 'localhost', diff --git a/tests/basicswap/test_run.py b/tests/basicswap/test_run.py index fa9a7d4..2052c18 100644 --- a/tests/basicswap/test_run.py +++ b/tests/basicswap/test_run.py @@ -139,6 +139,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey): btcdatadir = os.path.join(datadir, str(BTC_NODE)) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings = { + 'debug': True, 'zmqhost': 'tcp://127.0.0.1', 'zmqport': BASE_ZMQ_PORT + nodeId, 'htmlhost': 'localhost', diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index 5d36e5b..f84b90e 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -198,6 +198,7 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey): settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings = { + 'debug': True, 'zmqhost': 'tcp://127.0.0.1', 'zmqport': BASE_ZMQ_PORT + node_id, 'htmlhost': 'localhost', @@ -513,9 +514,9 @@ class Test(unittest.TestCase): return raise ValueError('wait_for_offer timed out.') - def wait_for_bid(self, swap_client, bid_id, state=None, sent=False): + def wait_for_bid(self, swap_client, bid_id, state=None, sent=False, wait_for=20): logging.info('wait_for_bid %s', bid_id.hex()) - for i in range(20): + for i in range(wait_for): time.sleep(1) bids = swap_client.listBids(sent=sent) for bid in bids: @@ -551,7 +552,11 @@ class Test(unittest.TestCase): swap_clients[0].acceptXmrBid(bid_id) - self.wait_for_bid(swap_clients[1], bid_id, BidStates.BID_ACCEPTED, 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, sent=True) +