protocol: Start adding anon particl option.

This commit is contained in:
tecnovert 2021-02-11 14:57:54 +02:00
parent c4fc2c70dc
commit f2018184e7
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
7 changed files with 273 additions and 112 deletions

View file

@ -25,7 +25,7 @@ from enum import IntEnum, auto
from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.orm.session import close_all_sessions from sqlalchemy.orm.session import close_all_sessions
from .interface_part import PARTInterface from .interface_part import PARTInterface, PARTInterfaceAnon
from .interface_btc import BTCInterface from .interface_btc import BTCInterface
from .interface_ltc import LTCInterface from .interface_ltc import LTCInterface
from .interface_nmc import NMCInterface from .interface_nmc import NMCInterface
@ -631,6 +631,9 @@ class BasicSwap(BaseApp):
'chain_median_time': None, 'chain_median_time': None,
} }
if coin == Coins.PART:
self.coin_clients[Coins.PART_ANON] = self.coin_clients[coin]
if self.coin_clients[coin]['connection_type'] == 'rpc': if self.coin_clients[coin]['connection_type'] == 'rpc':
if coin == Coins.XMR: if coin == Coins.XMR:
self.coin_clients[coin]['walletrpchost'] = chain_client_settings.get('walletrpchost', '127.0.0.1') self.coin_clients[coin]['walletrpchost'] = chain_client_settings.get('walletrpchost', '127.0.0.1')
@ -641,6 +644,8 @@ class BasicSwap(BaseApp):
raise ValueError('Missing XMR wallet rpc credentials.') raise ValueError('Missing XMR wallet rpc credentials.')
def ci(self, coin): # Coin interface def ci(self, coin): # Coin interface
if coin == Coins.PART_ANON:
return self.coin_clients[Coins.PART]['interface_anon']
return self.coin_clients[coin]['interface'] return self.coin_clients[coin]['interface']
def createInterface(self, coin): def createInterface(self, coin):
@ -702,6 +707,8 @@ class BasicSwap(BaseApp):
def createCoinInterface(self, coin): def createCoinInterface(self, coin):
if self.coin_clients[coin]['connection_type'] == 'rpc': if self.coin_clients[coin]['connection_type'] == 'rpc':
self.coin_clients[coin]['interface'] = self.createInterface(coin) self.coin_clients[coin]['interface'] = self.createInterface(coin)
if coin == Coins.PART:
self.coin_clients[coin]['interface_anon'] = PARTInterfaceAnon(self.coin_clients[coin], self.chain, self)
elif self.coin_clients[coin]['connection_type'] == 'passthrough': elif self.coin_clients[coin]['connection_type'] == 'passthrough':
self.coin_clients[coin]['interface'] = self.createPassthroughInterface(coin) self.coin_clients[coin]['interface'] = self.createPassthroughInterface(coin)
@ -1066,13 +1073,15 @@ class BasicSwap(BaseApp):
raise ValueError('Invalid swap type for XMR') raise ValueError('Invalid swap type for XMR')
def validateOfferAmounts(self, coin_from, coin_to, amount, rate, min_bid_amount): def validateOfferAmounts(self, coin_from, coin_to, amount, rate, min_bid_amount):
ci_from = self.ci(coin_from)
ci_to = self.ci(coin_to)
assert(amount >= min_bid_amount), 'amount < min_bid_amount' assert(amount >= min_bid_amount), 'amount < min_bid_amount'
assert(amount > chainparams[coin_from][self.chain]['min_amount']), 'From amount below min value for chain' assert(amount > ci_from.min_amount()), 'From amount below min value for chain'
assert(amount < chainparams[coin_from][self.chain]['max_amount']), 'From amount above max value for chain' assert(amount < ci_from.max_amount()), 'From amount above max value for chain'
amount_to = int((amount * rate) // self.ci(coin_from).COIN()) amount_to = int((amount * rate) // ci_from.COIN())
assert(amount_to > chainparams[coin_to][self.chain]['min_amount']), 'To amount below min value for chain' assert(amount_to > ci_to.min_amount()), 'To amount below min value for chain'
assert(amount_to < chainparams[coin_to][self.chain]['max_amount']), 'To amount above max value for chain' assert(amount_to < ci_to.max_amount()), 'To amount above max value for chain'
def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value): def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value):
if lock_type == OfferMessage.SEQUENCE_LOCK_TIME: if lock_type == OfferMessage.SEQUENCE_LOCK_TIME:
@ -1268,7 +1277,7 @@ class BasicSwap(BaseApp):
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path])['key_info']['result'] extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path])['key_info']['result']
privkey = decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey']) privkey = decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
if ci.isValidKey(privkey): if ci.verifyKey(privkey):
return privkey return privkey
nonce += 1 nonce += 1
if nonce > 1000: if nonce > 1000:
@ -1501,7 +1510,6 @@ class BasicSwap(BaseApp):
def getCachedStealthAddressForCoin(self, coin_type): def getCachedStealthAddressForCoin(self, coin_type):
self.log.debug('getCachedStealthAddressForCoin %s', coin_type) self.log.debug('getCachedStealthAddressForCoin %s', coin_type)
# TODO: auto refresh after used
ci = self.ci(coin_type) ci = self.ci(coin_type)
key_str = 'stealth_addr_' + ci.coin_name() key_str = 'stealth_addr_' + ci.coin_name()
@ -1524,6 +1532,37 @@ class BasicSwap(BaseApp):
self.mxDB.release() self.mxDB.release()
return addr return addr
def getCachedWalletRestoreHeight(self, ci):
self.log.debug('getCachedWalletRestoreHeight %s', ci.coin_name())
key_str = 'restore_height_' + ci.coin_name()
self.mxDB.acquire()
try:
session = scoped_session(self.session_factory)
try:
wrh = session.query(DBKVInt).filter_by(key=key_str).first().value
except Exception:
wrh = ci.getWalletRestoreHeight()
self.log.info('Found restore height for %s', ci.coin_name())
session.add(DBKVInt(
key=key_str,
value=wrh
))
session.commit()
finally:
session.close()
session.remove()
self.mxDB.release()
return wrh
def getWalletRestoreHeight(self, ci):
wrh = ci._restore_height
if wrh is not None:
return wrh
found_height = self.getCachedWalletRestoreHeight(ci)
ci.setWalletRestoreHeight(found_height)
return found_height
def getNewContractId(self): def getNewContractId(self):
self.mxDB.acquire() self.mxDB.acquire()
try: try:
@ -2017,12 +2056,15 @@ class BasicSwap(BaseApp):
xmr_swap.start_chain_a_height = ci_from.getChainHeight() xmr_swap.start_chain_a_height = ci_from.getChainHeight()
xmr_swap.b_restore_height = ci_to.getChainHeight() xmr_swap.b_restore_height = ci_to.getChainHeight()
if xmr_swap.b_restore_height < ci_to._restore_height: wallet_restore_height = self.getWalletRestoreHeight(ci_to)
xmr_swap.b_restore_height = ci_to._restore_height if xmr_swap.b_restore_height < wallet_restore_height:
self.log.warning('XMR swap restore height clamped to {}'.format(ci_to._restore_height)) xmr_swap.b_restore_height = wallet_restore_height
self.log.warning('XMR swap restore height clamped to {}'.format(wallet_restore_height))
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519=True)
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519=True) for_ed25519 = True if coin_to == Coins.XMR else False
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519)
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519)
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)
@ -2032,13 +2074,19 @@ class BasicSwap(BaseApp):
xmr_swap.pkaf = ci_from.getPubkey(kaf) xmr_swap.pkaf = ci_from.getPubkey(kaf)
if coin_to == Coins.XMR:
xmr_swap.kbsf_dleag = ci_to.proveDLEAG(kbsf) xmr_swap.kbsf_dleag = ci_to.proveDLEAG(kbsf)
else:
xmr_swap.kbsf_dleag = xmr_swap.pkbsf
xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33] xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33]
assert(xmr_swap.pkasf == ci_from.getPubkey(kbsf)) assert(xmr_swap.pkasf == ci_from.getPubkey(kbsf))
msg_buf.pkaf = xmr_swap.pkaf msg_buf.pkaf = xmr_swap.pkaf
msg_buf.kbvf = kbvf msg_buf.kbvf = kbvf
if coin_to == Coins.XMR:
msg_buf.kbsf_dleag = xmr_swap.kbsf_dleag[:16000] msg_buf.kbsf_dleag = xmr_swap.kbsf_dleag[:16000]
else:
msg_buf.kbsf_dleag = xmr_swap.kbsf_dleag
bid_bytes = msg_buf.SerializeToString() bid_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_FL) + bid_bytes.hex() payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_FL) + bid_bytes.hex()
@ -2053,6 +2101,7 @@ class BasicSwap(BaseApp):
ro = self.callrpc('smsgsend', [bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options]) ro = self.callrpc('smsgsend', [bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options])
xmr_swap.bid_id = bytes.fromhex(ro['msgid']) xmr_swap.bid_id = bytes.fromhex(ro['msgid'])
if coin_to == Coins.XMR:
msg_buf2 = XmrSplitMessage( msg_buf2 = XmrSplitMessage(
msg_id=xmr_swap.bid_id, msg_id=xmr_swap.bid_id,
msg_type=XmrSplitMsgTypes.BID, msg_type=XmrSplitMsgTypes.BID,
@ -2129,8 +2178,9 @@ class BasicSwap(BaseApp):
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()
kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1, for_ed25519=True) for_ed25519 = True if coin_to == Coins.XMR else False
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1, for_ed25519)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519)
kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3) kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3)
@ -2144,7 +2194,10 @@ class BasicSwap(BaseApp):
xmr_swap.pkal = ci_from.getPubkey(kal) xmr_swap.pkal = ci_from.getPubkey(kal)
if coin_to == Coins.XMR:
xmr_swap.kbsl_dleag = ci_to.proveDLEAG(kbsl) xmr_swap.kbsl_dleag = ci_to.proveDLEAG(kbsl)
else:
xmr_swap.kbsl_dleag = xmr_swap.pkbsl
# MSG2F # MSG2F
xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script = ci_from.createScriptLockTx( xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script = ci_from.createScriptLockTx(
@ -2180,7 +2233,10 @@ class BasicSwap(BaseApp):
msg_buf.bid_msg_id = bid_id msg_buf.bid_msg_id = bid_id
msg_buf.pkal = xmr_swap.pkal msg_buf.pkal = xmr_swap.pkal
msg_buf.kbvl = kbvl msg_buf.kbvl = kbvl
if coin_to == Coins.XMR:
msg_buf.kbsl_dleag = xmr_swap.kbsl_dleag[:16000] msg_buf.kbsl_dleag = xmr_swap.kbsl_dleag[:16000]
else:
msg_buf.kbsl_dleag = xmr_swap.kbsl_dleag
# MSG2F # MSG2F
msg_buf.a_lock_tx = xmr_swap.a_lock_tx msg_buf.a_lock_tx = xmr_swap.a_lock_tx
@ -2199,6 +2255,7 @@ class BasicSwap(BaseApp):
bid.accept_msg_id = bytes.fromhex(msg_id) bid.accept_msg_id = bytes.fromhex(msg_id)
xmr_swap.bid_accept_msg_id = bid.accept_msg_id xmr_swap.bid_accept_msg_id = bid.accept_msg_id
if coin_to == Coins.XMR:
msg_buf2 = XmrSplitMessage( msg_buf2 = XmrSplitMessage(
msg_id=bid_id, msg_id=bid_id,
msg_type=XmrSplitMsgTypes.BID_ACCEPT, msg_type=XmrSplitMsgTypes.BID_ACCEPT,
@ -3573,7 +3630,8 @@ class BasicSwap(BaseApp):
raise ValueError('TODO') raise ValueError('TODO')
elif offer_data.swap_type == SwapTypes.XMR_SWAP: elif offer_data.swap_type == SwapTypes.XMR_SWAP:
assert(coin_from != Coins.XMR) assert(coin_from != Coins.XMR)
assert(coin_to == Coins.XMR) assert(coin_from != Coins.PART_ANON)
assert(coin_to == Coins.XMR or coin_to == Coins.PART_ANON)
self.log.debug('TODO - More restrictions') self.log.debug('TODO - More restrictions')
else: else:
raise ValueError('Unknown swap type {}.'.format(offer_data.swap_type)) raise ValueError('Unknown swap type {}.'.format(offer_data.swap_type))
@ -3850,13 +3908,12 @@ class BasicSwap(BaseApp):
ci_from = self.ci(Coins(offer.coin_from)) ci_from = self.ci(Coins(offer.coin_from))
ci_to = self.ci(Coins(offer.coin_to)) ci_to = self.ci(Coins(offer.coin_to))
if offer.coin_to == Coins.XMR:
if len(xmr_swap.kbsf_dleag) < ci_to.lengthDLEAG(): if len(xmr_swap.kbsf_dleag) < ci_to.lengthDLEAG():
q = session.query(XmrSplitData).filter(sa.and_(XmrSplitData.bid_id == bid.bid_id, XmrSplitData.msg_type == XmrSplitMsgTypes.BID)).order_by(XmrSplitData.msg_sequence.asc()) q = session.query(XmrSplitData).filter(sa.and_(XmrSplitData.bid_id == bid.bid_id, XmrSplitData.msg_type == XmrSplitMsgTypes.BID)).order_by(XmrSplitData.msg_sequence.asc())
for row in q: for row in q:
xmr_swap.kbsf_dleag += row.dleag xmr_swap.kbsf_dleag += row.dleag
if not ci_to.verifyKey(xmr_swap.vkbvf):
raise ValueError('Invalid key.')
if not ci_to.verifyDLEAG(xmr_swap.kbsf_dleag): if not ci_to.verifyDLEAG(xmr_swap.kbsf_dleag):
raise ValueError('Invalid DLEAG proof.') raise ValueError('Invalid DLEAG proof.')
@ -3867,6 +3924,14 @@ class BasicSwap(BaseApp):
xmr_swap.pkbsf = xmr_swap.kbsf_dleag[33: 33 + 32] xmr_swap.pkbsf = xmr_swap.kbsf_dleag[33: 33 + 32]
if not ci_to.verifyPubkey(xmr_swap.pkbsf): if not ci_to.verifyPubkey(xmr_swap.pkbsf):
raise ValueError('Invalid coin b pubkey.') raise ValueError('Invalid coin b pubkey.')
else:
xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33]
if not ci_from.verifyPubkey(xmr_swap.pkasf):
raise ValueError('Invalid coin a pubkey.')
xmr_swap.pkbsf = xmr_swap.pkasf
if not ci_to.verifyKey(xmr_swap.vkbvf):
raise ValueError('Invalid key.')
if not ci_from.verifyPubkey(xmr_swap.pkaf): if not ci_from.verifyPubkey(xmr_swap.pkaf):
raise ValueError('Invalid pubkey.') raise ValueError('Invalid pubkey.')
@ -3897,16 +3962,14 @@ class BasicSwap(BaseApp):
assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex()) assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
xmr_swap = session.query(XmrSwap).filter_by(bid_id=bid.bid_id).first() 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()) assert(xmr_swap), 'XMR swap not found: {}.'.format(bid.bid_id.hex())
ci_from = self.ci(Coins(offer.coin_from)) ci_from = self.ci(offer.coin_from)
ci_to = self.ci(Coins(offer.coin_to)) ci_to = self.ci(offer.coin_to)
if offer.coin_to == Coins.XMR:
if len(xmr_swap.kbsl_dleag) < ci_to.lengthDLEAG(): if len(xmr_swap.kbsl_dleag) < ci_to.lengthDLEAG():
q = session.query(XmrSplitData).filter(sa.and_(XmrSplitData.bid_id == bid.bid_id, XmrSplitData.msg_type == XmrSplitMsgTypes.BID_ACCEPT)).order_by(XmrSplitData.msg_sequence.asc()) q = session.query(XmrSplitData).filter(sa.and_(XmrSplitData.bid_id == bid.bid_id, XmrSplitData.msg_type == XmrSplitMsgTypes.BID_ACCEPT)).order_by(XmrSplitData.msg_sequence.asc())
for row in q: for row in q:
xmr_swap.kbsl_dleag += row.dleag xmr_swap.kbsl_dleag += row.dleag
if not ci_to.verifyKey(xmr_swap.vkbvl):
raise ValueError('Invalid key.')
if not ci_to.verifyDLEAG(xmr_swap.kbsl_dleag): if not ci_to.verifyDLEAG(xmr_swap.kbsl_dleag):
raise ValueError('Invalid DLEAG proof.') raise ValueError('Invalid DLEAG proof.')
@ -3917,6 +3980,14 @@ class BasicSwap(BaseApp):
xmr_swap.pkbsl = xmr_swap.kbsl_dleag[33: 33 + 32] xmr_swap.pkbsl = xmr_swap.kbsl_dleag[33: 33 + 32]
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.')
else:
xmr_swap.pkasl = xmr_swap.kbsl_dleag[0: 33]
if not ci_from.verifyPubkey(xmr_swap.pkasl):
raise ValueError('Invalid coin a pubkey.')
xmr_swap.pkbsl = xmr_swap.pkasl
if not ci_to.verifyKey(xmr_swap.vkbvl):
raise ValueError('Invalid key.')
xmr_swap.vkbv = ci_to.sumKeys(xmr_swap.vkbvl, xmr_swap.vkbvf) 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.pkbv = ci_to.sumPubkeys(xmr_swap.pkbvl, xmr_swap.pkbvf)
@ -3955,8 +4026,8 @@ class BasicSwap(BaseApp):
offer, xmr_offer = self.getXmrOffer(offer_id, sent=True) offer, xmr_offer = self.getXmrOffer(offer_id, sent=True)
assert(offer and offer.was_sent), 'Offer not found: {}.'.format(offer_id.hex()) assert(offer and offer.was_sent), 'Offer not found: {}.'.format(offer_id.hex())
assert(xmr_offer), 'XMR offer not found: {}.'.format(offer_id.hex()) assert(xmr_offer), 'XMR offer not found: {}.'.format(offer_id.hex())
ci_from = self.ci(Coins(offer.coin_from)) ci_from = self.ci(offer.coin_from)
ci_to = self.ci(Coins(offer.coin_to)) ci_to = self.ci(offer.coin_to)
assert(offer.state == OfferStates.OFFER_RECEIVED), 'Bad offer state' assert(offer.state == OfferStates.OFFER_RECEIVED), 'Bad offer state'
assert(msg['to'] == offer.addr_from), 'Received on incorrect address' assert(msg['to'] == offer.addr_from), 'Received on incorrect address'
@ -3995,9 +4066,10 @@ class BasicSwap(BaseApp):
b_restore_height=ci_to.getChainHeight(), b_restore_height=ci_to.getChainHeight(),
start_chain_a_height=ci_from.getChainHeight(), start_chain_a_height=ci_from.getChainHeight(),
) )
if xmr_swap.b_restore_height < ci_to._restore_height: wallet_restore_height = self.getWalletRestoreHeight(ci_to)
xmr_swap.b_restore_height = ci_to._restore_height if xmr_swap.b_restore_height < wallet_restore_height:
self.log.warning('XMR swap restore height clamped to {}'.format(ci_to._restore_height)) xmr_swap.b_restore_height = wallet_restore_height
self.log.warning('XMR swap restore height clamped to {}'.format(wallet_restore_height))
else: else:
assert(bid.state == BidStates.BID_SENT), 'Wrong bid state: {}'.format(str(BidStates(bid.state))) assert(bid.state == BidStates.BID_SENT), 'Wrong bid state: {}'.format(str(BidStates(bid.state)))
bid.created_at = msg['sent'] bid.created_at = msg['sent']
@ -4009,6 +4081,16 @@ class BasicSwap(BaseApp):
self.log.info('Receiving xmr bid %s for offer %s', bid_id.hex(), bid_data.offer_msg_id.hex()) self.log.info('Receiving xmr bid %s for offer %s', bid_id.hex(), bid_data.offer_msg_id.hex())
self.saveBid(bid_id, bid, xmr_swap=xmr_swap) self.saveBid(bid_id, bid, xmr_swap=xmr_swap)
if offer.coin_to != Coins.XMR:
with self.mxDB:
try:
session = scoped_session(self.session_factory)
self.receiveXmrBid(bid, session)
session.commit()
finally:
session.close()
session.remove()
def processXmrBidAccept(self, msg): def processXmrBidAccept(self, msg):
# F receiving MSG1F and MSG2F # F receiving MSG1F and MSG2F
self.log.debug('Processing xmr bid accept msg %s', msg['msgid']) self.log.debug('Processing xmr bid accept msg %s', msg['msgid'])
@ -4027,8 +4109,8 @@ class BasicSwap(BaseApp):
offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=True) offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=True)
assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex()) assert(offer), 'Offer not found: {}.'.format(bid.offer_id.hex())
assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex()) assert(xmr_offer), 'XMR offer not found: {}.'.format(bid.offer_id.hex())
ci_from = self.ci(Coins(offer.coin_from)) ci_from = self.ci(offer.coin_from)
ci_to = self.ci(Coins(offer.coin_to)) ci_to = self.ci(offer.coin_to)
try: try:
xmr_swap.pkal = msg_data.pkal xmr_swap.pkal = msg_data.pkal
@ -4075,6 +4157,16 @@ class BasicSwap(BaseApp):
bid.setState(BidStates.BID_RECEIVING_ACC) bid.setState(BidStates.BID_RECEIVING_ACC)
self.saveBid(bid.bid_id, bid, xmr_swap=xmr_swap) self.saveBid(bid.bid_id, bid, xmr_swap=xmr_swap)
if offer.coin_to != Coins.XMR:
with self.mxDB:
try:
session = scoped_session(self.session_factory)
self.receiveXmrBidAccept(bid, session)
session.commit()
finally:
session.close()
session.remove()
except Exception as ex: except Exception as ex:
if self.debug: if self.debug:
traceback.print_exc() traceback.print_exc()
@ -4321,7 +4413,8 @@ 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)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) for_ed25519 = True if coin_to == Coins.XMR else False
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519)
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)
al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig) al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig)
@ -4374,7 +4467,8 @@ class BasicSwap(BaseApp):
kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf) kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf)
assert(kbsf is not None) assert(kbsf is not None)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) for_ed25519 = True if coin_to == Coins.XMR else False
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519)
vkbs = ci_to.sumKeys(kbsl, kbsf) vkbs = ci_to.sumKeys(kbsl, kbsf)
address_to = ci_to.getMainWalletAddress() address_to = ci_to.getMainWalletAddress()
@ -4429,7 +4523,8 @@ class BasicSwap(BaseApp):
kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl) kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl)
assert(kbsl is not None) assert(kbsl is not None)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) for_ed25519 = True if coin_to == Coins.XMR else False
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519)
vkbs = ci_to.sumKeys(kbsl, kbsf) vkbs = ci_to.sumKeys(kbsl, kbsf)
address_to = ci_to.getMainWalletAddress() address_to = ci_to.getMainWalletAddress()
@ -4490,7 +4585,8 @@ class BasicSwap(BaseApp):
xmr_swap.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig xmr_swap.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig
xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True) for_ed25519 = True if coin_to == Coins.XMR else False
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519)
kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3) kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3)
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)

