XMR successful swap works.
This commit is contained in:
parent
eb6bd444c4
commit
0e2011e085
@ -59,6 +59,7 @@ from .messages_pb2 import (
|
|||||||
XmrSplitMessage,
|
XmrSplitMessage,
|
||||||
XmrBidLockTxSigsMessage,
|
XmrBidLockTxSigsMessage,
|
||||||
XmrBidLockSpendTxMessage,
|
XmrBidLockSpendTxMessage,
|
||||||
|
XmrBidSecretMessage,
|
||||||
)
|
)
|
||||||
from .db import (
|
from .db import (
|
||||||
CURRENT_DB_VERSION,
|
CURRENT_DB_VERSION,
|
||||||
@ -80,6 +81,8 @@ from .db import (
|
|||||||
from .explorers import ExplorerInsight, ExplorerBitAps, ExplorerChainz
|
from .explorers import ExplorerInsight, ExplorerBitAps, ExplorerChainz
|
||||||
import basicswap.config as cfg
|
import basicswap.config as cfg
|
||||||
from .base import BaseApp
|
from .base import BaseApp
|
||||||
|
from .ecc_util import (
|
||||||
|
b2i, i2b)
|
||||||
|
|
||||||
|
|
||||||
MIN_OFFER_VALID_TIME = 60 * 10
|
MIN_OFFER_VALID_TIME = 60 * 10
|
||||||
@ -94,11 +97,12 @@ class MessageTypes(IntEnum):
|
|||||||
BID_ACCEPT = auto()
|
BID_ACCEPT = auto()
|
||||||
|
|
||||||
XMR_OFFER = auto()
|
XMR_OFFER = auto()
|
||||||
XMR_BID = auto()
|
XMR_BID_FL = auto()
|
||||||
XMR_BID_SPLIT = auto()
|
XMR_BID_SPLIT = auto()
|
||||||
XMR_BID_ACCEPT = auto()
|
XMR_BID_ACCEPT_LF = auto()
|
||||||
XMR_BID_TXN_SIGS_FL = 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):
|
class SwapTypes(IntEnum):
|
||||||
@ -125,6 +129,11 @@ class BidStates(IntEnum):
|
|||||||
SWAP_PARTICIPATING = auto() # Participate txn validated
|
SWAP_PARTICIPATING = auto() # Participate txn validated
|
||||||
SWAP_COMPLETED = auto() # All swap txns spent
|
SWAP_COMPLETED = auto() # All swap txns spent
|
||||||
XMR_SWAP_SCRIPT_COIN_LOCKED = auto()
|
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_DELAYING = auto()
|
||||||
SWAP_TIMEDOUT = auto()
|
SWAP_TIMEDOUT = auto()
|
||||||
BID_ABANDONED = auto() # Bid will no longer be processed
|
BID_ABANDONED = auto() # Bid will no longer be processed
|
||||||
@ -148,14 +157,20 @@ class TxTypes(IntEnum):
|
|||||||
PTX_REFUND = auto()
|
PTX_REFUND = auto()
|
||||||
|
|
||||||
XMR_SWAP_A_LOCK = auto()
|
XMR_SWAP_A_LOCK = auto()
|
||||||
|
XMR_SWAP_A_LOCK_SPEND = auto()
|
||||||
XMR_SWAP_A_LOCK_REFUND = auto()
|
XMR_SWAP_A_LOCK_REFUND = auto()
|
||||||
XMR_SWAP_A_LOCK_REFUND_SPEND = auto()
|
XMR_SWAP_A_LOCK_REFUND_SPEND = auto()
|
||||||
|
XMR_SWAP_B_LOCK = auto()
|
||||||
|
|
||||||
|
|
||||||
class EventTypes(IntEnum):
|
class EventTypes(IntEnum):
|
||||||
ACCEPT_BID = auto()
|
ACCEPT_BID = auto()
|
||||||
SIGN_XMR_SWAP_LOCK_TX_A = auto()
|
SIGN_XMR_SWAP_LOCK_TX_A = auto()
|
||||||
SEND_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):
|
class XmrSplitMsgTypes(IntEnum):
|
||||||
@ -487,7 +502,10 @@ class BasicSwap(BaseApp):
|
|||||||
elif coin == Coins.NMC:
|
elif coin == Coins.NMC:
|
||||||
return NMCInterface(self.coin_clients[coin], self.chain)
|
return NMCInterface(self.coin_clients[coin], self.chain)
|
||||||
elif coin == Coins.XMR:
|
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:
|
else:
|
||||||
raise ValueError('Unknown coin type')
|
raise ValueError('Unknown coin type')
|
||||||
|
|
||||||
@ -1076,6 +1094,10 @@ class BasicSwap(BaseApp):
|
|||||||
session.add(bid.participate_tx)
|
session.add(bid.participate_tx)
|
||||||
if bid.xmr_a_lock_tx:
|
if bid.xmr_a_lock_tx:
|
||||||
session.add(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:
|
if xmr_swap is not None:
|
||||||
session.add(xmr_swap)
|
session.add(xmr_swap)
|
||||||
|
|
||||||
@ -1208,6 +1230,20 @@ class BasicSwap(BaseApp):
|
|||||||
session.remove()
|
session.remove()
|
||||||
self.mxDB.release()
|
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):
|
def getXmrBid(self, bid_id, sent=False):
|
||||||
self.mxDB.acquire()
|
self.mxDB.acquire()
|
||||||
try:
|
try:
|
||||||
@ -1216,6 +1252,7 @@ class BasicSwap(BaseApp):
|
|||||||
xmr_swap = None
|
xmr_swap = None
|
||||||
if bid:
|
if bid:
|
||||||
xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid_id).first()
|
xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid_id).first()
|
||||||
|
self.loadBidTxns(bid, session)
|
||||||
return bid, xmr_swap
|
return bid, xmr_swap
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
@ -1242,8 +1279,7 @@ class BasicSwap(BaseApp):
|
|||||||
session = scoped_session(self.session_factory)
|
session = scoped_session(self.session_factory)
|
||||||
bid = session.query(Bid).filter_by(bid_id=bid_id).first()
|
bid = session.query(Bid).filter_by(bid_id=bid_id).first()
|
||||||
if bid:
|
if bid:
|
||||||
bid.initiate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.ITX)).first()
|
self.loadBidTxns(bid, session)
|
||||||
bid.participate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.PTX)).first()
|
|
||||||
return bid
|
return bid
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
@ -1399,10 +1435,16 @@ class BasicSwap(BaseApp):
|
|||||||
kaf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 3)
|
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)
|
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.pkaf = ci_from.getPubkey(kaf)
|
||||||
xmr_swap.pkarf = ci_from.getPubkey(karf)
|
xmr_swap.pkarf = ci_from.getPubkey(karf)
|
||||||
|
|
||||||
xmr_swap.kbsf_dleag = ci_to.proveDLEAG(kbsf)
|
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.pkaf = xmr_swap.pkaf
|
||||||
msg_buf.pkarf = xmr_swap.pkarf
|
msg_buf.pkarf = xmr_swap.pkarf
|
||||||
@ -1410,7 +1452,7 @@ class BasicSwap(BaseApp):
|
|||||||
msg_buf.kbsf_dleag = xmr_swap.kbsf_dleag[:16000]
|
msg_buf.kbsf_dleag = xmr_swap.kbsf_dleag[:16000]
|
||||||
|
|
||||||
bid_bytes = msg_buf.SerializeToString()
|
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:
|
if addr_send_from is None:
|
||||||
bid_addr = self.callrpc('getnewaddress')
|
bid_addr = self.callrpc('getnewaddress')
|
||||||
@ -1469,7 +1511,7 @@ class BasicSwap(BaseApp):
|
|||||||
session.close()
|
session.close()
|
||||||
session.remove()
|
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
|
return xmr_swap.bid_id
|
||||||
finally:
|
finally:
|
||||||
self.mxDB.release()
|
self.mxDB.release()
|
||||||
@ -1496,7 +1538,6 @@ class BasicSwap(BaseApp):
|
|||||||
ci_from = self.ci(coin_from)
|
ci_from = self.ci(coin_from)
|
||||||
ci_to = self.ci(coin_to)
|
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:
|
if xmr_swap.contract_count is None:
|
||||||
xmr_swap.contract_count = self.getNewContractId()
|
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)
|
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)
|
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.sh = hashlib.sha256(contract_secret).digest()
|
||||||
xmr_swap.pkal = ci_from.getPubkey(kal)
|
xmr_swap.pkal = ci_from.getPubkey(kal)
|
||||||
xmr_swap.pkarl = ci_from.getPubkey(karl)
|
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 = 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_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_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,
|
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_buf.al_lock_refund_tx_sig = xmr_swap.al_lock_refund_tx_sig
|
||||||
|
|
||||||
msg_bytes = msg_buf.SerializeToString()
|
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_LF) + msg_bytes.hex()
|
||||||
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_ACCEPT) + msg_bytes.hex()
|
|
||||||
options = {'decodehex': True, 'ttl_is_seconds': True}
|
options = {'decodehex': True, 'ttl_is_seconds': True}
|
||||||
msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
|
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])
|
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)
|
#self.swaps_in_progress[bid_id] = (bid, offer)
|
||||||
# Add to swaps_in_progress only when waiting on txns
|
# 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
|
return bid_id
|
||||||
finally:
|
finally:
|
||||||
self.mxDB.release()
|
self.mxDB.release()
|
||||||
@ -2138,7 +2186,155 @@ class BasicSwap(BaseApp):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def checkXmrBidState(self, bid_id, bid, offer):
|
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):
|
def checkBidState(self, bid_id, bid, offer):
|
||||||
# assert(self.mxDB.locked())
|
# assert(self.mxDB.locked())
|
||||||
@ -2464,6 +2660,14 @@ class BasicSwap(BaseApp):
|
|||||||
self.sendXmrBidTxnSigsFtoL(row.linked_id, session)
|
self.sendXmrBidTxnSigsFtoL(row.linked_id, session)
|
||||||
elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_A:
|
elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_A:
|
||||||
self.sendXmrBidCoinALockTx(row.linked_id, session)
|
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:
|
else:
|
||||||
self.log.warning('Unknown event type: %d', row.event_type)
|
self.log.warning('Unknown event type: %d', row.event_type)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -2808,6 +3012,7 @@ class BasicSwap(BaseApp):
|
|||||||
bid.setState(BidStates.BID_RECEIVED)
|
bid.setState(BidStates.BID_RECEIVED)
|
||||||
self.saveBidInSession(bid.bid_id, bid, session, xmr_swap)
|
self.saveBidInSession(bid.bid_id, bid, session, xmr_swap)
|
||||||
|
|
||||||
|
|
||||||
def receiveXmrBidAccept(self, bid, session):
|
def receiveXmrBidAccept(self, bid, session):
|
||||||
# Follower receiving MSG1F and MSG2F
|
# Follower receiving MSG1F and MSG2F
|
||||||
self.log.debug('Receiving xmr bid accept %s', bid.bid_id.hex())
|
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):
|
if not ci_to.verifyPubkey(xmr_swap.pkbsl):
|
||||||
raise ValueError('Invalid coin b pubkey.')
|
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):
|
if not ci_from.verifyPubkey(xmr_swap.pkal):
|
||||||
raise ValueError('Invalid pubkey.')
|
raise ValueError('Invalid pubkey.')
|
||||||
if not ci_from.verifyPubkey(xmr_swap.pkarl):
|
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)
|
self.createEventInSession(delay, EventTypes.SIGN_XMR_SWAP_LOCK_TX_A, bid.bid_id, session)
|
||||||
|
|
||||||
def processXmrBid(self, msg):
|
def processXmrBid(self, msg):
|
||||||
|
# MSG1L
|
||||||
self.log.debug('Processing xmr bid msg %s', msg['msgid'])
|
self.log.debug('Processing xmr bid msg %s', msg['msgid'])
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
bid_bytes = bytes.fromhex(msg['hex'][2:-2])
|
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())
|
assert(xmr_offer), 'XMR offer not found: {}.'.format(offer_id.hex())
|
||||||
coin_from = Coins(offer.coin_from)
|
coin_from = Coins(offer.coin_from)
|
||||||
coin_to = Coins(offer.coin_to)
|
coin_to = Coins(offer.coin_to)
|
||||||
|
ci_from = self.ci(coin_from)
|
||||||
|
ci_to = self.ci(coin_to)
|
||||||
|
|
||||||
logging.debug('TODO: xmr bid validation')
|
logging.debug('TODO: xmr bid validation')
|
||||||
|
|
||||||
@ -2899,6 +3111,7 @@ class BasicSwap(BaseApp):
|
|||||||
pkaf=bid_data.pkaf,
|
pkaf=bid_data.pkaf,
|
||||||
pkarf=bid_data.pkarf,
|
pkarf=bid_data.pkarf,
|
||||||
vkbvf=bid_data.kbvf,
|
vkbvf=bid_data.kbvf,
|
||||||
|
pkbvf=ci_to.getPubkey(bid_data.kbvf),
|
||||||
kbsf_dleag=bid_data.kbsf_dleag,
|
kbsf_dleag=bid_data.kbsf_dleag,
|
||||||
b_restore_height=self.ci(coin_to).getChainHeight(),
|
b_restore_height=self.ci(coin_to).getChainHeight(),
|
||||||
)
|
)
|
||||||
@ -2942,6 +3155,7 @@ class BasicSwap(BaseApp):
|
|||||||
xmr_swap.pkal = msg_data.pkal
|
xmr_swap.pkal = msg_data.pkal
|
||||||
xmr_swap.pkarl = msg_data.pkarl
|
xmr_swap.pkarl = msg_data.pkarl
|
||||||
xmr_swap.vkbvl = msg_data.kbvl
|
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.kbsl_dleag = msg_data.kbsl_dleag
|
||||||
|
|
||||||
xmr_swap.a_lock_tx = msg_data.a_lock_tx
|
xmr_swap.a_lock_tx = msg_data.a_lock_tx
|
||||||
@ -2961,7 +3175,7 @@ class BasicSwap(BaseApp):
|
|||||||
xmr_swap.pkarl, xmr_swap.pkarf,
|
xmr_swap.pkarl, xmr_swap.pkarf,
|
||||||
check_a_lock_tx_inputs
|
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(
|
lock_refund_tx_id, xmr_swap.a_swap_refund_value = ci_from.verifyLockRefundTx(
|
||||||
xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
|
xmr_swap.a_lock_refund_tx, xmr_swap.a_lock_refund_tx_script,
|
||||||
@ -3008,7 +3222,6 @@ class BasicSwap(BaseApp):
|
|||||||
try:
|
try:
|
||||||
karf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4)
|
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_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)
|
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
|
af_lock_refund_tx_sig=xmr_swap.af_lock_refund_tx_sig
|
||||||
)
|
)
|
||||||
|
|
||||||
mag_bytes = msg_buf.SerializeToString()
|
msg_bytes = msg_buf.SerializeToString()
|
||||||
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_TXN_SIGS_FL) + mag_bytes.hex()
|
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_TXN_SIGS_FL) + msg_bytes.hex()
|
||||||
|
|
||||||
options = {'decodehex': True, 'ttl_is_seconds': True}
|
options = {'decodehex': True, 'ttl_is_seconds': True}
|
||||||
# TODO: set msg_valid based on bid / offer parameters
|
# 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())
|
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.saveBidInSession(bid_id, bid, session, xmr_swap)
|
||||||
self.swaps_in_progress[bid_id] = (bid, offer)
|
self.swaps_in_progress[bid_id] = (bid, offer)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -3059,6 +3281,7 @@ class BasicSwap(BaseApp):
|
|||||||
xmr_swap.dest_af,
|
xmr_swap.dest_af,
|
||||||
xmr_offer.a_fee_rate)
|
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
|
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(
|
msg_buf = XmrBidLockSpendTxMessage(
|
||||||
@ -3066,8 +3289,8 @@ class BasicSwap(BaseApp):
|
|||||||
a_lock_spend_tx=xmr_swap.a_lock_spend_tx,
|
a_lock_spend_tx=xmr_swap.a_lock_spend_tx,
|
||||||
al_lock_spend_tx_esig=xmr_swap.al_lock_spend_tx_esig)
|
al_lock_spend_tx_esig=xmr_swap.al_lock_spend_tx_esig)
|
||||||
|
|
||||||
mag_bytes = msg_buf.SerializeToString()
|
msg_bytes = msg_buf.SerializeToString()
|
||||||
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_REFUND_SPEND_TX_LF) + mag_bytes.hex()
|
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_SPEND_TX_LF) + msg_bytes.hex()
|
||||||
|
|
||||||
options = {'decodehex': True, 'ttl_is_seconds': True}
|
options = {'decodehex': True, 'ttl_is_seconds': True}
|
||||||
# TODO: set msg_valid based on bid / offer parameters
|
# 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.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.saveBidInSession(bid_id, bid, session, xmr_swap)
|
||||||
self.swaps_in_progress[bid_id] = (bid, offer)
|
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):
|
def processXmrBidCoinALockSigs(self, msg):
|
||||||
# Leader processing MSG3L
|
# Leader processing MSG3L
|
||||||
self.log.debug('Processing xmr coin a follower lock sigs msg %s', msg['msgid'])
|
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)
|
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)
|
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)
|
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'
|
assert(v), 'Invalid signature for lock refund spend txn'
|
||||||
|
|
||||||
@ -3138,6 +3514,54 @@ class BasicSwap(BaseApp):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.setBidError(bid_id, bid, str(ex))
|
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):
|
def processXmrSplitMessage(self, msg):
|
||||||
self.log.debug('Processing xmr split msg %s', msg['msgid'])
|
self.log.debug('Processing xmr split msg %s', msg['msgid'])
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
@ -3157,6 +3581,35 @@ class BasicSwap(BaseApp):
|
|||||||
dbr.created_at = now
|
dbr.created_at = now
|
||||||
self.saveToDB(dbr)
|
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):
|
def processMsg(self, msg):
|
||||||
self.mxDB.acquire()
|
self.mxDB.acquire()
|
||||||
try:
|
try:
|
||||||
@ -3169,14 +3622,18 @@ class BasicSwap(BaseApp):
|
|||||||
self.processBid(msg)
|
self.processBid(msg)
|
||||||
elif msg_type == MessageTypes.BID_ACCEPT:
|
elif msg_type == MessageTypes.BID_ACCEPT:
|
||||||
self.processBidAccept(msg)
|
self.processBidAccept(msg)
|
||||||
elif msg_type == MessageTypes.XMR_BID:
|
elif msg_type == MessageTypes.XMR_BID_FL:
|
||||||
self.processXmrBid(msg)
|
self.processXmrBid(msg)
|
||||||
elif msg_type == MessageTypes.XMR_BID_ACCEPT:
|
elif msg_type == MessageTypes.XMR_BID_ACCEPT_LF:
|
||||||
self.processXmrBidAccept(msg)
|
self.processXmrBidAccept(msg)
|
||||||
elif msg_type == MessageTypes.XMR_BID_TXN_SIGS_FL:
|
elif msg_type == MessageTypes.XMR_BID_TXN_SIGS_FL:
|
||||||
self.processXmrBidCoinALockSigs(msg)
|
self.processXmrBidCoinALockSigs(msg)
|
||||||
|
elif msg_type == MessageTypes.XMR_BID_LOCK_SPEND_TX_LF:
|
||||||
|
self.processXmrBidLockSpendTx(msg)
|
||||||
elif msg_type == MessageTypes.XMR_BID_SPLIT:
|
elif msg_type == MessageTypes.XMR_BID_SPLIT:
|
||||||
self.processXmrSplitMessage(msg)
|
self.processXmrSplitMessage(msg)
|
||||||
|
elif msg_type == MessageTypes.XMR_BID_SECRET_LF:
|
||||||
|
self.processXmrSecretMessage(msg)
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log.error('processMsg %s', str(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
|
initiate_tx = None
|
||||||
participate_tx = None
|
participate_tx = None
|
||||||
xmr_a_lock_tx = None
|
xmr_a_lock_tx = None
|
||||||
|
xmr_b_lock_tx = None
|
||||||
|
xmr_a_lock_spend_tx = None
|
||||||
|
|
||||||
def getITxState(self):
|
def getITxState(self):
|
||||||
if self.initiate_tx is None:
|
if self.initiate_tx is None:
|
||||||
@ -236,6 +238,7 @@ class XmrSwap(Base):
|
|||||||
|
|
||||||
contract_count = sa.Column(sa.Integer)
|
contract_count = sa.Column(sa.Integer)
|
||||||
|
|
||||||
|
sv = sa.Column(sa.LargeBinary) # Secret value
|
||||||
sh = sa.Column(sa.LargeBinary) # Secret hash
|
sh = sa.Column(sa.LargeBinary) # Secret hash
|
||||||
|
|
||||||
dest_af = sa.Column(sa.LargeBinary) # Destination for coin A amount to follower when swap completes successfully
|
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)
|
kbsl_dleag = sa.Column(sa.LargeBinary)
|
||||||
kbsf_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 = sa.Column(sa.LargeBinary)
|
||||||
a_lock_tx_script = 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)
|
af_lock_refund_tx_sig = sa.Column(sa.LargeBinary)
|
||||||
|
|
||||||
a_lock_spend_tx = 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)
|
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
|
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.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth'])
|
||||||
self.txoType = CTxOut
|
self.txoType = CTxOut
|
||||||
self._network = network
|
self._network = network
|
||||||
|
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||||
|
|
||||||
def testDaemonRPC(self):
|
def testDaemonRPC(self):
|
||||||
self.rpc_callback('getwalletinfo', [])
|
self.rpc_callback('getwalletinfo', [])
|
||||||
@ -596,13 +597,14 @@ class BTCInterface(CoinInterface):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def verifyLockSpendTx(self, tx,
|
def verifyLockSpendTx(self, tx_bytes,
|
||||||
lock_tx, lock_tx_script,
|
lock_tx_bytes, lock_tx_script,
|
||||||
a_pkhash_f, feerate):
|
a_pkhash_f, feerate):
|
||||||
# Verify:
|
# Verify:
|
||||||
# Must have only one input with correct prevout (n is always 0) and sequence
|
# Must have only one input with correct prevout (n is always 0) and sequence
|
||||||
# Must have only one output with destination and amount
|
# Must have only one output with destination and amount
|
||||||
|
|
||||||
|
tx = self.loadTx(tx_bytes)
|
||||||
tx_hash = self.getTxHash(tx)
|
tx_hash = self.getTxHash(tx)
|
||||||
logging.info('Verifying lock spend tx: {}.'.format(b2h(tx_hash)))
|
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(tx.nLockTime == 0, 'nLockTime not 0')
|
||||||
assert_cond(len(tx.vin) == 1, 'tx doesn\'t have one input')
|
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)
|
lock_tx_id = self.getTxHash(lock_tx)
|
||||||
|
|
||||||
output_script = CScript([OP_0, hashlib.sha256(lock_tx_script).digest()])
|
output_script = CScript([OP_0, hashlib.sha256(lock_tx_script).digest()])
|
||||||
@ -727,21 +730,24 @@ class BTCInterface(CoinInterface):
|
|||||||
|
|
||||||
def getTransaction(self, txid):
|
def getTransaction(self, txid):
|
||||||
try:
|
try:
|
||||||
return self.rpc_callback('getrawtransaction', [txid.hex()])
|
return bytes.fromhex(self.rpc_callback('getrawtransaction', [txid.hex()]))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
# TODO: filter errors
|
# TODO: filter errors
|
||||||
return None
|
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.clear()
|
||||||
tx.wit.vtxinwit.append(CTxInWitness())
|
tx.wit.vtxinwit.append(CTxInWitness())
|
||||||
tx.wit.vtxinwit[0].scriptWitness.stack = stack
|
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]
|
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]
|
return tx.wit.vtxinwit[0].scriptWitness.stack[2]
|
||||||
|
|
||||||
def createBLockTx(self, Kbs, output_amount):
|
def createBLockTx(self, Kbs, output_amount):
|
||||||
@ -761,7 +767,8 @@ class BTCInterface(CoinInterface):
|
|||||||
return self.publishTx(b_lock_tx)
|
return self.publishTx(b_lock_tx)
|
||||||
|
|
||||||
def recoverEncKey(self, esig, sig, K):
|
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):
|
def getTxVSize(self, tx, add_bytes=0, add_witness_bytes=0):
|
||||||
wsf = self.witnessScaleFactor()
|
wsf = self.witnessScaleFactor()
|
||||||
@ -781,8 +788,8 @@ class BTCInterface(CoinInterface):
|
|||||||
if utxo['amount'] * COIN != cb_swap_value:
|
if utxo['amount'] * COIN != cb_swap_value:
|
||||||
logging.warning('Found output to lock tx pubkey of incorrect value: %s', str(utxo['amount']))
|
logging.warning('Found output to lock tx pubkey of incorrect value: %s', str(utxo['amount']))
|
||||||
else:
|
else:
|
||||||
return True
|
return {'txid': utxo['txid'], 'vout': utxo['vout'], 'amount': utxo['amount'], 'height': utxo['height']}
|
||||||
return False
|
return None
|
||||||
|
|
||||||
def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed):
|
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):
|
def spendBLockTx(self, address_to, kbv, kbs, cb_swap_value, b_fee, restore_height):
|
||||||
print('TODO: spendBLockTx')
|
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():
|
def testBTCInterface():
|
||||||
print('testBTCInterface')
|
print('testBTCInterface')
|
||||||
|
@ -31,6 +31,7 @@ class PARTInterface(BTCInterface):
|
|||||||
self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth'])
|
self.rpc_callback = make_rpc_func(coin_settings['rpcport'], coin_settings['rpcauth'])
|
||||||
self.txoType = CTxOutPart
|
self.txoType = CTxOutPart
|
||||||
self._network = network
|
self._network = network
|
||||||
|
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||||
|
|
||||||
def getNewAddress(self, use_segwit):
|
def getNewAddress(self, use_segwit):
|
||||||
return self.rpc_callback('getnewaddress', ['swap_receive'])
|
return self.rpc_callback('getnewaddress', ['swap_receive'])
|
||||||
|
@ -10,6 +10,7 @@ import logging
|
|||||||
|
|
||||||
import basicswap.contrib.ed25519_fast as edf
|
import basicswap.contrib.ed25519_fast as edf
|
||||||
import basicswap.ed25519_fast_util as edu
|
import basicswap.ed25519_fast_util as edu
|
||||||
|
import basicswap.util_xmr as xmr_util
|
||||||
from coincurve.ed25519 import ed25519_get_pubkey
|
from coincurve.ed25519 import ed25519_get_pubkey
|
||||||
from coincurve.keys import PrivateKey
|
from coincurve.keys import PrivateKey
|
||||||
from coincurve.dleag import (
|
from coincurve.dleag import (
|
||||||
@ -19,12 +20,13 @@ from coincurve.dleag import (
|
|||||||
dleag_prove)
|
dleag_prove)
|
||||||
|
|
||||||
from .util import (
|
from .util import (
|
||||||
|
dumpj,
|
||||||
format_amount)
|
format_amount)
|
||||||
from .rpc_xmr import (
|
from .rpc_xmr import (
|
||||||
make_xmr_rpc_func,
|
make_xmr_rpc_func,
|
||||||
make_xmr_wallet_rpc_func)
|
make_xmr_wallet_rpc_func)
|
||||||
from .ecc_util import (
|
from .ecc_util import (
|
||||||
b2i)
|
b2i, i2b, b2h)
|
||||||
from .chainparams import CoinInterface, Coins
|
from .chainparams import CoinInterface, Coins
|
||||||
|
|
||||||
XMR_COIN = 10 ** 12
|
XMR_COIN = 10 ** 12
|
||||||
@ -54,6 +56,10 @@ class XMRInterface(CoinInterface):
|
|||||||
self.rpc_cb = rpc_cb
|
self.rpc_cb = rpc_cb
|
||||||
self.rpc_wallet_cb = rpc_wallet_cb
|
self.rpc_wallet_cb = rpc_wallet_cb
|
||||||
self._network = network
|
self._network = network
|
||||||
|
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||||
|
|
||||||
|
def setWalletFilename(self, wallet_filename):
|
||||||
|
self._wallet_filename = wallet_filename
|
||||||
|
|
||||||
def testDaemonRPC(self):
|
def testDaemonRPC(self):
|
||||||
self.rpc_wallet_cb('get_languages')
|
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())
|
rv['unconfirmed_balance'] = format_amount(balance_info['balance'] - balance_info['unlocked_balance'], XMRInterface.exp())
|
||||||
return rv
|
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):
|
def getNewAddress(self, placeholder):
|
||||||
logging.debug('TODO - subaddress?')
|
logging.debug('TODO - subaddress?')
|
||||||
|
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||||
return self.rpc_wallet_cb('get_address')['address']
|
return self.rpc_wallet_cb('get_address')['address']
|
||||||
|
|
||||||
def isValidKey(self, key_bytes):
|
def isValidKey(self, key_bytes):
|
||||||
@ -123,36 +134,41 @@ class XMRInterface(CoinInterface):
|
|||||||
return i
|
return i
|
||||||
|
|
||||||
def sumKeys(self, ka, kb):
|
def sumKeys(self, ka, kb):
|
||||||
return (ka + kb) % edf.l
|
return i2b((b2i(ka) + b2i(kb)) % edf.l)
|
||||||
|
|
||||||
def sumPubkeys(self, Ka, Kb):
|
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):
|
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?
|
# TODO: How to set feerate?
|
||||||
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}]}
|
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}]}
|
||||||
rv = self.rpc_wallet_cb('transfer', params)
|
rv = self.rpc_wallet_cb('transfer', params)
|
||||||
logging.info('publishBLockTx %s to address_b58 %s', rv['tx_hash'], shared_addr)
|
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):
|
def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
|
||||||
Kbv_enc = self.encodePubkey(self.pubkey(kbv))
|
#Kbv_enc = self.encodePubkey(self.pubkey(kbv))
|
||||||
address_b58 = xmr_util.encode_address(Kbv_enc, self.encodePubkey(Kbs))
|
Kbv = self.getPubkey(kbv)
|
||||||
|
address_b58 = xmr_util.encode_address(Kbv, Kbs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.rpc_wallet_cb('close_wallet')
|
self.rpc_wallet_cb('close_wallet')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning('close_wallet failed %s', str(e))
|
logging.warning('close_wallet failed %s', str(e))
|
||||||
|
|
||||||
|
kbv_le = kbv[::-1]
|
||||||
params = {
|
params = {
|
||||||
'restore_height': restore_height,
|
'restore_height': restore_height,
|
||||||
'filename': address_b58,
|
'filename': address_b58,
|
||||||
'address': address_b58,
|
'address': address_b58,
|
||||||
'viewkey': b2h(intToBytes32_le(kbv)),
|
#'viewkey': b2h(intToBytes32_le(kbv)),
|
||||||
|
'viewkey': b2h(kbv_le),
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -162,6 +178,8 @@ class XMRInterface(CoinInterface):
|
|||||||
logging.info('generate_from_keys %s', dumpj(rv))
|
logging.info('generate_from_keys %s', dumpj(rv))
|
||||||
rv = self.rpc_wallet_cb('open_wallet', {'filename': address_b58})
|
rv = self.rpc_wallet_cb('open_wallet', {'filename': address_b58})
|
||||||
|
|
||||||
|
rv = self.rpc_wallet_cb('refresh')
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
try:
|
try:
|
||||||
current_height = self.rpc_cb('get_block_count')['count']
|
current_height = self.rpc_cb('get_block_count')['count']
|
||||||
@ -170,18 +188,15 @@ class XMRInterface(CoinInterface):
|
|||||||
logging.info('rpc_cb failed %s', str(e))
|
logging.info('rpc_cb failed %s', str(e))
|
||||||
current_height = None # If the transfer is available it will be deep enough
|
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'}
|
params = {'transfer_type': 'available'}
|
||||||
rv = self.rpc_wallet_cb('incoming_transfers', params)
|
rv = self.rpc_wallet_cb('incoming_transfers', params)
|
||||||
if 'transfers' in rv:
|
if 'transfers' in rv:
|
||||||
for transfer in rv['transfers']:
|
for transfer in rv['transfers']:
|
||||||
if transfer['amount'] == cb_swap_value \
|
if transfer['amount'] == cb_swap_value \
|
||||||
and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
|
and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
|
||||||
return True
|
return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': transfer['block_height']}
|
||||||
time.sleep(1 + i)
|
|
||||||
|
|
||||||
return False
|
return None
|
||||||
|
|
||||||
def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
|
def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
|
||||||
|
|
||||||
@ -244,11 +259,33 @@ class XMRInterface(CoinInterface):
|
|||||||
|
|
||||||
return False
|
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):
|
def spendBLockTx(self, address_to, kbv, kbs, cb_swap_value, b_fee_rate, restore_height):
|
||||||
|
|
||||||
Kbv_enc = self.encodePubkey(self.pubkey(kbv))
|
Kbv = self.getPubkey(kbv)
|
||||||
Kbs_enc = self.encodePubkey(self.pubkey(kbs))
|
Kbs = self.getPubkey(kbs)
|
||||||
address_b58 = xmr_util.encode_address(Kbv_enc, Kbs_enc)
|
address_b58 = xmr_util.encode_address(Kbv, Kbs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.rpc_wallet_cb('close_wallet')
|
self.rpc_wallet_cb('close_wallet')
|
||||||
@ -260,8 +297,8 @@ class XMRInterface(CoinInterface):
|
|||||||
params = {
|
params = {
|
||||||
'filename': wallet_filename,
|
'filename': wallet_filename,
|
||||||
'address': address_b58,
|
'address': address_b58,
|
||||||
'viewkey': b2h(intToBytes32_le(kbv)),
|
'viewkey': b2h(kbv[::-1]),
|
||||||
'spendkey': b2h(intToBytes32_le(kbs)),
|
'spendkey': b2h(kbs[::-1]),
|
||||||
'restore_height': restore_height,
|
'restore_height': restore_height,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,4 +335,4 @@ class XMRInterface(CoinInterface):
|
|||||||
b_fee += b_fee_rate
|
b_fee += b_fee_rate
|
||||||
logging.info('Raising fee to %d', b_fee)
|
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;
|
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',
|
syntax='proto3',
|
||||||
serialized_options=None,
|
serialized_options=None,
|
||||||
create_key=_descriptor._internal_create_key,
|
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,
|
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.fields_by_name['lock_type'].enum_type = _OFFERMESSAGE_LOCKTYPE
|
||||||
_OFFERMESSAGE_LOCKTYPE.containing_type = _OFFERMESSAGE
|
_OFFERMESSAGE_LOCKTYPE.containing_type = _OFFERMESSAGE
|
||||||
DESCRIPTOR.message_types_by_name['OfferMessage'] = _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['XmrBidAcceptMessage'] = _XMRBIDACCEPTMESSAGE
|
||||||
DESCRIPTOR.message_types_by_name['XmrBidLockTxSigsMessage'] = _XMRBIDLOCKTXSIGSMESSAGE
|
DESCRIPTOR.message_types_by_name['XmrBidLockTxSigsMessage'] = _XMRBIDLOCKTXSIGSMESSAGE
|
||||||
DESCRIPTOR.message_types_by_name['XmrBidLockSpendTxMessage'] = _XMRBIDLOCKSPENDTXMESSAGE
|
DESCRIPTOR.message_types_by_name['XmrBidLockSpendTxMessage'] = _XMRBIDLOCKSPENDTXMESSAGE
|
||||||
|
DESCRIPTOR.message_types_by_name['XmrBidSecretMessage'] = _XMRBIDSECRETMESSAGE
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||||
|
|
||||||
OfferMessage = _reflection.GeneratedProtocolMessageType('OfferMessage', (_message.Message,), {
|
OfferMessage = _reflection.GeneratedProtocolMessageType('OfferMessage', (_message.Message,), {
|
||||||
@ -711,5 +751,12 @@ XmrBidLockSpendTxMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockS
|
|||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(XmrBidLockSpendTxMessage)
|
_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)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import xmrswap.contrib.Keccak as Keccak
|
import basicswap.contrib.Keccak as Keccak
|
||||||
from .contrib.MoneroPy.base58 import encode as xmr_b58encode
|
from .contrib.MoneroPy.base58 import encode as xmr_b58encode
|
||||||
|
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
waitForRPC(btcRpc)
|
waitForRPC(btcRpc)
|
||||||
cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
|
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))
|
btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
|
||||||
|
|
||||||
ro = btcRpc('getblockchaininfo')
|
ro = btcRpc('getblockchaininfo')
|
||||||
|
@ -175,7 +175,7 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
num_blocks = 500
|
num_blocks = 500
|
||||||
btc_addr = btcRpc(1, 'getnewaddress mining_addr bech32')
|
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))
|
btcRpc(1, 'generatetoaddress {} {}'.format(num_blocks, btc_addr))
|
||||||
|
|
||||||
for i in range(20):
|
for i in range(20):
|
||||||
|
@ -271,7 +271,7 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
waitForRPC(btcRpc)
|
waitForRPC(btcRpc)
|
||||||
cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
|
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))
|
btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
|
||||||
|
|
||||||
ro = btcRpc('getblockchaininfo')
|
ro = btcRpc('getblockchaininfo')
|
||||||
|
@ -67,7 +67,6 @@ from tests.basicswap.common import (
|
|||||||
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
||||||
from bin.basicswap_run import startDaemon
|
from bin.basicswap_run import startDaemon
|
||||||
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
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,
|
'walletrpcport': XMR_BASE_WALLET_RPC_PORT + node_id,
|
||||||
'walletrpcuser': 'test' + str(node_id),
|
'walletrpcuser': 'test' + str(node_id),
|
||||||
'walletrpcpassword': 'test_pass' + str(node_id),
|
'walletrpcpassword': 'test_pass' + str(node_id),
|
||||||
|
'walletfile': 'testwallet',
|
||||||
'datadir': os.path.join(datadir, 'xmr_' + str(node_id)),
|
'datadir': os.path.join(datadir, 'xmr_' + str(node_id)),
|
||||||
'bindir': cfg.XMR_BINDIR,
|
'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)
|
auth = 'test{0}:test_pass{0}'.format(node_id)
|
||||||
return callrpc(base_rpc_port + node_id, auth, method, params, wallet)
|
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):
|
def run_loop(cls):
|
||||||
global stop_test
|
global stop_test
|
||||||
while not stop_test:
|
while not stop_test:
|
||||||
for c in cls.swap_clients:
|
for c in cls.swap_clients:
|
||||||
c.update()
|
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)
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
|
||||||
@ -325,6 +328,7 @@ class Test(unittest.TestCase):
|
|||||||
super(Test, cls).setUpClass()
|
super(Test, cls).setUpClass()
|
||||||
|
|
||||||
cls.update_thread = None
|
cls.update_thread = None
|
||||||
|
cls.coins_update_thread = None
|
||||||
cls.http_threads = []
|
cls.http_threads = []
|
||||||
cls.swap_clients = []
|
cls.swap_clients = []
|
||||||
cls.part_daemons = []
|
cls.part_daemons = []
|
||||||
@ -425,7 +429,7 @@ class Test(unittest.TestCase):
|
|||||||
cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
|
cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
|
||||||
|
|
||||||
num_blocks = 500
|
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)
|
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))
|
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)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
|
cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
|
||||||
cls.update_thread.start()
|
cls.update_thread.start()
|
||||||
|
|
||||||
|
cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,))
|
||||||
|
cls.coins_update_thread.start()
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
Test.tearDownClass()
|
Test.tearDownClass()
|
||||||
@ -455,7 +462,11 @@ class Test(unittest.TestCase):
|
|||||||
cls.update_thread.join()
|
cls.update_thread.join()
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.info('Failed to join update_thread')
|
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:
|
for t in cls.http_threads:
|
||||||
t.stop()
|
t.stop()
|
||||||
@ -541,7 +552,6 @@ class Test(unittest.TestCase):
|
|||||||
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
|
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
|
||||||
assert(len(offers) == 1)
|
assert(len(offers) == 1)
|
||||||
offer = offers[0]
|
offer = offers[0]
|
||||||
pprint(vars(offer))
|
|
||||||
|
|
||||||
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
|
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)
|
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[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_SCRIPT_COIN_LOCKED, sent=True)
|
|
||||||
|
|
||||||
|
|
||||||
|
#self.wait_for_bid(swap_clients[0], bid_id, BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED, wait_for=80)
|
||||||
|
#self.wait_for_bid(swap_clients[1], bid_id, BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED, sent=True)
|
||||||
|
|
||||||
|
self.wait_for_bid(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
|
||||||
|
self.wait_for_bid(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user