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