View file

@ -203,11 +203,11 @@ chainparams = {
class CoinInterface: class CoinInterface:
def __init__(self): def __init__(self):
self._unknown_wallet_seed = True
self.setDefaults() self.setDefaults()
def setDefaults(self): def setDefaults(self):
pass self._unknown_wallet_seed = True
self._restore_height = None
def make_int(self, amount_in, r=0): def make_int(self, amount_in, r=0):
return make_int(amount_in, self.exp(), r=r) return make_int(amount_in, self.exp(), r=r)
@ -222,8 +222,17 @@ class CoinInterface:
def ticker(self): def ticker(self):
return chainparams[self.coin_type()]['ticker'] return chainparams[self.coin_type()]['ticker']
def min_amount(self):
return chainparams[self.coin_type()][self._network]['min_amount']
def max_amount(self):
return chainparams[self.coin_type()][self._network]['max_amount']
def setWalletSeedWarning(self, value): def setWalletSeedWarning(self, value):
self._unknown_wallet_seed = value self._unknown_wallet_seed = value
def setWalletRestoreHeight(self, value):
self._restore_height = value
def knownWalletSeed(self): def knownWalletSeed(self):
return not self._unknown_wallet_seed return not self._unknown_wallet_seed

View file

@ -58,6 +58,8 @@ env.filters['formatts'] = format_timestamp
def getCoinName(c): def getCoinName(c):
if c == Coins.PART_ANON:
return chainparams[Coins.PART]['name'].capitalize() + 'Anon'
return chainparams[c]['name'].capitalize() return chainparams[c]['name'].capitalize()
@ -66,6 +68,9 @@ def listAvailableCoins(swap_client):
for k, v in swap_client.coin_clients.items(): for k, v in swap_client.coin_clients.items():
if v['connection_type'] == 'rpc': if v['connection_type'] == 'rpc':
coins.append((int(k), getCoinName(k))) coins.append((int(k), getCoinName(k)))
if k == Coins.PART:
coins.append((int(Coins.PART_ANON), getCoinName(k)))
return coins return coins

View file

@ -200,6 +200,26 @@ class BTCInterface(CoinInterface):
def getWalletInfo(self): def getWalletInfo(self):
return self.rpc_callback('getwalletinfo') return self.rpc_callback('getwalletinfo')
def walletRestoreHeight(self):
return self._restore_height
def getWalletRestoreHeight(self):
start_time = self.rpc_callback('getwalletinfo')['keypoololdest']
blockchaininfo = self.rpc_callback('getblockchaininfo')
best_block = blockchaininfo['bestblockhash']
chain_synced = round(blockchaininfo['verificationprogress'], 3)
if chain_synced < 1.0:
raise ValueError('{} chain isn\'t synced.'.format(self.chain_name()))
block_hash = best_block
while True:
block_header = self.rpc_callback('getblockheader', [block_hash])
if block_header['time'] < start_time:
return block_header['height']
block_hash = block_header['previousblockhash']
def getWalletSeedID(self): def getWalletSeedID(self):
return self.rpc_callback('getwalletinfo')['hdseedid'] return self.rpc_callback('getwalletinfo')['hdseedid']
@ -229,9 +249,6 @@ class BTCInterface(CoinInterface):
def getNewSecretKey(self): def getNewSecretKey(self):
return getSecretInt() return getSecretInt()
def pubkey(self, key):
return G * key
def getPubkey(self, privkey): def getPubkey(self, privkey):
return PublicKey.from_secret(privkey).format() return PublicKey.from_secret(privkey).format()
@ -258,10 +275,11 @@ class BTCInterface(CoinInterface):
return i return i
def sumKeys(self, ka, kb): def sumKeys(self, ka, kb):
return (ka + kb) % ep.o # TODO: Add to coincurve
return i2b((b2i(ka) + b2i(kb)) % ep.o)
def sumPubkeys(self, Ka, Kb): def sumPubkeys(self, Ka, Kb):
return Ka + Kb return PublicKey.combine_keys([PublicKey(Ka), PublicKey(Kb)]).format()
def getScriptForPubkeyHash(self, pkh): def getScriptForPubkeyHash(self, pkh):
return CScript([OP_0, pkh]) return CScript([OP_0, pkh])

View file

@ -97,3 +97,12 @@ class PARTInterfaceAnon(PARTInterface):
@staticmethod @staticmethod
def balance_type(): def balance_type():
return BalanceTypes.ANON return BalanceTypes.ANON
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
raise ValueError('TODO - new core release')
def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
raise ValueError('TODO - new core release')
def spendBLockTx(self, address_to, kbv, kbs, cb_swap_value, b_fee, restore_height):
raise ValueError('TODO - new core release')

View file

@ -134,6 +134,9 @@ 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 walletRestoreHeight(self):
return self._restore_height
def getMainWalletAddress(self): def getMainWalletAddress(self):
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename}) 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']
@ -147,10 +150,6 @@ class XMRInterface(CoinInterface):
self._log.warning('TODO - estimate fee rate?') self._log.warning('TODO - estimate fee rate?')
return 0.0, 'unused' return 0.0, 'unused'
def isValidKey(self, key_bytes):
ki = b2i(key_bytes)
return ki < edf.l and ki > 8
def getNewSecretKey(self): def getNewSecretKey(self):
return edu.get_secret() return edu.get_secret()

View file

@ -735,6 +735,31 @@ class Test(unittest.TestCase):
if float(js_0['blind_balance']) >= 10.0: if float(js_0['blind_balance']) >= 10.0:
raise ValueError('Expect blind balance < 10') raise ValueError('Expect blind balance < 10')
logging.warning('TODO')
return
amt_swap = make_int(random.uniform(0.1, 2.0), scale=8, r=1)
rate_swap = make_int(random.uniform(2.0, 20.0), scale=8, r=1)
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.PART_ANON, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
offer = offers[0]
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets/part').read())
print('[rm] js_1', js_1)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()