XMR successful swap works.

2024-05-20_merge
tecnovert 4 years ago
parent eb6bd444c4
commit 0e2011e085
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
  1. 507
      basicswap/basicswap.py
  2. 348
      basicswap/contrib/Keccak.py
  3. 10
      basicswap/db.py
  4. 52
      basicswap/interface_btc.py
  5. 1
      basicswap/interface_part.py
  6. 87
      basicswap/interface_xmr.py
  7. 5
      basicswap/messages.proto
  8. 49
      basicswap/messages_pb2.py
  9. 2
      basicswap/util_xmr.py
  10. 2
      tests/basicswap/test_nmc.py
  11. 2
      tests/basicswap/test_reload.py
  12. 2
      tests/basicswap/test_run.py
  13. 40
      tests/basicswap/test_xmr.py

@ -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,8 +3312,162 @@ 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):
@ -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))

@ -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,13 +303,9 @@ 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_loop(cls):
def run_coins_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))
@ -317,6 +313,13 @@ def run_loop(cls):
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()
time.sleep(1.0)
class Test(unittest.TestCase):
@ -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…
Cancel
Save